import cv2
import numpy as np
import datetime
import dlib #얼굴탐색
import pygame
from keras.models import load_model
from imutils import face_utils
저는 dlib와 OpenCV를 사용 하여 이미지에서 얼굴 랜드 마크 를 감지 할 것입니다. 얼굴 랜드 마크는 다음과 같이 얼굴의 두드러진 부분을 지역화하고 나타내는 데 사용됩니다.눈, 눈썹, 코, 입, Jawline 얼굴 랜드 마크는 얼굴 정렬, 머리 자세 추정, 얼굴 교체, 눈 깜박임 감지 등에 성공적으로 적용되었습니다.
그래서 먼저 두가지의 일이 필요한데,
1)패키지 설치 pip install dlib opencv-python
2)그리고 학습 모델 다운로드http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2
웹캠 재생시 찾은 얼굴 사각형 프레임씌우기에서 했던 코드를 그대로 갖고 와서 코드를 추가해보겠습니다.
다음 링크텍스트에서 확인해보면 됩니다.
본래 주목적이었던 눈을 감지해 눈이 감기면 음악을 재생시키기 위해서 필요한 부분입니다.
위에서 import한 pygame 음악 재생 모듈을 초기화시켜. pygame.mixer.music을 alarm변수에 넣어주고, alarm에 음악 재생 파일을 로드시켰습니다.
pygame은 원래 게임을 만들기 위한 모듈로 게임에 필요한 여러 가지 요소를 지원합니다. 게임에 필요한 도형 그리기와 각종 사용자가 발생시키는 이벤트에 대한 구분, 그리고 음악 및 효과음을 재생할 수 있도록 합니다.
pygame.init()
alarm = pygame.mixer.music
alarm.load('./datas/good_morning.ogg') # alarm에 음악 재생 파일 로드
# alarm.set_volume(0.7) #100프로는 1.0
numpy를 활용해 눈 이미지에서 눈 가로,세로,중앙, 넓이와 높이를 처리해주는 부분입니다.
def crop_eye(img, eye_points):
x1, y1 = np.amin(eye_points, axis=0)
x2, y2 = np.amax(eye_points, axis=0)
cx, cy = (x1 + x2) / 2, (y1 + y2) / 2 #중앙
w = (x2 - x1) * 1.2
h = w * 26 / 34
margin_x, margin_y = w / 2, h / 2
min_x, min_y = int(cx - margin_x), int(cy - margin_y)
max_x, max_y = int(cx + margin_x), int(cy + margin_y)
eye_rect = np.rint([min_x, min_y, max_x, max_y]).astype(np.int)
eye_img = img[eye_rect[1]:eye_rect[3], eye_rect[0]:eye_rect[2]]
return eye_img, eye_rect
(1) 눈을 두명 모두 감으면 True, 두명 모두 눈을 뜨면 False, 1명 이상이면 Ture면 알람을 실행하도록 했습니다.
(2) 여기서 Face를 흑백으로 해주는데, gray로 바꾸는 장점이 있다. 일단, 단일색인 데이터로 학습을 시키기도 했던 점이 크다. 그리고 채널의 색상의 값은 보통 0~255 까지의 값으로 표현되며, 0은 검은색이고 255는 하얀색이다. 거기서 0의 값만 갖고와서 처리하면 되어 속도가 빠른 결과값을 줄 수 있다고 한다. 그러므로 흑백은 데이터를 학습해 테스트하면 확률을 높일 수 있다고 알고 있다.
(3) 왼쪽 눈과 오른 쪽 눈 위치 정보와 눈 이미지의 크기를 정해주고, flip으로 눈이 상하반전을 해주었다.
(4) 1인 경우, true로 직사각형 프레임으로 눈을 초록색으로 표시해주고, 0인 경우, 눈을 감았기 때문에 빨간색으로 표시해주었다.
(5) 그리고 왼쪽 눈과 오른쪽 눈 모두가 0일 경우에만 알람을 재생시켰다. 그러므로 한쪽 눈만 감았다고 해서 눈을 감았다고 처리해주지 않는다.
(6) 그리고 눈으로 연속으로 감았다 떴다 할 경우 노래가 중복으로 들리는 경우가 있었다. 그래서 0이면 음악이 재생되지 않고,아니면 재생중으로 해 중복 재생 방지했다.
그런데, -1이면 음악이 반복 재생하게 했다. flag처리한 것이라 생각하면 된다.
그 이후에 그림을 반전시키는 작업을 했는데, 웹캠으로 볼때 상하가 반전돼서 나오기 때문에 현재 내 얼굴을 거울처럼 보고싶다면 해줘야 한다.
while(stream.isOpened()):
ret, frame = stream.read()
(1)
if ret:
alarm_flags = []
now = datetime.datetime.now()
#dets = detector(frame,1)
gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
faces = detector(gray)
#print(dets)
(2)
for face in faces:
shapes = predictor(gray, face)
#얼굴 특징을 행렬연산하도록 만들어준다.
shapes = face_utils.shape_to_np(shapes)
(3)
eye_img_l, eye_rect_l = crop_eye(gray, eye_points=shapes[36:42]) #왼쪽 눈 위치정보
eye_img_r, eye_rect_r = crop_eye(gray, eye_points=shapes[42:48]) #오른쪽 눈 위치정보
eye_img_l = cv2.resize(eye_img_l, dsize=(34,26))
eye_img_r = cv2.resize(eye_img_r, dsize=(34,26))
eye_img_r = cv2.flip(eye_img_r, flipCode=1)
eye_input_l = eye_img_l.copy().reshape((1, 26, 34, 1)).astype(np.float32) / 255.
eye_input_r = eye_img_r.copy().reshape((1, 26, 34, 1)).astype(np.float32) / 255.
pred_l = model.predict(eye_input_l)
pred_r = model.predict(eye_input_r)
# print(pred_l, pred_r)
state_l = 1 if pred_l > 0.1 else 0
state_r = 1 if pred_r > 0.1 else 0
(4)
#left
if state_l == 1:
cv2.rectangle(frame, pt1=tuple(eye_rect_l[0:2]), pt2=tuple(eye_rect_l[2:4]), color=(0,255,0), thickness=2)
else:
cv2.rectangle(frame, pt1=tuple(eye_rect_l[0:2]), pt2=tuple(eye_rect_l[2:4]), color=(0,0,255), thickness=2)
#right
if state_r == 1:
cv2.rectangle(frame, pt1=tuple(eye_rect_r[0:2]), pt2=tuple(eye_rect_r[2:4]), color=(0,255,0), thickness=2)
else:
cv2.rectangle(frame, pt1=tuple(eye_rect_r[0:2]), pt2=tuple(eye_rect_r[2:4]), color=(0,0,255), thickness=2)
(5)
# 알람 실행
if state_l == 0 and state_r == 0:
alarm_flags.append(True)
elif state_l ==1 and state_r == 1:
alarm_flags.append(False)
else:
alarm_flags.append(False)
if True in alarm_flags and alarm.get_busy() == 0:
alarm.play(-1) # -1 :
elif True not in alarm_flags:
alarm.stop()
#frame 크기 조정
#resize_frame = cv2.resize(text_frame ,None, fx=2, fy=2, interpolation=cv2.INTER_LINEAR) # 키울 그림,
#그림 반전
flip_frame = cv2.flip(frame,1)
text_frame = cv2.putText(flip_frame, str(now), (10,30) ,cv2.FONT_HERSHEY_SIMPLEX,1, (0,255,255), 1, cv2.LINE_AA)
resize_frame = cv2.resize(text_frame , dsize = (1920,1280), interpolation=cv2.INTER_LINEAR) # 키울 그림, dsize - 자신의 해상도 결정
cv2.imshow('cam', resize_frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
else:
break
pygame.mixer.quit()
stream.release()
cv2.destroyAllWindows()