[OpenCV] 얼굴 인식 관련 정리 – Facial Landmark, Dlib

이 글은 OpenCV를 사용하여 얼굴 인식 프로그램을 만든 뒤 그 내용에 대한 정리 글입니다.

얼굴 인식을 하는 프로그램을 만들고 싶어 자료 조사 중 FaceMark API에 대해 알게 되었습니다.
Facial Landmark Detection라고도 불리며 얼굴 인식을 하는 기능을 제공하며, 예시로 아래와 같이 인식이 가능합니다.

실제 결과물

FaceMark API를 사용하기 위해서는 아래 툴이 설치되어 있어야합니다.

  1. Visual Studio 2019: https://visualstudio.microsoft.com/ko/vs/older-downloads/
  2. CMake: https://cmake.org/

개발 환경은 Windows 10 x64이며 Visual Studio 2019, cmake-3.23.2 버전으로 다운 받았습니다.
각 프로그램을 설치하는 것은 구글에 검색하면 잘 나옵니다.

OpenCV

FaceMark를 사용하기 위해서는 OpenCV를 우선 설치해야 합니다.
OpenCV 홈페이지에 접속하여 다운로드를 합니다.

아래와 같이 다운로드가 됩니다.

더블클릭하여 실행한 다음 아래와 같은 경로 C:\FaceMark 에 추출합니다

진행이 되며 설정한 경로에 OpenCV가 추출됩니다

dlib

FaceMark는 dlib를 이용하여 구현합니다. dlib 홈페이지에 접속하여 아래와 같이 github에 접속합니다.
링크: dlib 홈페이지, dlib github

그 다음 zip을 다운받습니다

아래와 같이 zip파일이 다운 되면 압축을 해제 합니다.

그러면 dlib-master폴더 안에 동일한 이름으로 dlib-maskter폴더가 또 생기는데 이것을 dlib로 바꿔줍니다.

이름을 수정한 dlib 폴더 내부에 build와 source폴더를 생성 해준 뒤 두 개 폴더를 제외한 나머지 파일들은 source 폴더 안으로 이동합니다. 

이제 dlib 폴더를 아까 opencv를 추출한 경로(C:\FaceMark)로 이동합니다

CMake

이제 CMake를 실행하여 아래와 같이 경로를 입력한 뒤, Configure 버튼을 클릭합니다.

아래와 같이 Visual Studio 16 2019와 x64를 선택한 뒤 Finish 버튼을 클릭합니다.

진행이 완료되면 아래와 같이 붉은색으로 표시되는데 신경쓰지 말고 Generate 버튼을 클릭합니다

그러면 C:\FaceMark\dlib\build 폴더에 파일들이 생겼을 것입니다. 이 중 dlib_project.sln을 더블클릭하여 실행합니다

그리고 Release와 x64로 바꾼 뒤 ALL_BUILD를 우클릭하여 다시 빌드를 선택합니다

그럼 C:\FaceMark\dlib\build\dlib\Release경로에 dlib19.24.99_release_64bit_msvc1929.lib파일이 하나 생성 됩니다.
이 파일 이름이 너무 길으니 dlib.lib로 변경해줍니다

include, lib 폴더 생성

include할 파일과 링크할 lib파일은 모두 준비가 됐습니다.
visual로 링크하기 전, 정리를 위해  C:\FaceMark경로에 include와 lib 폴더를 각각 생성합니다.

include 폴더에는 아래 항목을 복사해 넣습니다

  • C:\FaceMark\opencv\build\include\opencv2 폴더
  • C:\FaceMark\dlib\source\dlib 폴더

lib 폴더에는 아래 항목을 복사해 넣습니다

  • C:\FaceMark\opencv\build\x64\vc15\lib\opencv_world455.lib
  • C:\FaceMark\dlib\build\dlib\Release\dlib.lib

코드 작성

Visual 2019를 실행하여 빈 프로젝트를 하나 생성하여 main.cpp를 생성해줍니다. 프로젝트명은 편한대로 생성합니다.

프로젝트를 우클릭 하여 속성을 선택합니다

구성과 플랫폼을 Release, x64로 변경합니다.

그 다음 C/C++ – 추가 포함 디렉터리에 C:\FaceMark\include를 입력합니다.

링커 – 일반 – 추가 라이브러리 디렉터리에 C:\FaceMark\lib를 입력합니다.

링커 – 입력 – 추가 종속성에 dlib.lib와 opencv_world455.lib를 입력합니다. 이 두개는 C:\FaceMark\lib경로에 있는 파일입니다

