Face Recognition + Mask Detection
01 Mar 2021 | Face Recognition Mask Detection Python여태 한 것들
Face Recognition은 Dlib으로 얼굴을 인식하고 특징점을 찾은 다음, DB에 있는 사진들과 비교하는 방식이다.
여기서 가장 치명적인 문제점은 마스크를 쓰고 있으면 인식을 하지 못한다는점.
그래서 Mask Detection과 병합을 해보려고 한 결과… 성공했다!
전체적인 코드는 여기에 있다.
작동 방법은 꽤 간단하다.
- 마스크를 낀 얼굴을 찾을 수 있는 모델로 위치를 추출
- 마스크를 쓰고있는지 확인
- 마스크를 쓰고있지 않을 시 얼굴의 특징점을 추출한 후 비교
얼굴의 위치를 추출
class FaceDetector:
__net = None
def __init__(self, prototxtPath, modelPath):
self.__net = cv2.dnn.readNet(prototxtPath, modelPath)
if self.__net is None:
print("Face Detector 클래스 생성 실패, 프로그램 종료")
sys.exit()
print("Face Detector 클래스 생성 완료")
얼굴을 찾는 모델을 인자로 받은 뒤 cv2.dnn.readNet 함수를 이용하여 얼굴을 찾는 모델을 net 변수로 받는다.
def GetDetectionsFromFrame(self, frame):
blob = cv2.dnn.blobFromImage(frame, 1.0, (300,300), (104.0,177.0,123.0))
self.__net.setInput(blob)
detections = self.__net.forward()#순방향 추론
return detections[0,0]
다음 프레임을 인자로 받은 뒤 프레임을 blob화 시키고 추론을 시작한 뒤, 결과값을 리턴한다.
얼굴들의 정확도와 x,y좌표값이 있는 배열을 리턴한다.
@staticmethod
def GetConfidence(detection):
return detection[2]
detection 중 정확도만 리턴하는 함수이다. 코드의 명료성를 위해 분리시켜놨다.
@staticmethod
def GetFaceLocation(detection, frame):
(height,width) = np.shape(frame)[:2]
faceLocation = detection[3:7] * np.array([width,height,width,height])
(left, top, right, bottom) = faceLocation.astype("int")
top = max(0,top)
bottom = min(height-1,bottom)
left = max(0,left)
right = min(width-1,right)
return (left,top,right,bottom)
detection에서 위치를 추출하는 코드이다.
frame에서 w,h를 구한 뒤 정제한 뒤, 튜플을 리턴한다.
@staticmethod
def GetFaceLocationForMD(detection, frame):
faceLocation = FaceDetector.GetFaceLocation(detection,frame)
(left, top, right, bottom) = faceLocation
face = frame[top:bottom,left:right]
face = cv2.cvtColor(face,cv2.COLOR_BGR2RGB)
face = cv2.resize(face, (224, 224))
return face
Mask Detection 라이브러리를 위해 추가 정체를 하는 과정이다.
Mask Detection의 모델을 사용할땐 해당 정제과정을 거쳐야 한다.
얼굴에서 마스크 유무를 판별
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
import sys
import numpy as np
class MaskDetector:
def __init__(self, modelPath):
self._model = load_model(modelPath)
if self._model is None:
print("Mask Detector 클래스 생성 실패, 프로그램 종료")
sys.exit()
print("Mask Detector 클래스 생성 완료")
def MaskDetection(self, face):
face = img_to_array(face)
face = preprocess_input(face)
face = np.expand_dims(face, axis = 0)
#return (mask, withoutMask)
return self._model.predict(face)[0]
Mask Detector 클래스의 생성자에서는 model 경로를 인자로 받는다.
다음 얼굴 이미지를 인자로 받은 뒤 모델을 적용한다.
그 다음 예측률을 리턴하는 간단한 클래스이다.
keras를 이용하였다.
Face Recognition
import face_recognition
import glob
import numpy as np
class FaceTool():
def __init__(self):
self.known_face = []
path = "server/resources/classes/"
classes = glob.glob(path+"*")
for label in classes:
imgs = glob.glob(label+"/*")
label = label.split("\\")[-1]
print(label)
for img in imgs:
self.registerImage(label, img)
def registerImage(self, label, imgPath):
try:
image = face_recognition.load_image_file(imgPath)
face_encoding = face_recognition.face_encodings(image)[0]
self.known_face.append((label, face_encoding))
print("reg img: %s(%s)"%(label, imgPath))
except IndexError:
print("failed to reg img: %s(%s)"%(label, imgPath))
Face Recognition에서 얼굴을 비교하기 위해 이미지에서 특징을 추출하는 과정이다.
지금은 테스트용으로 매 시행마다 받게 해놨지만, 나중엔 DB에서 검색하게 바꿔놔야 겠다.
def match(self, face_encoding):
name = "Unknown"
face_distances = face_recognition.face_distance(list(map(lambda x: x[1], self.known_face)), face_encoding)
print(face_distances)
best_match_index = np.argmin(face_distances)
if face_distances[best_match_index] <= 0.5:
name = self.known_face[best_match_index][0]
return name, float(face_distances[best_match_index])
encoding 된 특징점을 가지고 저장된 얼굴들과 비교하는 과정이다.
Main 함수
Flask 서버를 만들었다.
from face_recognition.api import face_encodings
import numpy as np
import cv2
from flask import request, Blueprint
from server.tools.face_tool import FaceTool
from server.tools.face_detector import FaceDetector
from server.tools.mask_detector import MaskDetector
ft = FaceTool()
fd = FaceDetector('server/tools/face_detector/deploy.prototxt',
'server/tools/face_detector/res10_300x300_ssd_iter_140000.caffemodel')
md = MaskDetector('server/tools/mask_detector.model')
bp = Blueprint('match', __name__, url_prefix='/match')
@bp.route('/', methods=['POST'], strict_slashes=False)
def match():
result = []
nparr = np.frombuffer(request.data, np.uint8) # 리퀘스트로 버퍼 읽기
frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR) # 버퍼 -> Mat
for detection in fd.GetDetectionsFromFrame(frame):
if fd.GetConfidence(detection) < 0.5: #임계점 이하일시 continue
continue
(mask,withoutMask) = md.MaskDetection(fd.GetFaceLocationForMD(detection, frame))
if mask>withoutMask:
print("Mask")
result.append((1, None, None))
else:
print("No Mask")
(left,top,right,bottom) = fd.GetFaceLocation(detection,frame)
faceLocation = []
faceLocation.append((top,right,bottom,left))
faceEncoding = face_encodings(frame,faceLocation)[0]
(name, face_distance) = ft.match(faceEncoding)
#print(name, face_distance)
result.append(( 0,
name,
(top.item(),right.item(),bottom.item(),left.item())
))
return {'data': result}, 200
match 함수에서 여태 소개한 함수들을 전부 사용하고 있다.
얼굴 위치와 프레임을 가지고 face recognition의 face_encodings 함수를 사용해 특징점을 추출하고, 그 것을 FaceTool.match 함수로 결과물을 만든 뒤 result 배열에 삽입한다.
이를 Requests로 전송한다.
결론
클라이언트는 8부능선을 지나온 것 같다.
앞으로 할 일은 적외선센서를 접목시키는 것 뿐!!