개인 프로젝트: 3차시 Raspberry Pi 로 얼굴인식하기 - Marvic1130/EmbeddedSystem_Class GitHub Wiki
Raspberry Pi 로 얼굴인식하기
Cam Test
캠이 재대로 나오는지 확인하기위해 CamTest.py를 작성한다.
Raspberry pi 캠 설정하기
이번 프로젝트에서 사용할 캠은 라즈베리파이 보드에 연결하는 기본 캠을 사용할 예정이다.
먼저 아래의 명령어를 입력하여 설정에 들어간다.
sudo raspi-config
- Interface Options 을 선택한다.
I1 Legacy Camera Enable/disable legacy camera support 를 누른 후 yes(enable) 선택
그 다음 Finish 를 누르면 Reboot를 할거냐고 물어보는데 yes 를 선택하여 Reboot 한다.
Reboot가 완료되었다면 아래 코드를 실행하여 카메라가 실행되는지 확인한다.
import numpy as np
import cv2
if __name__ == '__main__':
cap = cv2.VideoCapture(0)
cap.set(3, 640) # set Width
cap.set(4, 480) # set Height
while (True):
ret, frame = cap.read()
# gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow('frame', frame)
# cv2.imshow('gray', gray)
k = cv2.waitKey(30) & 0xff
if k == 27: # press 'ESC' to quit
break
cap.release()
cv2.destroyAllWindows()
코드 내에 주석처리한 부분은 흑백영상을 출력하는 코드이다.
소켓 통신
소켓 통신을 사용하는 이유
만들려고하는 프로그램은 opencv를 이용하여 얻어온 영상에서 얼굴을 인식한 후, 인식한 얼굴을 머신러닝을 이용하여 누구인지를 판별하는 프로그램인데 라즈베리파이 안에서 얼굴인식과 머신러닝을 사용하기에는 성능이 부족하여 Server에서 얼굴인식과 머신러닝을 수행한 후 결과값을 Client(라즈베리파이)로 넘겨주는 방법을 사용하려한다.
소켓 설치
아래 명령어를 사용하여 apt-get을 update, upgrade한다.
sudo apt-get update
sudo apt-get upgrade
아래 명령어를 입력하여 socket을 설치한다.
sudo apt-get install socket
라즈베리파이 캠 Socket통신으로 스트리밍
Project/FaceDetector_Server/Test/Server.py 코드를 서버컴퓨터에서 실행한다.
Project/FaceDetector_Client/main.py 코드를 라즈베리파이에서 실행한다.
이때 주의해야할 사항은 s.connect 내부 튜플에서 서버컴퓨터의 ip를 입력해야한다.
서버 컴퓨터의 ip를 알아내는 방법은 아래와 같다.
Windows: ipconfig
Linux, Mac OS: ifconfig
192.168로 시작하는 ip를 찾으면 된다.
아래 사진은 라즈베리파이에서 ifconfig 한 결과
그다음 실행하면 아래와같이 캠이 정상적으로 스트리밍되는것을 볼 수있다.
얼굴인식
얼굴인식 알고리즘
얼굴인식 알고리즘으로 대표적으로 Haar Cascade 라는 머신러닝 기반 알고리즘을 사용하는데 이것의 단점은 마스크를 착용하면 얼굴을 정상적으로 인식할 수 없다는 점이다.
이 프로젝트에서는 OpenCv 얼굴 특징점 추출 알고리즘으로 사람 얼굴에 마스크 이미지를 씌운 후 얼굴로 인식할 수 있도록 학습시킨 모델을 사용하였다.
얼굴인식 실행
Server에서 Project/FaceDetector_Server/Server.py를 실행시킨다.
Client(Raspberry Pi)는 위에서 사용한 코드 를 실행시킨다.
그러면 아래와 같은 결과를 얻을 수 있다.
코드
Client
# -*- coding: utf8 -*-
import cv2
import socket
import numpy as np
## TCP 사용
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
## server ip, port
s.connect(('192.168.100.3', 8808))
## webcam 이미지 capture
cam = cv2.VideoCapture(0)
## 이미지 속성 변경 3 = width, 4 = height
cam.set(3, 1280);
cam.set(4, 720);
## 0~100에서 90의 이미지 품질로 설정 (default = 95)
encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), 90]
while True:
# 비디오의 한 프레임씩 읽는다.
# 제대로 읽으면 ret = True, 실패면 ret = False, frame에는 읽은 프레임
ret, frame = cam.read()
# cv2. imencode(ext, img [, params])
# encode_param의 형식으로 frame을 jpg로 이미지를 인코딩한다.
result, frame = cv2.imencode('.jpg', frame, encode_param)
# frame을 String 형태로 변환
data = np.array(frame)
stringData = data.tostring()
#서버에 데이터 전송
#(str(len(stringData))).encode().ljust(16)
s.sendall((str(len(stringData))).encode().ljust(16) + stringData)
cam.release()
Server
import socket
import cv2
import numpy as np
import os
import random
import cv2
# socket에서 수신한 버퍼를 반환하는 함수
def recvall(sock, count):
# 바이트 문자열
buf = b''
while count:
newbuf = sock.recv(count)
if not newbuf: return None
buf += newbuf
count -= len(newbuf)
return buf
def rename_file(dist_lable: str):
count = 0
file_list = os.listdir("croppedData")
for i in range(file_list.__len__()):
if file_list[i].endswith(".jpg"):
src = "croppedData/" + file_list[i]
dst = "croppedData/" + dist_lable + count.__str__() + ".jpg"
os.rename(src, dst)
print(src + " rename to " + dst)
count += 1
if __name__ == '__main__':
facenet = cv2.dnn.readNet('models/model.prototxt', 'models/model.caffemodel')
HOST = '0.0.0.0'
PORT = 8808
# TCP 사용
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print('Socket created')
# 서버의 아이피와 포트번호 지정
s.bind((HOST, PORT))
print('Socket bind complete')
# 클라이언트의 접속을 기다린다. (클라이언트 연결을 10개까지 받는다)
s.listen(10)
print('Socket now listening')
# 연결, conn에는 소켓 객체, addr은 소켓에 바인드 된 주소
conn, addr = s.accept()
while True:
# client에서 받은 stringData의 크기 (==(str(len(stringData))).encode().ljust(16))
length = recvall(conn, 16)
stringData = recvall(conn, int(length))
data = np.fromstring(stringData, dtype='uint8')
# data를 디코딩한다.
frame = cv2.imdecode(data, cv2.IMREAD_COLOR)
h, w = frame.shape[:2]
blob = cv2.dnn.blobFromImage(frame, scalefactor=1., size=(405, 405), mean=(104., 177., 123.))
facenet.setInput(blob)
dets = facenet.forward()
for i in range(dets.shape[2]):
confidence = dets[0, 0, i, 2]
if confidence < 0.5:
continue
x1 = int(dets[0, 0, i, 3] * w)
y1 = int(dets[0, 0, i, 4] * h)
x2 = int(dets[0, 0, i, 5] * w)
y2 = int(dets[0, 0, i, 6] * h)
face = frame[y1:y2, x1:x2]
face = face / 256
if (x2 >= w or y2 >= h):
continue
if (x1 <= 0 or y1 <= 0):
continue
face_input = cv2.resize(face, (200, 200))
face_input = np.expand_dims(face_input, axis=0)
face_input = np.array(face_input)
color = (255, 255, 255)
file_list = os.listdir("croppedData")
cropped_data_path = "croppedData/temp" + random.randrange(0, 999999).__str__() + ".jpg"
height_dist = (y2 - y1) // 2
crop = frame[y1: y2 - height_dist, x1: x2]
try:
cv2.imwrite(cropped_data_path, crop)
except Exception as e:
print(e)
cv2.rectangle(frame, pt1=(x1, y1), pt2=(x2, y2), thickness=2, color=color, lineType=cv2.LINE_AA)
cv2.imshow('masktest', frame)
key = cv2.waitKey(1)
if key == 27:
break
rename_file('crop')