그 다음으로 C:\FaceMark\include\dlib\all\source.cpp파일을 프로젝트에 추가해줍니다.

그 다음 아래와 같이 코드를 입력합니다.

#include <iostream>

#include <dlib/opencv.h>
#include <dlib/image_processing.h>
#include <dlib/image_processing/frontal_face_detector.h>

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>

using namespace std;
using namespace cv;
using namespace dlib;

#define COLOR_DETECT Scalar(0, 255, 0)

// connect line
void connectLine(cv::Mat & img, full_object_detection landmarks, int iStart, int iEnd, bool isClosed = false)
{
	std::vector<cv::Point> points;
	for (int i = iStart ; i < iEnd ; i++)
	{
		points.push_back(cv::Point(landmarks.part(i).x(), landmarks.part(i).y()));
	}
	cv::polylines(img, points, isClosed, COLOR_DETECT, 2, 16);
}

// draw polygon
void drawPolygon(cv::Mat & img, full_object_detection landmarks)
{
	// shape_predictor_68_face_landmarks
	// facial landmark number: face.png
	connectLine(img, landmarks, 0, 16); // 턱
	connectLine(img, landmarks, 17, 21); // 왼쪽 눈썹
	connectLine(img, landmarks, 22, 26); // 오른쪽 눈썹
	connectLine(img, landmarks, 27, 30); // 콧대
	connectLine(img, landmarks, 30, 35, true); // 낮은 코
	connectLine(img, landmarks, 36, 41, true); // 왼쪽 눈
	connectLine(img, landmarks, 42, 47, true); // 오른쪽 눈
	connectLine(img, landmarks, 48, 59, true); // 입술 바깥쪽
	connectLine(img, landmarks, 60, 67, true); // 입술 안쪽 부분
}

// dlib rectangle to opencv rect
cv::Rect dlibRectToOpencv(dlib::rectangle r)
{
	return cv::Rect(cv::Point2i(r.left(), r.top()), cv::Point2i(r.right(), r.bottom()));
}

int main()
{
	Mat img = imread("lenna.bmp", IMREAD_COLOR);
	if (img.empty())
	{
		cerr << "img open fail" << endl;
		return -1;
	}

	frontal_face_detector detector = get_frontal_face_detector();
	shape_predictor landmarkDetector;
	deserialize(".\\shape_predictor_68_face_landmarks.dat") >> landmarkDetector;

	cv_image<bgr_pixel> dlib_img(img);
	std::vector<dlib::rectangle> faceRects = detector(dlib_img);
	int iFaceCount = faceRects.size();

	// draw
	for (int i = 0 ; i < iFaceCount ; i++)
	{
		full_object_detection faceLandmark = landmarkDetector(dlib_img, faceRects[i]);
		drawPolygon(img, faceLandmark);
	}

	imshow("img", img);
	waitKey();

	destroyAllWindows();
	return 0;
}

상단에 빌드 환경을 Release, x64로 맞춘 뒤 솔루션을 빌드합니다.

여기까지 정상적으로 따라왔다면 아래와 같이 출력창에 빌드 성공 메시지가 출력합니다.

실행 파일과 같은 위치에 파일 위치

아마 Ctrl + F5를 누르거나(F5가 아닙니다.) .exe 파일을 실행하면 opencv_world455.dll이 없다고 하거나, 프로그램이 정상적으로 실행되지 않을 수 있습니다.

[솔루션 폴더 경로]\x64\Release에 프로그램 실행을 위한 파일들이 없어서 그런 것입니다.

  1. lenna.bmp는 인터넷에서 찾아서 넣어준다. 
  2. C:\FaceMark\opencv\build\x64\vc15\bin\opencv_world455.dll을 위 경로에 넣어준다
  3. shape_predictor_68_face_landmarks.dat파일은 얼굴 모델이 학습된 dat 파일인데 구글에 dlib model이라고 검색 한뒤 github에서 다운받을 수 있다.
    – github: https://github.com/davisking/dlib-models
    – bz2: http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2

그 다음 실행해서 확인합니다.

각 점에 대한 정보는 아래와 같습니다

조금만 활용하면 아래와 같이 동영상에서 얼굴 인식도 가능합니다

추가적으로 찾아보니 구글에서 얼굴 인식과 관련하여 MediaPipe라는것도 있습니다.
MediaPipe는 얼굴 인식 뿐만 아니라 손 인식, 몸 인식 등 3D로 제공해줍니다.