这篇文章是对应之前人脸对齐的python版本的C++版本,之前python版本见这里。通俗理解就是将图片人人脸姿态不太正确的给矫正过来,所以写了C++版本的人脸对齐算法。基本原理是先通过MTCNN检测到人脸的五个关键点,再把原图中人脸区域外扩100%(这样做的目的是保证对齐后图片中没有黑色区域,当然这个外扩的比例是看对齐效果自己可以调节的,我这里设置的100%)。最后的人脸对齐尺寸分为两种:112X96尺寸和112X112尺寸,其中首先需要定死仿射变换后人脸在目标图上的坐标,然后直接变换。废话不多说,直接手撕代码。
#include#include #include #include #include #include #include #include #include "detect/mtcnn/detector.h" #include "detect/draw.hpp" #include "align/FaceProprocess.h" #include "align/FaceAlign.h" int main() { /检测部分/ std::string modelPath = "detect/models/"; std::string picPath; ProposalNetwork::Config pConfig; pConfig.caffeModel = modelPath + "det1.caffemodel"; pConfig.protoText = modelPath + "det1.prototxt"; pConfig.threshold = 0.6f; RefineNetwork::Config rConfig; rConfig.caffeModel = modelPath + "/det2.caffemodel"; rConfig.protoText = modelPath + "/det2.prototxt"; rConfig.threshold = 0.7f; OutputNetwork::Config oConfig; oConfig.caffeModel = modelPath + "/det3.caffemodel"; oConfig.protoText = modelPath + "/det3.prototxt"; oConfig.threshold = 0.7f; MTCNNDetector detector(pConfig, rConfig, oConfig); ifstream infile("data.txt"); if (!infile) {//文件没打开 std::cout << "文件不存在!" << std::endl; } while (infile >> picPath) { cv::Mat img = cv::imread(picPath); std::vector faces; { faces = detector.detect(img, 20.f, 0.709f); } std::cout << "Number of faces found in the supplied image - " << faces.size() << std::endl; std::vector data; /检测部分/ /对齐部分1/ double coord5point1[10] = { 30.2946, 65.5318, 48.0252, 33.5493, 62.7299, 51.6963, 51.5014, 71.7366, 92.3655, 92.2041 }; //112x96的目标点 double coord5point2[10] = { 30.2946 + 8.0000, 65.5318 + 8.0000, 48.0252 + 8.0000, 33.5493 + 8.0000, 62.7299 + 8.0000, 51.6963, 51.5014, 71.7366, 92.3655, 92.2041 }; //112x112的目标点 for (size_t i = 0; i < faces.size(); i++) { int x1 = faces[i].bbox.x1; int y1 = faces[i].bbox.y1; int x2 = faces[i].bbox.x2; int y2 = faces[i].bbox.y2; //外扩100%,防止对齐后人脸出现黑边 int new_x1 = std::max(int(1.50 * x1 - 0.50 * x2), 0); int new_x2 = std::min(int(1.50 * x2 - 0.50 * x1), img.cols - 1); int new_y1 = std::max(int(1.50 * y1 - 0.50 * y2), 0); int new_y2 = std::min(int(1.50 * y2 - 0.50 * y1), img.rows - 1); Mat enlargedFace = img(Rect(new_x1, new_y1, new_x2 - new_x1 + 1, new_y2 - new_y1 + 1)); //imwrite("enlargedFace.jpg", enlargedFace); //得到原始图中关键点 int left_eye_x = faces[i].ptsCoords[0]; int left_eye_y = faces[i].ptsCoords[1]; int right_eye_x = faces[i].ptsCoords[2]; int right_eye_y = faces[i].ptsCoords[3]; int nose_x = faces[i].ptsCoords[4]; int nose_y = faces[i].ptsCoords[5]; int left_mouth_x = faces[i].ptsCoords[6]; int left_mouth_y = faces[i].ptsCoords[7]; int right_mouth_x = faces[i].ptsCoords[8]; int right_mouth_y = faces[i].ptsCoords[9]; //得到外扩100% 后图中关键点坐标(外扩的目的是为了防止对齐后出现黑边) int new_left_eye_x = left_eye_x - new_x1; int new_right_eye_x = right_eye_x - new_x1; int new_nose_x = nose_x - new_x1; int new_left_mouth_x = left_mouth_x - new_x1; int new_right_mouth_x = right_mouth_x - new_x1; int new_left_eye_y = left_eye_y - new_y1; int new_right_eye_y = right_eye_y - new_y1; int new_nose_y = nose_y - new_y1; int new_left_mouth_y = left_mouth_y - new_y1; int new_right_mouth_y = right_mouth_y - new_y1; double M[6]; double enlargeFace_landmark[10] = { new_left_eye_x, new_right_eye_x, new_nose_x, new_left_mouth_x, new_right_mouth_x, new_left_eye_y, new_right_eye_y, new_nose_y, new_left_mouth_y, new_right_mouth_y }; //112x112 getAffineMatrix(enlargeFace_landmark, coord5point2, M); Mat warp_mat_112x112 = (Mat_ (2, 3) << M[0], M[1], M[2], M[3], M[4], M[5]); Mat alignFace_112x112 = Mat::zeros(112, 112, img.type()); warpAffine(enlargedFace, alignFace_112x112, warp_mat_112x112, alignFace_112x112.size());//裁剪图片 int position = picPath.find("/", 0); imwrite("result/" + picPath.substr(position + 1, 4) + "_align_112x112.jpg", alignFace_112x112); //112x96 getAffineMatrix(enlargeFace_landmark, coord5point1, M); Mat warp_mat_112x96 = (Mat_ (2, 3) << M[0], M[1], M[2], M[3], M[4], M[5]); Mat alignFace_112x96 = Mat::zeros(112, 96, img.type()); warpAffine(enlargedFace, alignFace_112x96, warp_mat_112x96, alignFace_112x96.size());//裁剪图片 imwrite("result/" + picPath.substr(position + 1, 4) + "_align_112x96.jpg", alignFace_112x96); } } /对齐部分1/ return 0; }
运行上述代码,效果如下所示:
注意事项:该工程通过Visual studio 2019编写,环境仅仅依赖OpenCV,下载到工程后,需要设置OpenCV的包含目录和库目录,并设置附加依赖项。然后直接打开工程运行即可得到上述结果过。
完整的工程下载点这里(code: face), 欢迎下载使用。



