이번 시간에는 face alignment 기법을 알아보겠다. face alignmet기법을 통해서 사진들을 전부 정면을 보게끔 처리해 줄 수 있다. 이를 통해 face recognition의 인식률을 높일 수 있게 된다.
전반적인 작업 순서는 이렇다.
1. dlib을 사용하여 사진의 사람 얼굴을 검출한 뒤 눈의 좌표를 구한다.
2. 눈의 좌표를 통해 눈 사이의 거리, 사진의 틀어진 정도(arctan)를 구한다.
3. affine 변환을 통해 사진을 돌린다
4. 얼굴 부분만 crop하여 사진을 생성한다.
import numpy as np
import dlib
import cv2
RIHGT_EYE=list(range(36,42))
LEFT_EYE=list(range(42,48))
EYES=list(range(36,48))
predictor_file = './model/shape_predictor_68_face_landmarks.dat'
image_file = './image/marathon_03.jpg'
MARGIN_RATIO=1.5 #실제 얼굴 크기보다 추출된 사진의 크기 비율 계수
OUTPUT_SIZE=(300,300)
detector=dlib.get_frontal_face_detector()
predictor=dlib.shape_predictor(predictor_file)
image=cv2.imread(image_file)
image_origin=image.copy()
(image_height,image_width)=image.shape[:2]
gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
rects=detector(gray,1)
def getFaceDimension(rect):
#얼굴을 가리키는 사각형의 왼쪽 상단 (꼭지점 좌표, 너비, 높이)
return (rect.left(),rect.top(),rect.right()-rect.left(),rect.bottom()-rect.top())
def getCropDimension(rect, center):
width = (rect.right() - rect.left())
half_width = width // 2
(centerX, centerY) = center
startX = centerX - half_width
endX = centerX + half_width
startY = rect.top()
endY = rect.bottom()
return (startX, endX, startY, endY)
for (i,rect) in enumerate(rects):
(x,y,w,h) = getFaceDimension(rect)
cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),2)
points=np.matrix([[p.x,p.y] for p in predictor(gray,rect).parts()])
show_parts=points[EYES]
right_eye_center=np.mean(points[RIHGT_EYE],axis=0).astype('int') #오른쪽 눈의 좌표값들의 평균값
left_eye_center=np.mean(points[LEFT_EYE],axis=0).astype('int') #왼쪽 눈의 좌표들값의 평균값
print(right_eye_center,left_eye_center)
cv2.circle(image,(right_eye_center[0,0],right_eye_center[0,1]),5,(0,0,255),-1)
cv2.circle(image,(left_eye_center[0,0],left_eye_center[0,1]),5,(0,0,255),-1)
#왼쪽눈의 x좌표, 오른쪽 눈의 y좌표(실제 방향)
cv2.circle(image,(left_eye_center[0,0],right_eye_center[0,1]),5,(0,255,0),-1)
cv2.line(image,(right_eye_center[0,0],right_eye_center[0,1]),
(left_eye_center[0,0],left_eye_center[0,1]),(0,255,0),2)
cv2.line(image,(right_eye_center[0,0],right_eye_center[0,1]),
(left_eye_center[0,0],right_eye_center[0,1]),(0,255,0),1)
cv2.line(image,(left_eye_center[0,0],right_eye_center[0,1]),
(left_eye_center[0,0],left_eye_center[0,1]),(0,255,0),1)
eye_delta_x=right_eye_center[0,0] - left_eye_center[0,0]
eye_delta_y=right_eye_center[0,1] - left_eye_center[0,1]
degree=np.degrees(np.arctan2(eye_delta_y,eye_delta_x)) - 180
eye_distance=np.sqrt((eye_delta_x ** 2) + (eye_delta_y ** 2))
# 사진을 돌린 이후 눈 사이의 거리 조정
aligned_eye_distance=left_eye_center[0,0] - right_eye_center[0,0]
scale=aligned_eye_distance / eye_distance
eyes_center=((left_eye_center[0,0] + right_eye_center[0,0])//2,
(left_eye_center[0,1] + right_eye_center[0,1])//2)
cv2.circle(image,eyes_center,5,(255,0,0),-1)
matrix=cv2.getRotationMatrix2D(eyes_center,degree,scale)
cv2.putText(image,'{:.5f}'.format(degree),(right_eye_center[0,0],right_eye_center[0,1]),
cv2.FONT_HERSHEY_SIMPLEX,0.5,(0,255,0),1)
wraped=cv2.warpAffine(image_origin,matrix,(image_width,image_height),
flags=cv2.INTER_CUBIC)
cv2.imshow('wrapAffine',wraped)
(startX,endX,startY,endY) = getCropDimension(rect,eyes_center)
croped=wraped[startY:endY,startX:endX] # 이 순서는 cv2의 규칙이다
output=cv2.resize(croped,OUTPUT_SIZE)
cv2.imshow('output',output)
# output_file='./image/tedy-front/1_out.jpg'
# cv2.imwrite(output_file,output)
cv2.imshow('Face Alignment',image)
cv2.waitKey(0)
cv2.destroyAllWindows()
이와같이 얼굴 부분만 정면을 보게끔 변환시켜 잘라낼 수 있다.
눈의 좌표를 이용한 계산 과정은 위 사진과 같다.
'Object Detection' 카테고리의 다른 글
Custom YOLO project - YOLO 이미지 데이터 만들기 (0) | 2021.08.21 |
---|---|
yolov3를 이용한 객체 탐지 (0) | 2021.08.17 |
졸음 감지 프로젝트 (0) | 2021.08.16 |
face_aligned_photo_generator (0) | 2021.08.16 |
face_landmark 방법으로 얼굴 검출하기 (0) | 2021.08.15 |