내맘대로 공부기록.

[ C++ ]

[ openCV | C++ ] 기본기. 얼굴 인식(검출) 방법과 벡터 변수의 활용

fwanggus 2021. 2. 8. 23:06

Cascade Classifier 🌞

C++ 환경에서 사람 얼굴 인식(검출)을 구현해보겠습니다. openCV 라이브러리에는 Cascade Classifier(분류기)가 제공되며, 이 Classifier에서는 두 가지 방법에 따라 구분될 수 있습니다. 첫 번째는 Haar classifier, 두 번째는 LBP classifier로 윤곽선을 검출할 수 있습니다. 일반적으로는 Haas feature 기반의 Classifier를 많이 사용한다고 합니다. (공홈 내용 참고하였습니다. Theory 부분을 열어봤지만, 블로그를 찾아보는 게 더욱 효과적이었습니다.)

 

얼굴 검출 classifier는 얼굴이 있는 이미지(Positive Image) / 얼굴이 없는 이미지(Negative Image) 데이터로부터 Cascade 함수를 학습하게 된다. 여기서 모델은 얼굴을 나타내는 Feature를 추출하게 되는데, 아래와 같은 행렬 데이터로 이미지(영상) 픽셀을 필터링하게 됩니다. 필터링 과정에서 사람의 얼굴 형태가 있는지 없는지 찾아내기 위해 몇 단계의 필터링 과정을 거치게 되며, 분리된 단계의 작업으로 빠르고 효율적으로 사람 얼굴에 대한 인식 작업을 진행할 수 있다고 합니다. 이러한 장점을 가지고 있기 때문에 다른 분류기(Classifier)보다 보편적으로 많이 사용되는 분류기가 아닐까 하는 생각이 듭니다. 

 

Haar Features 행렬(이미지 필터에 사용된다.)
Haas Feature 검출(아래 중간 사진 : 눈이 있는 영역과 눈아래 영역을 필터링 적용해서 감지)

 

보다 깊은 이해를 위해 Loner의 학습노트 님의 블로그를 참고했으며, 아래와 같이 케스케이드 구조와 개념에 대해서 직관적으로 이해할 수가 있었습니다.🙏  공식 문서는 여기에서 확인할 수 있습니다.

 

(케스케이드 구조)
- 케스케이드 구조는, 안면 검출시, 얼굴이 아닌 범위를 걸러내는 방식이라 할 수 있습니다.
- 1단계에서 얼굴 검출에 가장 유용한 유사-하르 필터를 사용하여 얼굴이 아닌 것을 걸러내고,
2단계에서 필터 5개를 사용하여 보다 자세히 검사하고,
3단계에서 필터 20개를 사용함으로써,
얼굴이 아닌 것을 걸러내며 최종적으로 얼굴임을 판단하는 것입니다.

 

기본적인 개념 외에도, 구현 작업에서 필요한 XML 파일의 역할에 대해서도 설명이 잘 돼있어 아래와 같이 인용하겠습니다.

 

- xml 파일
: xml파일은 미리 훈련된 분류기 정보입니다.
CascadeClassifier는, 단순히 얼굴 검출기라기보다는, 유사-하르 필터를 이용하여 유사도로 특정 객체를 검출해내는 검출기로, 어떤 객체를 검출할지를 바로 이 xml파일에 설정함으로써 변경 가능하고, load 메서드를 통해 미리 훈련된 분류기 정보를 가져올 수 있습니다.
미리 훈련된 분류기 XML 파일은 OpenCV에서 제공하며,
%OPENCV_DIR%\etc\haarcascades
에서 찾을 수 있습니다.

 

그렇습니다. 검출하고자 하는 용도에 맞게 XML 파일을 미리 로드할 필요가 있습니다. 즉, 특정 데이터에 의해서 이미 훈련돼있는 모델을 가져오는 개념으로 이해했습니다. 그렇게 되면 유저들은 애플리케이션에 적당히 적용만 하면 되는 거죠. 특별히 데이터 학습에 대한 고민은 하지 않아도 되는 것 같습니다. 

 

XML종류에 따라서는 사람의 얼굴뿐만 아니라 고양이 얼굴 그리고 러시아 자동차 번호판? 도 검출할 수 있도록 파일이 분리가 되어 있네요. Loner님 또 한 번 감사합니다.🙇🏻‍♂️

 

다양한 XML 파일 구성

 

Cascade Classifier 구현 😎

 

실제 이미지를 사용하여 구현해보겠습니다. 이론에 비해 구현에 관련된 조작은 상당히 간단하다고 생각됩니다.

실습에 사용한 이미지는 요즘 최고의 주가를 달리고 있는 Elon Musk님의 사진을 이용해 보겠습니다.

 

I LOVE TESLA.

 

☑️ 라이브러리 불러오기 및 코드의 기본 구성

 

이미지를 읽고, 프로세싱하고, 다시 화면에 출력하기 위해 필요한 라이브러리 들을 불러와줍니다.

 

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/objdetect.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main(){
	// Read Image.
	string path= "Resources/elon.png";
	Mat img = imread(path);
    
	...
    
	// Show Img.
	imshow("Image", img);
	waitKey(0);
    
	return 0;
}

 

