728x90

최종데모
동영상 못 찍은게 아쉽다.
728x90
728x90

특징점을 추출하는데 있어 핵심은 얼굴 전체를 보는 것이 아니라 작게 잘라서 생각한다는 것이었다. 일단 얼굴영역 추출은 OpenCV에 있는 method를 그대로 사용한 것이라 특별히 할 이야기가 없다. 프로그램 전체가 그 함수가 제대로 적용된다는 가정하에 만들어진 것이라 문제가 있다. 시간이 더 많다면 얼굴영역을 추출하는 알고리즘도 생각해볼만 하다.

이렇게 얼굴영역이 잡히면 눈코입이 들어있을 것으로 예상되는 영역을 잡는다. 얼굴에서 눈코입의 위치는 어느정도 예측 가능하므로 눈이 세개이거나 코가 눈위로 가 있다거나 하는 경우는 생각하지 않고 일반적인 얼굴에 대해서만 연산을 하였다.

이 과정에서 또다시 문제가 되는 것이 촬영 각도다. 그림을 그리거나 인물사진을 많이 찍은 사람은 알겠지만 얼굴의 비율은 각도에 따라 심하게 차이가 난다. 즉, 고개를 들거나 숙이면 전체에서 눈코입의 위치를 예상하기 어렵다는 것이다. 이것도 얼굴의 범위가 일정하게 잡힌다면 어느정도 예상 가능하지만 OpenCV에 있는 함수가 원래 정면을 기준으로 한 것이기 때문에 그런 것까지 만들기는 어려웠다.
얼굴영역을 작게 나눠서 부분 이진화 시켜봤다. 각 부분에서 경계값은 구역의 gray영상에 대한 평균값으로 했다. 눈코입을 알아볼 수 잇을 정도로 나왔지만 대상마다 다르게 나와 값으로 사용하기엔 문제가 있었다.

눈과 입부분에 대해서 부분 이진화작업을 거쳐 추출한 영상이다. 얼핏 잘나온 것 같지만 실상은 그게 아니어서 계속 정확한 값을 잡기 위해 노력했었다. 코는 부분 이진화를 했을 때 가로방향으로 히스토그램이 많이 나오는 것을 선택(Y값)하고 그부분의 중간값(X값)을 중심으로 잡았다.
턱부분을 잡았다. 턱은 같은 피부색 영역 내에서 음영의 차로 잡아야 했다. 육안으로 봤을 때 가로방향으로 V자에 가까운 형태를 가지며 입 아래에 있고 피부색영역에 들면서 얼굴영역과 음영으로 선이 생기는 곳이 턱이다. 그런 턱을 어떻게 잡아야 할까?

위의 사진에서 오른쪽 아래부분은 턱을 추출한 영상이다. 입의 아래부분에서 피부색만 남기고 나머지는 제거한 다음 세로방향으로 길게 잘랐다.(저 작은 영상을 30개로 잘랐다) 그런다음 Histogram평활화를 거치고, 평균값*depth로 이진화 작업을 거쳤다.
양쪽 광대뼈가 있는 부분을 샘플로 피부색을 추출하였다. 일반적인 증명사진을 시험해봤을 때 결과가 좋게 나왔고 낮에 실내에서 촬영한 사진 역시 피부색이 제대로 추출되었다. 하지만 어두운 실내에서 조명을 많이 받는 사진은 이마와 광대뼈 부분이 피부색이 아닌 흰색에 가깝게 촬영되어 오히려 피부색을 죽이고 흰색을 살리는 결과가 나타났다.
이에대한 해결책으로 밝게 촬영되는 광대뼈 부분이 아니라 입술을 추출하기 위해 잡았던 영역에서 입술로 나온 부분을 제외한 나머지 부분을 피부 샘플로 사용하였다. 피부색 추출은 YCbCr을 이용하여 추출하였다. 피부색에서 Cb,Cr의 값은 변화폭이 좁다는 점을 이용한 추출이다. 샘플에서 가장 빈도가 높은 Cb와 Cr값을 추출하고 이 값을 기준으로 일정 범위 내에 있는 값은 피부색으로 추출하고 나머지 값은 버렸다. 위의 사진은 그 결과로 나온 값이며 흰색으로 추출되던 이마와 광대뼈 부분이 피부색으로 인식되지 않았음을 알 수 있다.

