캐스케이드 분류기와 얼굴 검출

  • 비올라-존스(Viola-Jones) 얼굴 검출기(face detector)
    • 2001년 CVPR 학회에서 발표된 객체 검출 알고리즘
    • Positive 영상(얼굴 영상)과 negative 영상(얼굴 아닌 영상)을 수천~수만장 모아서 학습시켜 빠르고 정확하게 얼굴 영역을 검출하는 방법을 제안
    • 기존 얼굴 검출 방법보다 약 15배 빠르게 동작
    • 기존 얼굼 검출 방법과의 차별점
      • 유사 하르(Haar-like) 특징을 사용
      • AdaBoost에 기반한 강한 분류 성능
      • 캐스케이드(cascade) 방식을 통한 빠른 동작 속도

유사 하르 특징

  • 사각형 형태의 필터 집합을 이용
  • 흰색 사각형 영역 픽셀 값의 합에서 검정색 사각형 영역 픽셀 값을 뺀 결과 값을 추출
  • 적분 영상(integral image)을 이용하여 빠르게 계산 가능

image

AdaBoost 알고리즘을 이용하여 얼굴 판별에 유용한 유사 하르 특징을 선별

  • 24 x 24 크기의 부분 영상에서 180,000개 이상의 특징 추출 가능
  • AdaBoost 알고리즘을 이용하여 효과적인 유사 하르 특징 6천여개를 선별하여 사용

#####

캐스케이드 분류기(Cascade Classifier)

  • 일반적인 영상에는 얼굴이 한 두개 있을 뿐 나머지 영역은 대부분 non-face 영역
  • Non-face 영역을 빠르게 skip 하도록 다단계 검사 수행

image

캐스케이드 분류기 얼굴 감청 과정 시각화

https://youtu.be/hPCTwxF0qf4

객체 검출을 위한 CacadeClassifier 클래스

image

CacadeClassifier 멀티 스케일 객체 검출 멤버 함수

void CascadeClassiier::detectMultiScale(InputArray image, std:: vector<Rect>& objects, double scaleFactor = 1.1, int minNeighbors = 3, int flags = 0, Size minSize = Size(), Size maxSize() = Size());
  • image : (입력) 입력 영상(CV_8U)
  • objects : (출력) 검출된 객체의 사각형 정보(vector)
  • scaleFactor : 영상 축소 비율
  • minNeighbors : 얼마나 많은 이웃 사각형이 검출되어야 최종 검출 영역으로 설정할지를 지정
  • flags : (현재) 사용되지 않음
  • minSize : 최소 객체 크기
  • maxSize : 최대 객체 크기

OpenCV 제공 분류기 XML 파일

  • C:\opencv\build\etc\haarcascades

image

얼굴 및 눈 검출 예제 코드

#include <iostream>
#include "opencv2/opencv.hpp"
#include "opencv2/core/ocl.hpp"

using namespace std;
using namespace cv;

int main()
{
	ocl::setUseOpenCL(false); // 정확하게 시간 측정을 하기 위해 OpenCL을 사용하지 말아라
	// OpenCL 관련된 처리 또는 체크를 하는 시간이 100mili 까지 걸리는데 이 작업의 측정 시간을 배제하기 위해

	Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return -1;
	}

	CascadeClassifier face_cascade("haarcascade_frontalface_default.xml");
	// haarcascade_frontalface_default.xml 이 파일을 복사해서 코드가 들어있는 폴더에 복사
	// 다른 위치에 있는 경우 상대/절대 경로까지 같이 입력해줘야함

	if (face_cascade.empty()) { // 정상적으로 xml 파일을 불러왔는지 확인하는 코드
		cerr << "Failed to open (face) xml file!" << endl;
		return -1;
	}

	TickMeter tm; // 시간측정
	tm.start(); // 시간측정 시작

	vector<Rect> faces; // 얼굴 검출 결과를 받을 vector<Rect>타입의 faces 선언
	face_cascade.detectMultiScale(src, faces); // src 영상의 검출된 결과인 사각형 정보를 받은 faces

	tm.stop();  // 시간측정 끝
	cout << "Face detect: " << tm.getTimeMilli() << " ms." << endl; // Milli 단위로 결과 출력

	Mat dst;
	cvtColor(src, dst, COLOR_GRAY2BGR); // dst 영상을 컬러영상 형태로 입력영상을 변환해서 만듬

	for (size_t i = 0; i < faces.size(); i++) { // 0부터 증가시켜가면서 faces의 사이즈까지
		rectangle(dst, faces[i], Scalar(255, 0, 255), 2, LINE_AA); // 결과 영상 dst에 사각형 정보를 이용해서 보라색2px 으로 사각형을 그림
	}

#if 0
	CascadeClassifier eyes_cascade("haarcascade_eye.xml"); // 눈을 검출
	if (eyes_cascade.empty()) {
		cerr << "Failed to open (eye) xml file!" << endl;
		return -1;
	}

	for (size_t i = 0; i < faces.size(); i++) {
		Mat faceROI = src(faces[i]); // 위에서 검출한 얼굴 영역에 대해서 ROI를 설정함
		vector<Rect> eyes;
		eyes_cascade.detectMultiScale(faceROI, eyes); // 얼굴 영역 안에서만 눈을 찾음

		for (size_t j = 0; j < eyes.size(); j++) {
			Point eye_center(faces[i].x + eyes[j].x + eyes[j].width / 2, faces[i].y + eyes[j].y + eyes[j].height / 2);
			int radius = cvRound((eyes[j].width + eyes[j].height) * 0.25);
			circle(dst, eye_center, radius, Scalar(255, 0, 0), 2, LINE_AA); // 결과를 파란색 2px 동그라미로 표시
		}
	}
#endif

//	imshow("src", src);
	imshow("dst", dst);
	waitKey();
}

image

  • face_cascade.detectMultiScale(src, faces, 1.2)

image

  • face_cascade.detectMultiScale(src, faces, 1.2, 3, 0, Size(100, 100), Size(300, 300))

image

  • 얼굴 영역에서 눈 추가로 검출하기

image