ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • OpenCV 강좌 - Haar Cascades에 대해 알아보자.
    OpenCV/OpenCV Python 강좌 2019. 6. 28. 17:41



    얼굴 인식에 사용하는 Haar Cascades에 대해 간단히 알아보았습니다.

    아직 공부중이라 정확하지 않은 내용이 있을 수 있습니다 ;;



    최초 포스팅  2019. 6. 28




    Haar Cascade는 머신 러닝기반의  오브젝트 검출 알고리즘입니다. 

     

    2001년 논문 "Rapid Object Detection using a Boosted Cascade of Simple Features"에서 Paul Viola와 Michael Jones가 제안한 특징(feature)을 기반으로 비디오 또는 이미지에서 오브젝트를 검출하기 위해 사용됩니다. 

    직사각형 영역으로 구성되는 특징을 사용기 때문에 픽셀을 직접 사용할 때 보다 동작 속도가 빠릅니다.  

     

    찾으려는 오브젝트(여기에선 얼굴)가  포함된 이미지와 오브젝트가 없는 이미지를 사용하여 Haar Cascade Classifier(하르 특징 분류기)를 학습시킵니다. 

    그리고나서 분류기를 사용하여 오브젝트를 검출합니다. 



    알고리즘은 다음 4단계로 구성됩니다.

     

    1. Haar Feature Selection

    2. Creating  Integral Images

    3. Adaboost Training

    4. Cascading Classifiers

     

    Haar Feature Selection(하르 특징 선택)

    첫번째 단계는 이미지에서 하르 특징(Haar Features)을 계산하는 겁니다. 

    모든 가능한 크기의 커널을 가지고 이미지 전체를 스캔하여 하르 특징을 계산합니다.

    예를 들어  24 x 24 크기의 윈도우를 사용시  160000 개 이상의 하르 특징을 구하게 됩니다. 

      

    하르 특징은 이미지를 스캔하면서 위치를 이동시키는 인접한 직사각형들의 영역내에 있는 픽셀의 합의 차이를 이용하는 겁니다.   사각 영역 내부의 픽셀들을 빨리 더하기 위해 다음 장에서 소개하는 적분 이미지( integral image)를 사용합니다.    

     

    하르 특징에는 다음 세가지가 있습니다. 

     

    • 두 개의 사각형으로 구성된 하르 특징의 값은 두 사각 영역 내부에 있는 픽셀들의 합하여 검은색 영역의 합에서 흰색 영역의 합을 빼서  구한 것입니다. 두 사각형의 크기와 모양은 동일합니다.

    • 세 개의 사각형으로 구성된 하르 특징은 중앙에 있는 검은색 사각 영역 내부의 픽셀 합에서 바깥에 있는 두 개의 흰색 사각 영역 내부의  픽셀 합을 뺀 것입니다.  

    • 4개의 사각형으로 구성된 하르 특징은 대각선에 위치한 영역간의 차이를 구합니다. 

     

    Integral Images(적분 이미지)  

     

    하르 특징을 계산하려면 검은색 사각형과 흰색 사각형 아래에 있는 픽셀의 합을 구해야 합니다.  

    픽셀의 합을 구하는것을 빠르게 하기 위해서 적분 이미지( integral image)를 사용합니다.  

    큰 이미지라도 빠르게 지정한 영역의 픽셀의 합을 구할 수 있습니다. 



    적분 이미지는 다음처럼 생성합니다. 기존 이미지의 너비와 높이에 1씩 더해서 더 큰 이미지를 만든 후

    맨 왼쪽과 맨 위쪽은 0으로 채웁니다.

     

    그리고 나서 기존 이미지에 영역을 지정하여 내부 값들의 합을 구한 후, 적분 이미지에서 원본에 지정한 영역의 오른쪽 아래 픽셀에 대응하는 위치에 합을 입력해줍니다. 

    적분 이미지는 다음처럼 사용합니다. 

    왼쪽에서 보이는  기존 이미지에서 영역을 지정하여 내부의 값을 구할때 

    적분 이미지에서 대응하는  영역의 4곳의 픽셀값을 사용하여 영역의 합을 계산할 수 있습니다. 

    적분 이미지에서 대응하는 영역의 오른쪽아래 픽셀의 값에서 위쪽 픽셀과 아래쪽 픽셀값을 빼주고 대각선 방향에 있는 픽셀을 더해줍니다.   

     

    Adaboost Training

    우선 이미지에서 앞에서 선택한 하르 특징을 사용하여 특징을 계산합니다. 

     

    160000 개 이상의 특징이 검출되는데  얼굴 검출을 하는데 도움이 되는 의미 있는 특징을 골라내야 합니다.

    이 때 처리 성능을 향상시키기 위해 Adaboost를 사용합니다. 



    앞에서 구한 특징 중 대부분은 의미 없는 특징입니다. 

    예를 들어 다음 그림의 두가지 하르 특징은 눈 주위에서만 의미있는 특징입니다.  

    즉, 이 두 개의 하르 특징은 눈 근처에서만 의미있는 값을 내놓습니다. 

     

    • 가로 방향으로 검은색 사각영역과 흰색 사각영역이 있는 특징의 경우에는  코와 빰보다 눈 부분이 더 어둡다는 특성을 사용합니다. 

    • 세로 방향으로 흰색 사각영역이 있고 좌우에 검은색 사각 영역이 있는 특징의 경우에는 중앙에 있는 코보다 양쪽에 있는 눈 부분이 더 어둡다는 특성을 사용합니다. 

     




    Adaboost는 다음 과정을 통해 이루어집니다.

     

    최적의  특징을 선택하기 위해 모든 학습 이미지에  특징을 적용합니다. 

    각  특징에 대해 얼굴이 포함된 이미지와 얼굴이 없는 이미지를 분류하기 위한 최적의 임계값(threshold)을  찾습니다. 

    ( 흰색 영역과 검은색 영역의 차이가 일정 임계값 이상이면 얼굴을 위한 특징이라 보는 겁니다. 

    얼굴이 아닌 영역에서는 두 영역간의 차이가 거의 없을거라고 보고 만든 알고리즘입니다. [2] ) 



    이 때 잘못 분류될 가능성이 있기 때문에 에러률이 낮은  특징을 선택해야 합니다. 

    즉 얼굴이 포함된 이미지와 얼굴이 없는 이미지를 정확하게 분류할 수 있는 특징을 선택하는 것입니다. 

     

    다음 과정을 반복합니다.  

     

    0.처음에는 모든 하르 특징이 똑같은 가중치를 갖습니다. 

     

    1.모든 하르 특징에 대해 학습 데이터 세트를 사용하여 분류한 결과  각 하르 특징의 에러률을 계산하고 

    잘못 분류하는 하르 특징에는 가중치를 증가시킵니다.

    결과적으로 성능 좋은 하르 특징은 낮은 에러률을 갖게 됩니다. 

     

    낮은 에러률을 보이는 하르 특징을 선택하게 됩니다. 

     

    2. 다음을 만족할 때까지 1번을 반복합니다.

    요구하는 정확성 또는 요구하는 에러율 획득 또는 요구하는 개수의 특징을 발견

     

    최종 분류자는 약 분류자들에 대한  가중치 합입니다.  

    개별 특징으로는 이미지를 분류할 수 없어 약 분류자라고 부릅니다.

    하지만 약 분류자를 여러개 묶으면 이미지를 분류할 수 있게 됩니다.  

     

    Adaboost를 통해 160,000개의 특징은 6000개의 특징으로 줄어들게 됩니다. 



    Cascade Classifier

    이제 이미지를 준비하여  24 x 24 크기의 윈도우를 사용하여 6000개의 하르 특징을 적용하여 얼굴을 검출하면 됩니다. 

    하지만 이렇게 하면 계산량이 너무 많기 때문에 비효율적입니다. 

     

    이미지의 대부분의 공간은 얼굴이 없는 영역입니다. 그래서 현재 윈도우가 있는 영역이 얼굴 영역인지를 단계별로  체크하는 방법을 사용합니다. 

    낮은 단계에서는 짧은 시간에 얼굴 영역인지 판단하게 되며 

    상위 단계로 갈수록  좀더 시간이 오래걸리는 연산을 수행합니다. 

     

    이 방식을 Cascade Classifier라고 합니다. 

    윈도우가 이미지 위를 이동할 때마다 6000개의 특징을 모두 적용하지 않고 여러 단계의 그룹으로 묶어 사용하는 겁니다.

     

    첫번째 단계의 특징에서 얼굴 영역이 아니라는 판정이 나면 바로 다음 위치로 윈도우를 이동하며

    첫번째 단계의 특징에서 얼굴 영역이라는 판정이 내려지면  현재 윈도우가 위치한 곳에 다음 단계의 특징을 적용합니다. 

     

    Haar-cascade Detection in OpenCV

    OpenCV는 원하는 오브젝트를 검출하기 위해 필요한 학습시키는 방법을 제공합니다.

    자세한 내용은 다음 링크에서 확인하세요.

    https://docs.opencv.org/4.1.0/dc/d88/tutorial_traincascade.html



    여기서는 OpenCV로 얼굴과 눈을  찾는 검출기를 만들어 봅니다.  

    얼굴과 눈을 검출하기 위해 미리 학습시켜놓은 분류기가 필요합니다.  XML 포맷으로 opencv/data/haarcascades/ 위치에 저장되어 있습니다. 

    입력 이미지로는 그레이스케일 이미지를 사용해야 합니다. 




    다음은 파이썬으로 작성된 예제 코드입니다. 



    import numpy as np

    import cv2 as cv

     

    # 얼굴과 눈을 검출하기 위해 미리 학습시켜 놓은 XML 포맷으로 저장된 분류기를 로드합니다. 

    face_cascade = cv.CascadeClassifier('haarcascade_frontalface_default.xml')

    eye_cascade = cv.CascadeClassifier('haarcascade_eye.xml')

     

    # 얼굴과 눈을 검출할 그레이스케일 이미지를 준비해놓습니다. 

    img = cv.imread('sachin.jpg')

    gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

     

    # 이미지에서 얼굴을 검출합니다. 

    faces = face_cascade.detectMultiScale(gray, 1.3, 5)

     

    # 얼굴이 검출되었다면 얼굴 위치에 대한 좌표 정보를 리턴받습니다. 

    for (x,y,w,h) in faces:

     

        # 원본 이미지에 얼굴의 위치를 표시합니다. 

        cv.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)

     

        # 눈 검출은 얼굴이 검출된 영역 내부에서만 진행하기 위해 ROI를 생성합니다. 

        roi_gray = gray[y:y+h, x:x+w]

        roi_color = img[y:y+h, x:x+w]

     

        # 눈을 검출합니다. 

        eyes = eye_cascade.detectMultiScale(roi_gray)

     

        # 눈이 검출되었다면 눈 위치에 대한 좌표 정보를 리턴받습니다. 

        for (ex,ey,ew,eh) in eyes:

     

            # 원본 이미지에 얼굴의 위치를 표시합니다. ROI에 표시하면 원본 이미지에도 표시됩니다. 

            cv.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)



    # 얼굴과 눈 검출 결과를 화면에 보여줍니다.

    cv.imshow('img',img)

    cv.waitKey(0)

     

    cv.destroyAllWindows()

     

    import numpy as np
    import cv2 as cv
    
    
    # 얼굴과 눈을 검출하기 위해 미리 학습시켜 놓은 XML 포맷으로 저장된 분류기를 로드합니다. 
    face_cascade = cv.CascadeClassifier('haarcascade_frontalface_default.xml')
    eye_cascade = cv.CascadeClassifier('haarcascade_eye.xml')
    
    
    # 얼굴과 눈을 검출할 그레이스케일 이미지를 준비해놓습니다. 
    img = cv.imread('sachin.jpg')
    gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    
    
    # 이미지에서 얼굴을 검출합니다. 
    faces = face_cascade.detectMultiScale(gray, 1.3, 5)
    
    
    # 얼굴이 검출되었다면 얼굴 위치에 대한 좌표 정보를 리턴받습니다. 
    for (x,y,w,h) in faces:
    
        # 원본 이미지에 얼굴의 위치를 표시합니다. 
        cv.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
    
        # 눈 검출은 얼굴이 검출된 영역 내부에서만 진행하기 위해 ROI를 생성합니다. 
        roi_gray = gray[y:y+h, x:x+w]
        roi_color = img[y:y+h, x:x+w]
    
        # 눈을 검출합니다. 
        eyes = eye_cascade.detectMultiScale(roi_gray)
    
        # 눈이 검출되었다면 눈 위치에 대한 좌표 정보를 리턴받습니다. 
        for (ex,ey,ew,eh) in eyes:
            # 원본 이미지에 얼굴의 위치를 표시합니다. ROI에 표시하면 원본 이미지에도 표시됩니다. 
            cv.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)
    
    
    # 얼굴과 눈 검출 결과를 화면에 보여줍니다.
    cv.imshow('img',img)
    cv.waitKey(0)
    
    cv.destroyAllWindows()



    참고

    [1] https://docs.opencv.org/4.1.0/d7/d8b/tutorial_py_face_detection.html

     

    [2] https://eehoeskrap.tistory.com/95 


    [3] http://www.willberger.org/cascade-haar-explained/

     

    포스트 작성시에는 문제 없었지만 이후 문제가 생길 수 있습니다.
    댓글로 알려주시면 빠른 시일내에 답변을 드리겠습니다.

    여러분의 응원으로 좋은 컨텐츠가 만들어집니다. 지금 본 내용이 도움이 되었다면 유튜브 구독 부탁드립니다. 감사합니다 : )

    유튜브 구독하기


    댓글 0

Designed by Tistory.