감정 인식을 위해 데이터 전처리 과정에서 쓰일 수도 있기 때문에 기록해둡니다.

 

Opencv 에는 여러가지 기능을 가지고 있는데 그 중에서 Haarcascade를 이용하여 진행하겠습니다.

 

이런식으로 detector를 선언해줄 수 있습니다.

 

저는 face align을 위해 눈만 detection 하겠습니다.

 

원래는 얼굴 검출 후 눈을 검출하는 단계로 진행해야하지만 제가 가지고 있는 Data는 crop된 얼굴 이미지 형태이기 때문에 face detection은 제외하겠습니다.

 

 먼저 eye_detect.detectMultiScale(img_gray, scaleFactor, minNeighbors) 를 통해 나오는 결과값을 살펴보면

 

눈의 (x좌표, y좌표, w, h) 순서대로 되어있습니다.

 

위 코드에서는 x좌표를 이용하여 왼쪽, 오른쪽 눈을 지정해줍니다.

 

그 이유는 눈을 기준으로 alignment를 진행해주기 위해서 입니다.

 

위 코드에서는 왼쪽, 오른쪽 눈의 중심점을 찾기 위해 위와 같은 연산을 진행해줍니다.

 

세번째 포인트를 계산해 줍니다.

 

세번째 포인트를 지정해주는 이유는 왼쪽과 오른쪽 눈의 회전되는 각도를 알기 위해서 입니다.

 

 

여기서 a, b, c의 길이는 좌표값을 알고 있기 때문에 피타고라스 정리를 통하여 유클리드 거리를 구할 수 있습니다.

 

 

그 후, 각도를 구하기 위해서 코사인 제 2 법칙을 사용 후 역삼각함수를 이용합니다.

 

 

만약 시계방향으로 이미지를 회전하려면 90도에서 찾은 각도를 뺀 각도로 이미지를 회전시킵니다.

 

 

위 그림으로 왜 그런지 이해할 수 있을 것입니다.

 

이제 이미지를 회전시켜주면 끝입니다!

 

Full Code

더보기
def face_alignment(img, face_detect, eye_detect):
    first_show = True
    show_img = False
    last_show = False
    img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

    eyes_list = eye_detect.detectMultiScale(img_gray)

    eyes = eye_detect.detectMultiScale(img_gray, scaleFactor=1.2, minNeighbors=2)
    # if len(eyes_list) >= 3:
    #     max_y = []
    #     for idx in range(len(eyes_list)):
    #         max_y.append(eyes_list[idx][1])
    #
    #     idx_list = []
    #     for i in range(2):
    #         idx_list.append(max_y.index(min(max_y)))
    #         max_y[max_y.index(min(max_y))] = 1000
    #         eyes.append(eyes_list[idx_list[i]])

    index = 0
    for (eye_x, eye_y, eye_w, eye_h) in eyes:
        if index == 0:
            eye_1 = (eye_x, eye_y, eye_w, eye_h)
        elif index == 1:
            eye_2 = (eye_x, eye_y, eye_w, eye_h)
        index = index + 1
        if first_show:
            cv.rectangle(img, (eye_x, eye_y), (eye_x + eye_w, eye_y + eye_h), (255, 255, 255), 2)

    if first_show:
        cv.imshow('test', img)
        cv.waitKey(0)

    if eye_1[0] < eye_2[0]:
        left_eye = eye_1
        right_eye = eye_2
    else:
        left_eye = eye_2
        right_eye = eye_1

    left_eye_center = (int(left_eye[0] + (left_eye[2] / 2)), int(left_eye[1] + (left_eye[3] / 2)))
    left_eye_x = left_eye_center[0]
    left_eye_y = left_eye_center[1]

    right_eye_center = (int(right_eye[0] + (right_eye[2] / 2)), int(right_eye[1] + (right_eye[3] / 2)))
    right_eye_x = right_eye_center[0]
    right_eye_y = right_eye_center[1]

    if show_img:
        cv.circle(img, left_eye_center, 2, (255, 0, 0), 2)
        cv.circle(img, right_eye_center, 2, (255, 0, 0), 2)
        cv.line(img, right_eye_center, left_eye_center, (67, 67, 67), 2)
        cv.imshow('test', img)
        cv.waitKey(0)

    if left_eye_y < right_eye_y:
        point_3rd = (right_eye_x, left_eye_y)
        direction = -1  # rotate same direction to clock
        print("rotate to clock direction")
    else:
        point_3rd = (left_eye_x, right_eye_y)
        direction = 1  # rotate inverse direction of clock
        print("rotate to inverse clock direction")

    if show_img:
        cv.circle(img, point_3rd, 2, (0, 255, 0), 2)

        cv.line(img, right_eye_center, left_eye_center, (67, 67, 67), 1)
        cv.line(img, left_eye_center, point_3rd, (67, 67, 67), 1)
        cv.line(img, right_eye_center, point_3rd, (67, 67, 67), 1)
        cv.imshow('test', img)
        cv.waitKey(0)

    a = euclidean_distance(left_eye_center, point_3rd)
    b = euclidean_distance(right_eye_center, left_eye_center)
    c = euclidean_distance(right_eye_center, point_3rd)

    cos_a = (b * b + c * c - a * a) / (2 * b * c)
    angle = np.arccos(cos_a)
    angle = (angle * 180) / math.pi

    print("cos(a) = ", cos_a)
    print("angle: ", angle, " in radian")
    print("angle: ", angle, " in degree")

    if direction == -1:
        angle = 90 - angle
    if point_3rd[0] == left_eye_x and point_3rd[1] < left_eye_y:
        angle = -angle
    elif point_3rd[0] == right_eye_x and point_3rd[1] < right_eye_y:
        angle = -angle

    new_img = Image.fromarray(img)
    new_img = np.array(new_img.rotate(direction * angle))

    if last_show:
        cv.imshow('test', new_img)
        cv.waitKey(0)

    return new_img

 

 

출처 : https://a292run.tistory.com/entry/Face-Alignment-for-Face-Recognition-in-Python-within-OpenCV-1

'기록해둘 코드 > python' 카테고리의 다른 글

Padding 붙이기  (0) 2022.09.29

+ Recent posts