사실 영상처리 분야에 대해서는 하나도 관심이 없지만 교수님의 지시사항으로 연구 일환으로 진행한 내용이며
연구실에서 진행한 내용이지만 해당 내용은 두개의 예제를 합친것과 같다고 생각되기 때문에 블로그에 작성해도 큰 문제가 없을것으로 예상된다. 예제는 오픈소스인데다 참고한 알고리즘 블로그 또한 오픈소스이다.
Open CV의 영상처리와 dlib라이브러리의 영상처리 두가지를 섞어서 사용한 기술이다.
dlib라이브러리의 example폴더에 face_landmark_detection_ex.cpp 와 webcam_face_pose_ex.cpp와 아래 소개할 블로그의 HeadPose에 대한 내용을 섞은 느낌이라고 생각하면된다.
www.learnopencv.com/head-pose-estimation-using-opencv-and-dlib/
Head Pose Estimation using OpenCV and Dlib
In this tutorial we will learn how to estimate the pose of a human head in a photo using OpenCV and Dlib. In many applications, we need to know how the head is tilted with respect to a camera. In a virtual reality application, for example, one can use the
www.learnopencv.com
HeadPose에 대한 코드 및 알고리즘에 대한 내용은 이분께서 블로그에 잘 작성해 주셨기에 따로 적지 않는다.
이분의 코드를 약간 변형해서 Wecam에서도 사용할 수 있도록 변형했다.
웹캠이 없어서 스마트폰을 웹캠으로 만들어주는 프로그램들을 이용해서 테스트 했다.
dlib19.19 버전과 OpenCV 4.2.0 버전을 이용하였다.
이 코드를 만약에 사용하실 분이 계시다면
웹캠에 얼굴이 2명이상 잡히면 죽는 버그를 수정해야 사용하실 수 있습니다.
#if (_MSC_VER >= 1915)
#define no_init_all deprecated
#endif
#include <dlib/opencv.h>
#include <opencv2/highgui/highgui.hpp>
#include <dlib/image_processing/frontal_face_detector.h>
#include <dlib/image_processing/render_face_detections.h>
#include <dlib/image_processing.h>
#include <dlib/gui_widgets.h>
#include <opencv2/opencv.hpp>
using namespace dlib;
using namespace std;
int main()
{
try
{
cv::VideoCapture cap(0);
if (!cap.isOpened())
{
//Sleep(1);
cerr << "Unable to connect to camera" << endl;
return 1;
}
image_window win;
// Load face detection and pose estimation models.
frontal_face_detector detector = get_frontal_face_detector();
shape_predictor pose_model;
deserialize("shape_predictor_68_face_landmarks.dat") >> pose_model;
// Grab and process frames until the main window is closed by the user.
while (!win.is_closed())
{
// Grab a frame
cv::Mat temp;
if (!cap.read(temp))
{
break;
}
// Turn OpenCV's Mat into something dlib can deal with. Note that this just
// wraps the Mat object, it doesn't copy anything. So cimg is only valid as
// long as temp is valid. Also don't do anything to temp that would cause it
// to reallocate the memory which stores the image as that will make cimg
// contain dangling pointers. This basically means you shouldn't modify temp
// while using cimg.
IplImage ipl_img = cvIplImage(temp);
cv_image<bgr_pixel> cimg(ipl_img);
// Detect faces
// 얼굴갯수
std::vector<rectangle> faces = detector(cimg);
// Find the pose of each face.
std::vector<full_object_detection> shapes;
std::vector<cv::Point2d> image_points;
// 얼굴 갯수에 따른 루프
for (unsigned long i = 0; i < faces.size(); ++i)
{
full_object_detection shape = pose_model(cimg, faces[i]);
// 68 land mark 사용중이라 68개 전부 출력됨
/*
cout << "number of parts: " << shape.num_parts() << endl;
for (int j = 0; j <= shape.num_parts(); ++j) {
cout << "pixel position of "<< j << " part: " << shape.part(j) << endl;
}
*/
// 2D image Points
image_points.push_back(cv::Point2d(shape.part(30).x(), shape.part(30).y())); // Nose tip
image_points.push_back(cv::Point2d(shape.part(8).x(), shape.part(8).y())); // Chin
image_points.push_back(cv::Point2d(shape.part(45).x(), shape.part(45).y())); // Left eye left corner
image_points.push_back(cv::Point2d(shape.part(36).x(), shape.part(36).y())); // Right eye right corner
image_points.push_back(cv::Point2d(shape.part(54).x(), shape.part(54).y())); // Left Mouth corner
image_points.push_back(cv::Point2d(shape.part(48).x(), shape.part(48).y())); // Right mouth corner
shapes.push_back(shape);
}
if (image_points.size() == 0)
{
win.clear_overlay();
win.set_image(cimg);
continue;
}
// 임의의 3D 모델 포인트
// 3D model points.
std::vector<cv::Point3d> model_points;
model_points.push_back(cv::Point3d(0.0f, 0.0f, 0.0f)); // Nose tip
model_points.push_back(cv::Point3d(0.0f, -330.0f, -65.0f)); // Chin
model_points.push_back(cv::Point3d(-225.0f, 170.0f, -135.0f)); // Left eye left corner
model_points.push_back(cv::Point3d(225.0f, 170.0f, -135.0f)); // Right eye right corner
model_points.push_back(cv::Point3d(-150.0f, -150.0f, -125.0f)); // Left Mouth corner
model_points.push_back(cv::Point3d(150.0f, -150.0f, -125.0f)); // Right mouth corner
// Camera internals
double focal_length = temp.cols; // Approximate focal length.
cv::Point2d center = cv::Point2d(temp.cols / 2, temp.rows / 2);
cv::Mat camera_matrix = (cv::Mat_<double>(3, 3) << focal_length, 0, center.x, 0, focal_length, center.y, 0, 0, 1);
cv::Mat dist_coeffs = cv::Mat::zeros(4, 1, cv::DataType<double>::type); // Assuming no lens distortion
//cout << "Camera Matrix " << endl << camera_matrix << endl;
// Output rotation and translation
cv::Mat rotation_vector; // Rotation in axis-angle form
cv::Mat translation_vector;
// Solve for pose
cv::solvePnP(model_points, image_points, camera_matrix, dist_coeffs, rotation_vector, translation_vector);
// Project a 3D point (0, 0, 1000.0) onto the image plane.
// We use this to draw a line sticking out of the nose
std::vector<cv::Point3d> nose_end_point3D;
std::vector<cv::Point2d> nose_end_point2D;
nose_end_point3D.push_back(cv::Point3d(0, 0, 1000.0));
projectPoints(nose_end_point3D, rotation_vector, translation_vector, camera_matrix, dist_coeffs, nose_end_point2D);
for (int i = 0; i < image_points.size(); i++)
{
circle(temp, image_points[i], 3, cv::Scalar(0, 0, 255), -1);
}
cv::line(temp, image_points[0], nose_end_point2D[0], cv::Scalar(255, 0, 0), 2);
cout << "Rotation Vector " << endl << rotation_vector << endl;
//cout << "Translation Vector" << endl << translation_vector << endl;
//cout << nose_end_point2D << endl;
// Display it all on the screen
win.clear_overlay();
win.set_image(cimg);
win.add_overlay(render_face_detections(shapes));
}
}
catch (serialization_error& e)
{
cout << "You need dlib's default face landmarking model file to run this example." << endl;
cout << "You can get it from the following URL: " << endl;
cout << " http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2" << endl;
cout << endl << e.what() << endl;
}
catch (exception& e)
{
cout << e.what() << endl;
}
}