Yours Ever, Data Chronicles

Python OpenCV (3) - 이미지에서 사람 얼굴 & 방향 인식하기 (python dlib, 안면 인식 기술) 본문

Data Science/Analysis Study

Python OpenCV (3) - 이미지에서 사람 얼굴 & 방향 인식하기 (python dlib, 안면 인식 기술)

Everly. 2022. 5. 13. 12:02

저번 포스팅에서는 얼굴 인식에 사용되는 2가지 모델(HOG 특징량의 SVMDetector, CascadeClassifier) 을 사용하여,

이미지 파일을 불러오고 여기의 사람 얼굴을 인식한 후 네모박스로 처리해보았습니다. 굉장히 신기했죠?

 

이번 포스팅은 사람 얼굴을 인식하는 것에서 더 나아가, 이 얼굴의 랜드마크를 뽑아보고 & 이 얼굴이 어느 쪽을 보고 있는지를 검출해봅니다.

dlib이라는 라이브러리를 활용합니다. dlib은 얼굴을 눈, 코, 입, 윤곽의 특징점(랜드마크)를 뽑아주고, 이를 계산하여 사람이 얼굴을 어느 쪽으로 돌리고 있는지와 같은 세세한 정보를 검출할 수 있습니다.

 

이번 포스팅을 따라가기 위해서는 dlib 라이브러리 설치가 필수인데요. 아나콘다 프롬프트를 열고 다음을 실행합니다.

 

pip install dlib

 

하지만 이렇게 해서 에러가 뜨는 분들도 있을 텐데(제가 그랬습니다..), 에러 해결법은 이 포스팅에 자세히 기록해두었으니 참고하시길 바랍니다! :)

 

python dlib 설치하기 - anaconda dlib install error