☑️ Classifier Object 준비

 

이제 Cascasde Classifier 객체를 선언 해준 후, 얼굴 인식에 사용할 모델을 불러와야겠죠. 우리는 처음부터 모델을 훈련하고 구성할 필요가 없다고 했습니다. 이 역할을 XML 파일이 대신해줍니다. 그럼 아래와 같이 XML 파일까지 Load 해서 분류기가 얼굴을 감지할 수 있게 준비합니다.

여기에서는 "haarcascade_frontalface_default.xml" 을 적용했습니다. 저는 XML 파일을 현재 프로젝트 폴더에 복사해서 사용했습니다. load 하는 경로는 다를 수 있습니다. 

 

	CascadeClassifier faceCascade;
	faceCascade.load("Resources/haarcascade_frontalface_default.xml");
    
	// check xml file loading is success or not
	if(faceCascade.empty()){cout << "XML file not loaded ! " << endl;}

 

☑️ detectMultiScale 함수 호출

 

준비는 모두 끝낫습니다. 이제 detectMultiScale을 호출하면 Haar-Feature 필터링 기법으로 입력한 이미지에서 얼굴을 찾게 됩니다. 이 함수는 얼굴을 검출한 후 검출에 대한 피드백 요소를 반환하는데, 사각형 형태의 요소들로 피드백을 표현하게 됩니다. 이렇게 무언가 반환하게 되면, 우리는 변수를 통해 데이터를 받을 수 있게 되죠.

 

드디어 벡터 변수를 도입할 순서가 되었습니다.

 

📚 벡터 변수 선언 방법

	// 1차원 벡터 선언
	vector<자료형> variableName

	// 2차원 벡터선언
	vector<vector<자료형>> variableName
    
	// 3차원 벡터선언
	vector<vector<vector<자료형>>> variableName
    
	...

 

벡터 변수는 "오브젝트 변수"와 같은 느낌적인 느낌이다. 지금도 사각형 자료형을 갖는 오브젝트를 통째로 받기 위해, faces라는 벡터 변수를 선언하게 됩니다. 이렇게 되면, faces 변수에는 Rect 자료형의 오브젝트가 반환되며, 손쉽게 사각형 객체의 특징을 사용할 수 있게 됩니다. (검출된 테두리를 그리기 위한, 꼭짓점 데이터를 사용 ) 

 

	// detect된 객체를 표시할 사각형 자료형을 갖는 벡터를 정의
	vector<Rect> faces;
	faceCascade.detectMultiScale(img, faces, 1.1, 3);

 

벡터 변수는 자료형(위:Rect )과 변수 이름(위 : faces), 그리고 세모 괄호인가? 명칭을 정확하게 모르겠지만, 보통 키보드 물음표 왼쪽에 위치한 괄호를 이용하여 선언할 수 있습니다. 함수(메서드)에 대한 설명은 다음과 같습니다. 

 

detectMultiScale 공식 문서 캡쳐

 

공식 문서에서 힌트를 얻을 수 있네요. Rect자료형의 벡터를 objects 자리에 대입해주면 되겠습니다.

 


detectMultiScale(const Mat& image, vector<Rect>& objects, double scaleFactor=1.1, int minNeighbors=3, int flags=0, Size minSize=Size(),Size maxSize=Size())

PARAMETERS

*image – Matrix of the type CV_8U containing an image where objects are detected.
*objects – Vector of rectangles where each rectangle contains the detected object.
*scaleFactor – Parameter specifying how much the image size is reduced at each image scale.
*minNeighbors – Parameter specifying how many neighbors each candidate rectangle should have to retain it.
*flags – Parameter with the same meaning for an old cascade as in the function cvHaarDetectObjects. It is not used for a new cascade.
*minSize – Minimum possible object size. Objects smaller than that are ignored.
*maxSize – Maximum possible object size. Objects larger than that are ignored.

 

☑️ Print Out

 

다시 실습으로 돌아와서, faces 변수에 얼굴 검출에 대한 피드백 요소가 저장되었을 겁니다. 그럼 이 요소들을 이미지에 올려서 확인해 보도록 하겠습니다. 사각형 요소로 검출되었기 때문에 Rectangle이라는 함수를 이용할 수 있습니다. 해당 함수의 자세한 적용방법을 여기를 참고해주세요.

 

	// print out lines for detected boundary
	for(int i=0 ; i<faces.size() ; i++)
	{
		rectangle(img, faces[i].tl(), faces[i].br(),  Scalar(255,0,255),3);
	}

 

faces.size()를 for 문에 사용하였습니다. 만약에 얼굴이 여러 개인 사진을 적용 할 경우에도 이 코드는 강력하게 모든 사람들 얼굴(for 반복문 덕분)에 테두리를 만들어 줄 걸로 예상할 수 있습니다. 또한, 라벨링 하는 방법도 여기에서 참고할 수 있으며, 텍스트의 좌표값으로 faces[i].tl() 를 이용하면 그 위치를 조절할 수 있을 것입니다. 복수 얼굴에 대한 검증과 라벨링은 각자 진행해 보면 더 재밌을 것 같네요. 😎  읽어주셔서 감사합니다.

 

Future Maker detected.

반응형