특을 추출하는 것도 잡음의 영향을 덜 받도록 조금 수정했는데 침식과 팽창을 이용하여 잡음을 줄였다. 검은색으로 추출된 턱부분에서 실제로 잡는 점은 중간점이 아니라 검은 선의 윗부분을 잡는다.(간혹 튀는 값으로 인해 심한 오차가 생기지 않도록 막기 위해서다)


입술을 추출하는데 gray로 이진화하면 입술의 중간부분과 입고리는 잘 잡는데 비해 입술의 폭은 정확하게 잡아내지 못하는 단점이 있었다. 그래서 개선 한 사항은 휘도를 줄여서 튀는 값인 입술부분을 잡아내자는 것이었다. RGB값에 대해서 각각 아래 연산을 거쳐 휘도를 줄인다는 것이다.
r = R/(R+B+G)
g = G/(R+B+G)
b = B/(R+B+G)
어디까지나 위의 식은 R+B+G의 값이 1이라는 상황에서 사용하는 식이므로 각각255까지의 수를 사용하는 OpenCV환경에서는 *755를 해줘야 했다.
이렇게 해서 잡은 입술영역은 아래와 위는 잘 잡았으나 입고리부분이 정확하지 못했다. 결국 두가지 버젼으로 입술을 구한다음 OR연산을 거쳐 합쳤다. 입고리부분은 표정에 따라 Y값의 폭이 너무 크므로 입고리부분의 Y값은 사용하지 않고 입술의 중심값으로 Y값을 대신 사용하였다.(사진을 보면 특징점과 입고리부분의 Y값이 일치하지 않음을 알 수 있다.)

핵심은 눈을 추출하는 것인데 눈을 추출하는데 가장 많은 알고리즘을 시험해봤고 많은 작업을 거쳤으며 최종버젼에서도 눈을 추출하는데 드는 작업이 가장 많다.
실제로 눈을 추출하는 것은 가장 간단한 방법으로 했다. 수많은 알고리즘과 논문을 시험해봤으나 결과적으로 가장 효과적인 것은 Gray영상을 이진화하는 것이었다. 눈과 눈썹이 하나로 합쳐지도록 Histogram평활화를 사용하였고 경계값을 설정하는 것도 평균값*depth로 하였는데 depth가 가변적이도록 설정하였다.
눈을 추출하고 중심이 맞지 않으면 맞지 않는 각도만큼 역회전하여 눈을 다시 추출한다. 그렇게해서 중심의 Y값이 일치할 때까지 회전하며 최종적으로 나온 눈의 값에서 가로와 세로의 비율을 따진다. 일반적인 사람의 눈이 잡히지 않으면 다시 추출하고 회전하는 과정을 반복한다. (원래 회전은 360도까지 모든 각도로 회전해보고 가장 결과과 좋은 것(histgram이 가로방향으로 가장 많다던가..)을 선택해서 쓰는 것이 좋다. 하지만 내가 취업을 해버리는 바람에 소스코드를 최적화시킬 시간적 여유가 없었다.

턱의 양쪽은 특별한 추출 알고리즘이 없다. Y축은 입술의 중심값을 사용하고 X축은 피부색영역의 외각부분을 값으로 사용한다.
728x90
728x90

나를 심하게 고생시키던 얼굴인식 프로젝트가 드디어 종결됐다.
운좋게도 최종 데모때 매칭률 100%를 자랑했다.ㅎㅎ
시간내서 한번 정리해놔야 겠다.

눈 추출과 입 추출에 있어서는 다른 논문들보다 훨씬 결과가 좋다고 생각한다. 그리고 대부분 제대로 찾지 못해서 포기하는 턱을 잡았다는 것이 기분이 좋다..
그렇긴 해도 영상처리의 특성상 제대로 잡기가 어렵다.
영상처리는 참 어려운 주제인듯 하다.
728x90
728x90

부분적으로 형활화 및 스트레칭을 시켜서 값의 차이를 증폭시켜라.

각 특징점들의 위치를 잡아라. AAM이고 자시고 하나씩 위치를 잡아버려라!

눈코입부분에서 피부색을 추출한 다음 그 값으로 피부색 영역 구분에 평균값으로 이용하라(이 경우 peer처럼 값이 정해진 것보다는 YCbCr의 값으로 하는 것이 유리할 듯.

경계값을 고정값으로 사용하지 마라.

알고리즘을 사용할 때 근거를 반드시 만들어둬라. 무슨 근거에 의해서인지. 책에 나와있거나 논문으로 나와 있거나.
728x90

+ Recent posts