머신러닝

7-18 Haarcascade 특징추출(Feature Extraction) 원리와 드로우잉 스케치 OpenCV AI 안면인식

coding art 2020. 4. 17. 21:07
728x90

 

Opencv에서 오브젝트를 인식하는 대표적인 방법 중의 하나는 Haarcascade 라이브러리 모듈을 사용하는 방법이다. Haarcascade 라이브러리를 사용하여 인식할 수 있는 오브젝트는 사람의 정면 얼굴, 얼굴 안의 눈, 고양이 얼굴, 사람의 몸 각 부분들, 컬러 및 차량을 포함한다. 다음의 흑백 현상 사진 현상에서 얻을 수 있는 이미지들로부터 분명히 Edge Features 중에서 이마와 눈 사이의 E-h 특징과 입술에 대해서 Line Features 중에 L-h 특징을 확인할 수 있다.

 

 

 

 

Haar의 알고리듬에 의한 안면 인식의 원리를 흑백 현상 사진이 아닌 실제 흑백이미지를 대상으로 살펴보도록 하자. 안면 인식된 사례에서 눈썹과 눈두덩이+눈 사이를 잘 관찰해 보면 눈두덩이+눈 부분이 어둡다는 사실을 발견 할 수 있다. Edge Features 의 첫 번째 특징 E-v 에 해당한다. 아울러 입에서 흰색이빨이 드러나면 아래 윗입술이 다소 어두워 보인다. 즉 수평적형태의 Line Features에서 L-v 특징에 해당한다.

 

 

 

 

Haar의 사람의 얼굴을 인식하기 위한 알고리듬의 원리는 대단히 간단하지만 대단히 효율적이다. 이 알고리듬을 바탕으로 이미지 머신 러닝에 적합한 기법이 바로 컨볼류션CNN: convolution neural network) 신경망을 사용하는 Viola Jones 알고리듬이다.

다음과 같이 가장 간단한 Edge Features 중에 E-v 모델을 고려해 보자. 이 모델을 대상으로 예를 들자면 같은 웨이트 값 1.0을 가지는 2X2 커늘을 준비하여 1칸씩 스트라이딩(Striding)한다면 결과적으로 밝은 부분은 균일하게 밝아지고 어두운 부분은 균일하게 어두워지며 그 중간은 그 사이의 일정한 밝기를 가지게 될 것이다. Pading을 어떻게 줄 것인지 감안허여 각자 계산해 보는 것이 좋을 듯하다. Pading을 주어야 4X4 이미지에서 4X4 이미지 계산이 얻어진다.

 

 

 

한편 실제 이미지에 대한 Haar Features를 추출하여 안면을 인식하기 위한 Viola Hones 의 알고리듬은 OpenCV 코드를 통해 구현할 수 있다. 첨부된 파이선 코드 face_detect_sketch.py를 참조하자. 이 코드는 원래 동영상이나 또는 실제 이지지 캠코더용 오브젝트 검출 코드에서 jpg 이미지 파일용으로 슬쩍 바꾸어 본 것이다.

 

사진 이미지를 대상으로 안면 인식이 가능한 face_detect_sketch.py를 사용하여 사진이 아닌 인물 드로잉을 대상으로 보다 흥미로운 안면 인식 실험을 해 보고자 한다. 이 실험을 위해서 yahoo.com과 유튜브에서 안면 스케치 그림을 수집하여 jpg 이미지파일을 준비하자.

 

PiCamera 이미지 해상도를 예를 들자면 240X192로 둔다. 좀 높여도 상관없다. 아울러 frame rate 의 표준 값은 32 이다. 1분에 32 프레임 정도이지만 지금 현재 멈춰있는 그림 이미지 한 번 읽어 들이면 충분하므로 1.0 으로 두자.

 

아울러 안면인식을 위한 이미지 파일 sketches.jpg는 그림판에서 준비하여 face_detect_sketch.py 와 같은 폴더에 저장한다. IDLE에서 face_detect_sketch.py 를 실행하여 이미지를 관찰해 보자. 윗 줄의 5개 이미지 중 5개는 제대로 스케치가 이루어진 상태이며 Haar 특징을 갖추고 있음을 쉽게 알 수 있다. 특히 3번째 이미지는 작가가 눈썹과 눈까지 충분한 명암을 부요 하여 눈 부위 대 아랫 부분의 E-h 조건을 만족하는 듯하다.

반면에 아랫줄 드로우잉에서 완전히 실패한 8번째는 눈썹의 기울어짐으로 인해 명암 색출에 실퍄한 듯하다. 7번째에서 9번째까지는 간단한 점을 찍는 눈 그리기로 인해 제대로 인식을 못한 것 같다.

 

 

만약 8번째 드로우잉이 기울어진 눈썹이 문제라면 눈썹만 수정해서 다시 안면 인식을 시켜 보도록 하자. 결과를 보면 눈썹 명암이 원인이 아니며 눈 드로우잉 기법을 의심해 볼 여지가 있을 것이다.

 

 

 

이와 같이 간단한 얼굴 스케치 기법에 의해 그려도 안면 인식이 손쉽게 이루어짐을 알 수 있다. 한편 안면 인식을 위한 Haarcascade 라이브러리를 준비하기 위해서는 상당히 많은 분량의 사진 이미지 데이터 학습이 필요하지만 얼굴 스케치를 위한 드로우잉 기법 차원에서는 기법 자체가 거의 알고리듬에 가깝다는 점을 감안하게 되면 Haarcascade 말고도 나름 새로우면서도 간단할 수도 있는 효율적인 안면인식 알고리듬을 제안할 수 있을 것으로 보인다.

 

첨부된 코드를 다운하여 실험해 보자.

sketches.jpg

 

 

 

#face_detect_sketch.py

from picamera.array import PiRGBArray

from picamera import PiCamera

import time

import cv2

import sys

 

 

#Create the haar cascadefaceCascade = cv2.CascadeClassifier(cascPath)

faceCascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")

eyeCascade = cv2.CascadeClassifier("haarcascade_eye.xml")

# initialize the camera and grab a reference to the raw camera capture

camera = PiCamera()

#camera.resolution 

camera.resolution = (240, 192)

camera.framerate = 1

rawCapture = PiRGBArray(camera, size=(240, 192))

# allow the camera to warmup

time.sleep(0.1)

lastTime = time.time()*1000.0

# capture frames from the camera

for frame in camera.capture_continuous(rawCapture, format="bgr", use_video_port=True):

 

    #image = frame.array

    image = cv2.imread('sketches.jpg')

    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    

    # Detect faces in the image

    faces = faceCascade.detectMultiScale(

    gray,

    scaleFactor=1.1,

    minNeighbors=2,

    minSize=(100, 100),

#    flags = cv2.CV_HAAR_SCALE_IMAGE

    flags = cv2.CASCADE_SCALE_IMAGE

    )

    print (time.time()*1000.0-lastTime," Found {0} faces!".format(len(faces)) )

    lastTime = time.time()*1000.0

    # Draw a rectangle around the faces

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

        cv2.rectangle(image, (x, y),(x+w, y+h), (255, 255, 0), 2) 

        #cv2.circle(image, (int(x+w/2), int(y+h/2)), int((w+h)/3), (255, 255, 255), 3)

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

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

        eyes = eyeCascade.detectMultiScale(roi_gray)

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

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

    # show the frame

    cv2.imshow("Frame", image)

    key = cv2.waitKey(1) & 0xFF

    # clear the stream in preparation for the next frame

    rawCapture.truncate(0)    

# if the `q` key was pressed, break from the loop

    if key == ord("q"):

        break