Object Detection

face alignment

jwjwvison 2021. 8. 15. 23:36

이번 시간에는 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()

이와같이 얼굴 부분만 정면을 보게끔 변환시켜 잘라낼 수 있다.

 눈의 좌표를 이용한 계산 과정은 위 사진과 같다.