컴퓨터비전 시간에 했던 과제에 관해 정리해보았다.
화소 처리(pixel processing) 기법 및 컬러 공간/변환을 이용하여 얼굴인 부분을 알 수 있도록 검출한 후 사각형 또는 타원형으로 얼굴 부분을 화면 상에서 표시하도록 한다. 컬러 외에 다른 정보를 사용 가능하나 (배우지 않은)Haar 특징 등은 사용할 수 없다.
웹캠 또는 웹 상의 얼굴이 있는 동영상을 활용하고, 정지 영상에 대해서는 테스트해볼 수 있으나 최종 개발은 동영상에서 진행한다.
얼굴 검출을 Pixel processing과 컬러 공간 변환을 사용해 구현하는 방법은 피부색 기반 탐지와 형태 분석을 조합하는 방식으로 접근할 수 있다.
얼굴 검출을 위해 먼저 입력 비디오 프레임을 적절한 컬러 공간으로 변환한 후 피부색을 기반으로 얼굴을 탐지한다. 일반적으로 피부색을 잘 표현하는 컬러 공간으로 YCrCb를 사용할 수 있다.
YCrCb 컬러 공간에서 피부색은 Cr, Cb 채널에서 특정 값 범위를 가진다. 이를 통해 피부색을 기반으로 마스크 이미지를 만들 수 있다. 이 컬러 공간에서 피부색은 Cr값 133~173의 범위, Cb값 77~127의 범위이다.
이를 통해 피부 영역을 검출하는 바이너리 마스크를 생성할 수 있다.
윤곽선 검출 기법을 사용하여 검출된 피부색 영역에서 가장 큰 윤곽선 또는 얼굴에 해당할 가능성이 높은 영역을 찾는다,
윤곽선이 검출되면, 그 윤곽선을 바탕으로 얼굴 영역을 사각형으로 감싸서 화면에 표시한다. 윤곽선의 크기나 형태를 기준으로 얼굴이 될 만한 영역을 선택하고, 해당 영역에 사각형을 그린다.
이 과정을 웹캠에서 실시간으로 처리하기 위해, 비디오 프레임을 연속적으로 받아와서 각 프레임에 대해 얼굴 검출 과정을 수행한다. OpenCV의 videoCapture 클래스를 사용하여 웹캠 또는 비디오 파일을 읽고, 실시간으로 얼굴을 검출하여 표시한다.
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main() {
VideoCapture cap(0); // 웹캠 열기 (0은 기본 웹캠)
if (!cap.isOpened()) {
cout << "웹캠을 열 수 없습니다." << endl;
return -1;
}
Mat frame, ycrcb, mask;
while (true) {
cap >> frame; // 프레임을 가져오기
if (frame.empty()) break;
// 컬러 공간 변환 (BGR -> YCrCb)
cvtColor(frame, ycrcb, COLOR_BGR2YCrCb);
// 피부색 범위 설정 (Cr: 133~173, Cb: 77~127)
inRange(ycrcb, Scalar(0, 133, 77), Scalar(255, 173, 127), mask);
// 윤곽선 검출
vector<vector<Point>> contours;
findContours(mask, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
// 얼굴 영역을 사각형으로 표시
for (size_t i = 0; i < contours.size(); i++) {
Rect face = boundingRect(contours[i]);
rectangle(frame, face, Scalar(255, 0, 0), 2);
}
// 결과 출력
imshow("Face Detection - YCrCb", frame);
if (waitKey(30) >= 0) break;
}
return 0;
}
구현 과정을 바탕으로, 기본적인 코드를 작성했다. 작성한 코드를 바탕으로 성능 실험을 진행해보았다.

그 결과, 얼굴 영역을 찾았지만 잡음이 굉장히 많이 발생하는 것을 확인할 수 있었다.
이러한 잡음을 제거하고 얼굴을 detection 가능하게 하기 위해서 여러 가지 부분들을 조정해보았다.
현재 YCrCb의 범위를 일반적인 피부색으로 설정하였으나, 범위가 넓어서 얼굴 이외의 영역을 많이 포함할 가능성이 있었다. 따라서 Trackbar를 통해 직접 최적의 범위를 찾아보았다.

그 결과, Cr의 범위는 그대로, Cb의 범위는 95~127로 범위를 좁게 한정하기로 결정했다.
검출된 영역의 크기를 기준으로 필터링해서, 얼굴 크기에 맞지 않는 너무 작거나 큰 영역을 제외한다. 잡음처럼 굉장히 작은 영역도 detection하는 문제를 해결하기 위해 생각해본 해결책이다.

그 결과, 얼굴과 손 모두 잘 인식하는 것을 확인할 수 있었다.

추가로 다른 물체(아이패드)와 함께 웹캠 테스트를 진행해보았을 때, 피부색과 일치하는 얼굴과 손만 잘 인식하는 것을 확인할 수 있었다.
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main() {
VideoCapture cap(0); // 웹캠 열기 (0은 기본 웹캠)
if (!cap.isOpened()) {
cout << "웹캠을 열 수 없습니다." << endl;
return -1;
}
Mat frame, ycrcb, mask;
while (true) {
cap >> frame; // 프레임을 가져오기
if (frame.empty()) break;
// 컬러 공간 변환 (BGR -> YCrCb)
cvtColor(frame, ycrcb, COLOR_BGR2YCrCb);
// 피부색 범위 설정 (Cr: 133~173, Cb: 95~127)
inRange(ycrcb, Scalar(0, 133, 95), Scalar(255, 173, 127), mask);
// 윤곽선 검출
vector<vector<Point>> contours;
findContours(mask, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
// 얼굴 영역을 사각형으로 표시
for (size_t i = 0; i < contours.size(); i++) {
Rect face = boundingRect(contours[i]);
// 크기 필터링 (너무 작거나 너무 큰 영역 제외)
if (face.width < 50 || face.height < 50 || face.width > 300 || face.height > 300) {
continue; // 얼굴 크기에 맞지 않는 영역은 무시
}
// 검출된 얼굴에 사각형 그리기
rectangle(frame, face, Scalar(255, 0, 0), 2);
}
// 결과 출력
imshow("Face Detection - YCrCb", frame);
if (waitKey(30) >= 0) break;
}
return 0;
}
구현 방법에 따른 초기 코드에서, 피부색 Cr, Cb 범위 조정과 크기 필터링을 추가로 하여 Face (Hand) detection의 성능을 높일 수 있었다.
이 과제는 피부색을 통해 얼굴이나 손, 즉 피부를 detection한 것이다. 교수님께서 배우지 않은 Haar 특징은 사용하지 않고 과제를 수행하라고 하셨기 때문에, 컬러 공간만을 이용했지만, 언급하신 내용이 궁금해졌다.
reference
https://medium.com/analytics-vidhya/what-is-haar-features-used-in-face-detection-a7e531c8332b
사람의 얼굴에는 특별한 패턴이 있는데, 두 눈은 명암이 어둡고 코는 명암이 밝다. 이러한 명암을 이용해 패턴을 구하는 것이고, 이들을 Haar like feature라고 한다. 사람의 얼굴 위에 흑백의 사각형을 겹쳐 놓은 다음 밝은 영역에 속한 픽셀 값들의 평균에서 어두운 영역에 속한 픽셀값들의 평균의 차이를 구한다. 그 차이가 Threshold를 넘으면 사람 얼굴에 대한 Haar like feature이 되는 것이다.


그림과 같이 눈썹과 코, 입술을 detect하기 위해서 Haar feature을 사용한다.
이것을 활용해서 haar cascade algorithm을 구현했고, 이는 face detection에 기본적으로 쓰이는 방법이다.
사실 어떠한 알고리즘이 사용될 것이라고는 생각했지만, Haar feature의 상자같은 것으로 명암대비를 활용하여 detection하는 지는 알지 못했다. 컴퓨터비전 수업을 듣게 되면서 영상에서의 명암대비의 중요성을 알게 되었는데, 여기서도 사용되었다는 점이 흥미로웠다. 간단하게만 알아보았지만, 앞으로 수업을 들으며 그 개념을 구체화해나가야겠다고 생각했다.