안면 인식 기술로 활용되는 dlib 라이브러리. 설치하는데 너무 에러가 많이 떠서 이를 해결하기 위해 많은 삽질을 해보고 쓰는 글이다. 이대로만 따라하자! (그리고 나는 윈도우(Windows)를 사용하

suy379.tistory.com

 

✔Table of Contents

     

     

    Tech 86. 이미지 속 사람 얼굴이 어느 쪽을 보고 있는지를 알아보자

     

    우리가 이번 분석에 사용할 이미지 데이터입니다. 정면을 보고 있는 것 같은 저 남성의 얼굴이 오늘의 분석 재료입니다.

    이 남성의 얼굴로부터 랜드마크를 뽑아보고, 얼굴의 방향이 어디를 향하고 있는지를 알아봅시다.

     

    먼저 dlib은 face detectorlandmark(shape) predictor가 있습니다. 

    전자는 얼굴 전체를, 후자는 얼굴의 랜드마크(눈, 코, 입, 윤곽)을 찍어주는 것이라고 생각하시면 됩니다. 

     

    필요 메서드

     

    • detector = dlib.get_frontal_face_detector() : 얼굴 전체(face, detector), 이하 'd'
      • dets = detector(img, 1)
    • predictor = dlib.shape_predictor('...') : 얼굴 내 랜드마크(predictor), 이하 'shape'
      • shape = predictor(img, d)

     

    얼굴 전체(d)에 사각형 그리기

     

    • cv2.rectangle(img, pt1, pt2, color, line-thickness)

     

    얼굴 랜드마크(shape)에 점 찍기 (좌표 필요)

     

    • cv2.circle(img, (x, y), circle_r, color, line-thickness) : (x, y)엔 좌표를 넣는다. 
    • 참고로 얼굴 랜드마크에만 점을 찍을 뿐 아니라, 랜드마크 점들 중에서 중심점을 찍을 때에도 사용한다.

     

    그림 위에 텍스트(text) 추가하기

     

    • cv2.putText(img, text, (x, y), fontType, fontSize, color, line-thickness) : img 위에 텍스트가 원하는 좌표 위치에 입력된다.

     

    import cv2, dlib, math
    
    #준비
    predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
    detector = dlib.get_frontal_face_detector()
    
    #얼굴
    img = cv2.imread('img/img02.jpg') #이미지 파일 불러오기
    dets = detector(img, 1)
    dets

     

    먼저 img02.jpg 파일을 불러오고, 여기에 얼굴 전체 (d)를 설정합니다. 현재 사각형 위치정보가 표시되어 있죠.

     

    이제부터 dlib이 인식한 얼굴 개수만큼 반복하여 얼굴 윤곽(shape, 랜드마크)을 표시하겠습니다.

    얼굴 윤곽은 img와 얼굴 전체(d)로부터 predictor 메서드를 사용해 뽑을 수 있습니다.

    shape.num_parts로 얼굴 특징점(랜드마크)가 몇개나 있는지를 알아봅시다.

     

    ## 이제부터 인식된 얼굴 개수만큼 반복하여 얼굴 윤곽을 표시한다.
    # k: 얼굴 인덱스, d: 얼굴 좌표
    
    for k, d in enumerate(dets): 
        shape = predictor(img, d) #shape: 얼굴 랜드마크 추출 
        print(shape.num_parts) #추출된 점은 68개.


    이제 복잡한 for문이 나오는데요, 전체적으로 이런 순서로 그림을 그리겠다고 보시면 됩니다.

     

    1. 얼굴 전체(d), 얼굴의 랜드마크(shape)을 설정한다.

    2. 얼굴 영역에 표시할 색깔(color), 표시할 선, 도형, 글씨체 등을 설정한다.

    (참고로 얼굴 전체에는 빨간색 사각형을, 랜드마크 바깥쪽(out)은 파란색 점을, 랜드마크 안쪽(in)은 초록색 점으로 찍는다.)

     

    3. 얼굴 전체(d)에 사각형을 그림: cv2.rectanlge

    4. 얼굴 랜드마크(shape)에 점을 찍음: cv2.circle

    이 때 필요한 것은 어디에 찍을지 좌표이다! 이 x,y 좌표는 shape.part(번호)에 들어있다. 

    번호는 총 68개가 있으며, 17을 기준으로 번호가 17보다 작으면 out(바깥쪽)에 점을 찍는다. - 파란색 점 

    번호가 17보다 크면 in(안쪽)에 점을 찍는다. - 초록색 점 

    5. 랜드마크에 찍힌 점들 중에서, 가장 중심위치를 표시한다. cv2.circle

     

    6. 얼굴 방향을 표시한다. (정면인지, 측면인지?) 

    이는 앞서 계산한 out, in 좌표를 활용해 수식을 계산하면 된다. → 이로써 얼굴의 방향과, 그 때의 각도(단위: 라디안)를 계산할 수 있다.

    7. 마지막으로 위에서 계산한 얼굴 방향과 각도값을 cv2.putText를 활용해 그림 위에 표시한다.

     


    위에서 정리한 순서에 맞게 코드를 짜봅니다. 주석에 자세히 적어두었으니 참고하세요!

     

    img = cv2.imread('img/img02.jpg') #이미지 파일 불러오기
    dets = detector(img, 1)
    
    ## 이제부터 인식된 얼굴 개수만큼 반복하여 얼굴 윤곽을 표시한다.
    # k: 얼굴 인덱스, d: 얼굴 좌표
    
    for k, d in enumerate(dets): 
        shape = predictor(img, d) #shape: 얼굴 랜드마크 추출 
        print(shape.num_parts) #추출된 점은 68개. 
        print(' ')
        
        #얼굴 영역 표시
            ## 색깔
        color_f = (0, 0, 255) #face - 빨강
        color_l_out = (255, 0, 0) #랜드마크 바깥쪽(out) - 파랑
        color_l_in = (0, 255, 0)    #랜드마크 안쪽(in) - 초록 
            ## 표시할 선, 도형
        line_width = 3
        circle_r = 3
            ## 글씨 
        fontType = cv2.FONT_HERSHEY_SIMPLEX
        fontSize = 2
        
        #얼굴(detector)에 사각형 그림
        cv2.rectangle(img, (d.left(), d.top()), (d.right(), d.bottom()), color_f, line_width)
        
        #이제 랜드마크에 점을 찍어보자.
        num_of_points_out = 17
        num_of_points_in = shape.num_parts - num_of_points_out
        gx_out = 0; gy_out = 0; gx_in = 0; gy_in = 0
        
        #점을 찍으려면 필요한 건 좌표!  -> 이는 shape.part(번호) 에 (x,y로) 들어있다.
        # 번호값을 하나씩 바꿔가며 좌표를 찍자.
        for i in range(shape.num_parts): #총 68개
            shape_point = shape.part(i)
            print('얼굴 랜드마크 No.{} 좌표위치: ({}, {})'.format(i, shape_point.x, shape_point.y))
            
            #얼굴 랜드마크마다 그리기
            ## i(랜드마크 번호)가 17보다 작으면 out(바깥쪽)을 그린다 - 파란색 점 
            if i < num_of_points_out:
                cv2.circle(img, (shape_point.x, shape_point.y), circle_r, color_l_out, line_width)
                gx_out = gx_out + shape_point.x/num_of_points_out
                gy_out = gy_out + shape_point.y/num_of_points_out
            
            ##반면 i가 17이상이면 in(안쪽)을 그린다 - 초록색 점 
            else:
                cv2.circle(img, (shape_point.x, shape_point.y), circle_r, color_l_in, line_width)
                gx_in = gx_in + shape_point.x/num_of_points_in
                gy_in = gy_in + shape_point.y/num_of_points_in
                
        #랜드마크에 톡톡톡 찍힌 점들 중에서도, 가장 중심위치를 표시해보자.
            #먼저 out(바깥쪽)은 빨강색
        cv2.circle(img, (int(gx_out), int(gy_out)), circle_r, (0,0,255), line_width)
            #그리고 in(안쪽)은 검은색 
        cv2.circle(img, (int(gx_in), int(gy_in)), circle_r, (0,0,0), line_width)
        
        
        #얼굴 방향 표시하기(정면인지? 측면인지? -> 앞서 만든 out, in 좌표로 계산!)
        theta = math.asin(2*(gx_in-gx_out) / (d.right() - d.left()))
        radian = theta*180/math.pi
        print(' ')
        print('얼굴방향: {0:.3f} (각도: {1:.3f}도)'.format(theta, radian))
        
        #이 얼굴방향과 각도를 face('d') 사각형 위에 출력 
        if radian < 0:
            textPrefix = 'left'
        else: 
            textPrefix = 'right'
            
        textShow = textPrefix + str(round(abs(radian), 1)) + " deg."
        cv2.putText(img, textShow, (d.left(), d.top()), fontType, fontSize, color_f, line_width)

     

    결과를 보시면 이런 식으로 얼굴 랜드마크(shape) 68개의 좌표위치가 나옵니다.

    그리고 맨 마지막에 보시면 얼굴 방향 각도가 출력됩니다.

     

     

    이제 img02.jpg 위에 검출된 얼굴과 랜드마크가 잘 찍혔는지를 확인해봅시다.

     

    cv2.namedWindow('img')
    cv2.imshow('img', img)
    cv2.imwrite('temp.jpg', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

     

    사진이 좀 무섭긴(?) 하지만, 우리가 원하는 대로 잘 나왔습니다.

    얼굴 전체(d)는 빨간색 네모박스가 그려졌고,

    dlib 모델이 인식한 얼굴의 랜드마크에서 바깥쪽은 파란색 점, 안쪽은 초록색 점으로 그려졌네요.

    그 중에서도 파란색 점들의 중심점은 빨간색, 초록색 점들의 중심점은 검은색 점으로 찍혔습니다.

     

    그리고 모델의 각도는 3.699도(약 3.7도)로, 얼굴 방향은 오른쪽(right)을 향하고 있음을 알 수 있습니다.

     

    이렇게 하여 이미지에서 사람 얼굴 인식을 마치고, 다음부터는 동영상 데이터로부터 사람의 얼굴을 인식해보겠습니다. 

    반응형