人脸对齐过程实现 c++和python

c++ 实现版本:

   1 人脸检测
        1.1 使用mtcnn-ncnn进行人脸检测,会输出face box和landmark5
            face box: [x1,y1,x2,y2]
            landmark5: [left_eye,right_eye, nose, month_left, month_right]
    2 图像变换(使用Egien矩阵库)
        2.1 下载Egien
        2.2 创建一个项目, 引用Egien,
        2.3 使用SVD分解, 计算变换矩阵, 
        2.4 进行图像的仿射变换
		=================================================
		代码如下:
		#pragma once
		#include<Dense>
		#include"opencv_base.h"

		using namespace std;
		using namespace Eigen;

		/*
		使用五点人脸对齐
		left_eye, right_eye, nose, left_month, right_month
		*/
		static  Mat face_align_bypoint5(Mat src_frame, vector<Point2d> landmark5, vector<Point2d> box) {
			MatrixXf src_landmark_mtx(5, 2);
			int i = 0;
			for (size_t r = 0; r < landmark5.size(); r++)
			{
				src_landmark_mtx(r, 0) = landmark5.at(r).x;
				src_landmark_mtx(r, 1) = landmark5.at(r).y;
				i++;
			}

			Matrix<float, 5, 2> dst_landmark_mtx;
			dst_landmark_mtx << 30.2946, 51.6963,
				65.5318, 51.6963,
				48.0252, 71.7366,
				33.5493, 92.3655,
				62.7299, 92.3655;

			int rows = src_landmark_mtx.rows();
			int cols = src_landmark_mtx.cols();
			MatrixXf mean1 = src_landmark_mtx.colwise().mean(); // [1,2]
			MatrixXf mean2 = dst_landmark_mtx.colwise().mean(); // [1,2]
			float col_mean_1 = mean1(0, 0);  // 列的均值
			float col_mean_2 = mean2(0, 0);

			//cout << src_landmark_mtx << endl;
			//cout << dst_landmark_mtx << endl;

			// std = sqrt(mean(abs(x - x.mean())**2))
			Matrix<float, 5, 2> m1, m2;
			for (size_t r = 0; r < rows; r++)
			{
				for (size_t c = 0; c < cols; c++)
				{
					src_landmark_mtx(r, c) -= mean1(0, c);
					dst_landmark_mtx(r, c) -= mean2(0, c);

					auto abs_v1 = std::abs(src_landmark_mtx(r, c));
					auto abs_v2 = std::abs(dst_landmark_mtx(r, c));
					m1(r, c) = pow(abs_v1, 2);
					m2(r, c) = pow(abs_v2, 2);
				}
			}

			float std1 = sqrt(m1.mean());
			float std2 = sqrt(m2.mean());
			src_landmark_mtx /= std1;
			dst_landmark_mtx /= std2;

			//printf_s(" ============= std norm =============\n");
			//cout << src_landmark_mtx << endl;
			//cout << dst_landmark_mtx << endl;

			MatrixXf m = src_landmark_mtx.transpose() * dst_landmark_mtx;  // [2,2]
			JacobiSVD<MatrixXf> svd(m, ComputeThinU | ComputeThinV);
			MatrixXf VT = svd.matrixV().transpose();
			MatrixXf U = svd.matrixU();
			MatrixXf  A = svd.singularValues();
			MatrixXf R = (U*VT).transpose();  // [2,2]

			//printf_s(" ============= svd =============\n");
			//cout << VT << endl;
			//cout << U << endl;
			//cout << A << endl;
			//cout << R << endl;

			MatrixXf M(3, 3);
			M.row(2) << 0., 0., 1.;
			MatrixXf t1 = (std1 / std2)*R;	// [2,2]
			MatrixXf t2 = mean2.transpose() - (std1 / std2)*R*mean1.transpose();  // [2,1]
			M.row(0) << t1(0, 0), t1(0, 1), t2(0, 0);
			M.row(1) << t1(1, 0), t1(1, 1), t2(1, 0);

			//printf_s(" ============= transformer =============\n");
			//cout << M << endl;

			//printf_s(" ============= face transformer =============\n");
			Mat cv_transformMat = (Mat_<double>(2, 3) << \
				M(0, 0), M(0, 1), M(0, 2), \
				M(1, 0), M(1, 1), M(1, 2));
			Mat align_img = Mat::zeros(src_frame.rows * 3, src_frame.cols * 3, src_frame.type());
			src_frame.copyTo(align_img(Rect(100, 100, src_frame.cols, src_frame.rows)));
			for (size_t k = 0; k < box.size(); k++)
			{
				box.at(k).x += 100;
				box.at(k).y += 100;
			}
			//rectangle(align_img, 
			//	Rect(box[0].x, box[0].y, 
			//	box[3].x - box[0].x, 
			//	box[3].y - box[0].y),
			//	Scalar(0, 255, 0), 1, 1);
			for (size_t k = 0; k < landmark5.size(); k++)
			{
				landmark5.at(k).x += 100;
				landmark5.at(k).y += 100;
				//circle(align_img, landmark5.at(k), 2, Scalar(0, 255, 0), 1);
			}

			//imshow("align", align_img);
			warpAffine(align_img, align_img, cv_transformMat, align_img.size());

			//printf_s(" ============= point transformer =============\n");
			Rect roi(0, 0, 0, 0);
			for (size_t r = 0; r < box.size(); r++)
			{
				auto x = box.at(r).x, y = box.at(r).y;
				box[r].x = x * M(0, 0) + y * M(0, 1) + M(0, 2);
				box[r].y = x * M(1, 0) + y * M(1, 1) + M(1, 2);
			}
			for (size_t r = 0; r < landmark5.size(); r++)
			{
				auto x = landmark5.at(r).x, y = landmark5.at(r).y;
				landmark5[r].x = x * M(0, 0) + y * M(0, 1) + M(0, 2);
				landmark5[r].y = x * M(1, 0) + y * M(1, 1) + M(1, 2);
			}

			// 由于box的点对齐后会出现 脸不全的情况,需要使用对齐后的关键点来矫正box
			auto dis_left_eye_right_eye = landmark5[1].x - landmark5[0].x;
			auto dis_left_month_left_eye = landmark5[landmark5.size() - 2].y - landmark5[0].y;
			box[0].x = min(box[0].x, landmark5[0].x - dis_left_eye_right_eye / 2);		// left top X;
			box[0].y = min(box[0].y, landmark5[0].y - dis_left_month_left_eye / 1.1);		// left top Y;
			box[box.size() - 1].x = max(box[box.size() - 1].x, landmark5[1].x + dis_left_eye_right_eye / 2);		// right bottom X;
			box[box.size() - 1].y = max(box[box.size() - 1].y, landmark5[landmark5.size() - 1].y + dis_left_month_left_eye / 1.1);		// right bottom y;


			roi.x = max(0, int(box[0].x));
			roi.y = max(0, int(box[0].y));
			roi.width = min(int(box.at(box.size() - 1).x - roi.x) + 10, align_img.cols - roi.x);
			roi.height = min(int(box.at(box.size() - 1).y - roi.y) + 10, align_img.rows - roi.y);
			Mat align_face = align_img(roi);

			//// draw
			//rectangle(align_img, roi, Scalar(0, 0, 255), 1, 1);
			//for (size_t k = 0; k < landmark5.size(); k++)
			//{
			//	circle(align_img, landmark5.at(k), 2, Scalar(0, 0, 255), 1);
			//}

			//resize(align_face, align_face, Size(300, 350));

			//imshow("src", src_frame);
			//imshow("align2", align_img);
			//imshow("align3", align_face);
			//waitKey(0);

			return align_face;
		}

		static Mat face_align_bypoint72(Mat src_frame, vector<Point2d> landmark72, vector<Point2d> box) {
			int rows = landmark72.size();
			int cols = 2;
			MatrixXf src_landmark_mtx(rows, 2);
			int i = 0;
			for (size_t r = 0; r < landmark72.size(); r++)
			{
				src_landmark_mtx(r, 0) = landmark72.at(r).x;
				src_landmark_mtx(r, 1) = landmark72.at(r).y;
				i++;
			}
			MatrixXf dst_landmark_mtx(rows, 2);
			dst_landmark_mtx << 32.00, 210.00,
				42.00, 281.00,
				54.00, 353.00,
				73.00, 424.00,
				118.00, 497.00,
				189.00, 558.00,
				265.00, 579.00,
				335.00, 553.00,
				400.00, 494.00,
				444.00, 425.00,
				465.00, 353.00,
				477.00, 282.00,
				484.00, 211.00,
				107.00, 256.00,
				132.00, 241.00,
				158.00, 238.00,
				185.00, 245.00,
				205.00, 267.00,
				181.00, 274.00,
				154.00, 275.00,
				128.00, 268.00,
				160.00, 254.00,
				73.000, 212.00,
				107.00, 190.00,
				148.00, 194.00,
				182.00, 207.00,
				214.00, 231.00,
				179.00, 226.00,
				143.00, 219.00,
				107.00, 213.00,
				312.00, 266.00,
				334.00, 243.00,
				361.00, 235.00,
				387.00, 239.00,
				412.00, 254.00,
				392.00, 268.00,
				364.00, 275.00,
				336.00, 273.00,
				361.00, 253.00,
				298.00, 230.00,
				334.00, 207.00,
				369.00, 196.00,
				411.00, 192.00,
				447.00, 213.00,
				412.00, 215.00,
				374.00, 221.00,
				337.00, 228.00,
				229.00, 270.00,
				224.00, 314.00,
				220.00, 357.00,
				204.00, 402.00,
				225.00, 409.00,
				295.00, 410.00,
				320.00, 404.00,
				303.00, 359.00,
				296.00, 315.00,
				287.00, 270.00,
				260.00, 392.00,
				188.00, 465.00,
				227.00, 464.00,
				264.00, 467.00,
				302.00, 461.00,
				340.00, 466.00,
				305.00, 487.00,
				264.00, 492.00,
				223.00, 485.00,
				226.00, 475.00,
				263.00, 481.00,
				302.00, 476.00,
				300.00, 467.00,
				263.00, 467.00,
				229.00, 465.00;

			MatrixXf mean1 = src_landmark_mtx.colwise().mean(); // [1,2]
			MatrixXf mean2 = dst_landmark_mtx.colwise().mean(); // [1,2]
			float col_mean_1 = mean1(0, 0);  // 列的均值
			float col_mean_2 = mean2(0, 0);

			cout << src_landmark_mtx << endl;
			cout << dst_landmark_mtx << endl;

			// std = sqrt(mean(abs(x - x.mean())**2))
			MatrixXf m1(rows, 2), m2(rows, 2);
			for (size_t r = 0; r < rows; r++)
			{
				for (size_t c = 0; c < cols; c++)
				{
					src_landmark_mtx(r, c) -= mean1(0, c);
					dst_landmark_mtx(r, c) -= mean2(0, c);

					auto abs_v1 = std::abs(src_landmark_mtx(r, c));
					auto abs_v2 = std::abs(dst_landmark_mtx(r, c));
					m1(r, c) = pow(abs_v1, 2);
					m2(r, c) = pow(abs_v2, 2);
				}
			}

			float std1 = sqrt(m1.mean());
			float std2 = sqrt(m2.mean());
			src_landmark_mtx /= std1;
			dst_landmark_mtx /= std2;

			printf_s(" ============= std norm =============\n");

			cout << src_landmark_mtx << endl;
			cout << dst_landmark_mtx << endl;

			MatrixXf m = src_landmark_mtx.transpose() * dst_landmark_mtx;  // [2,2]
			JacobiSVD<MatrixXf> svd(m, ComputeThinU | ComputeThinV);
			MatrixXf VT = svd.matrixV().transpose();
			MatrixXf U = svd.matrixU();
			MatrixXf  A = svd.singularValues();
			MatrixXf R = (U*VT).transpose();  // [2,2]

			printf_s(" ============= svd =============\n");
			cout << VT << endl;
			cout << U << endl;
			cout << A << endl;
			cout << R << endl;

			MatrixXf M(3, 3);
			M.row(2) << 0., 0., 1.;
			MatrixXf t1 = (std1 / std2)*R;	// [2,2]
			MatrixXf t2 = mean2.transpose() - (std1 / std2)*R*mean1.transpose();  // [2,1]
			M.row(0) << t1(0, 0), t1(0, 1), t2(0, 0);
			M.row(1) << t1(1, 0), t1(1, 1), t2(1, 0);

			printf_s(" ============= transformer =============\n");
			cout << M << endl;


			printf_s(" ============= face transformer =============\n");
			Mat cv_transformMat = (Mat_<double>(2, 3) << \
				M(0, 0), M(0, 1), M(0, 2), \
				M(1, 0), M(1, 1), M(1, 2));
			Mat align_img = Mat::zeros(src_frame.rows * 3, src_frame.cols * 3, CV_8UC3);
			src_frame.copyTo(align_img(Rect(100, 100, src_frame.cols, src_frame.rows)));
			for (size_t k = 0; k < box.size(); k++)
			{
				box.at(k).x += 100;
				box.at(k).y += 100;
			}
			//rectangle(align_img, Rect(box[0].x, box[0].y, box[3].x - box[0].x, box[3].y - box[0].y),
			//	Scalar(0, 255, 0), 1, 1);
			for (size_t k = 0; k < landmark72.size(); k++)
			{
				landmark72.at(k).x += 100;
				landmark72.at(k).y += 100;
				//circle(align_img, landmark72.at(k), 2, Scalar(0, 255, 0), 1);
			}

			//imshow("align", align_img);
			warpAffine(align_img, align_img, cv_transformMat, align_img.size());

			printf_s(" ============= point transformer =============\n");
			Rect roi(0, 0, 0, 0);
			for (size_t r = 0; r < box.size(); r++)
			{
				auto x = box.at(r).x, y = box.at(r).y;
				box[r].x = x * M(0, 0) + y * M(0, 1) + M(0, 2);
				box[r].y = x * M(1, 0) + y * M(1, 1) + M(1, 2);
			}
			for (size_t r = 0; r < landmark72.size(); r++)
			{
				auto x = landmark72.at(r).x, y = landmark72.at(r).y;
				landmark72[r].x = x * M(0, 0) + y * M(0, 1) + M(0, 2);
				landmark72[r].y = x * M(1, 0) + y * M(1, 1) + M(1, 2);
			}

			// 由于box的点对齐后会出现 脸不全的情况,需要使用对齐后的关键点来矫正box

			//auto dis_left_eye_right_eye = landmark72[38].x - landmark72[21].x;
			//auto dis_left_month_left_eye = landmark72[58].y - landmark72[21].y;
			//box[0].x = min(box[0].x, landmark72[21].x - dis_left_eye_right_eye / 2);		// left top X;
			//box[0].y = min(box[0].y, landmark72[21].y - dis_left_month_left_eye / 1.1);		// left top Y;
			//box[box.size() - 1].x = max(box[box.size() - 1].x, landmark72[38].x + dis_left_eye_right_eye / 2);		// right bottom X;
			//box[box.size() - 1].y = max(box[box.size() - 1].y, landmark72[58].y + dis_left_month_left_eye / 1.1);		// right bottom y;
			//roi.x = max(0, int(box[0].x));
			//roi.y = max(0, int(box[0].y));
			//roi.width = min(int(box.at(box.size() - 1).x - roi.x) + 10, align_img.cols - roi.x);
			//roi.height = min(int(box.at(box.size() - 1).y - roi.y) + 10, align_img.rows - roi.y);

			// 使用脸部外围的极值点来矫正
			double min_x = landmark72[0].x, min_y = landmark72[41].y;
			double max_x = landmark72[12].x, max_y = landmark72[6].y;
			box[0].x = min(box[0].x, min_x);
			box[0].y = min(box[0].y, min_y);
			roi.x = max(0, int(box[0].x));
			roi.y = max(0, int(box[0].y));
			roi.width = min(int(max_x - roi.x) + 5, align_img.cols - roi.x);
			roi.height = min(int(max_y - roi.y) + 5, align_img.rows - roi.y);
			if (roi.width <= 0 || roi.height <= 0)
			{
				return align_img;
			}

			Mat align_face = align_img(roi);

			//// draw box & keypoints
			//rectangle(align_img, roi, Scalar(0, 0, 255), 1, 1);
			//for (size_t k = 0; k < landmark72.size(); k++)
			//{
			//	circle(align_img, landmark72.at(k), 2, Scalar(0, 0, 255), 1);
			//}

			//resize(align_face, align_face, Size(300, 350));

			//imshow("src", src_frame);
			//imshow("align2", align_img);
			//imshow("align3", align_face);
			//waitKey(0);

			return align_face;
		}
		=================================================
    3 输出
        3.1 输出的图像就是垂直的人脸图像

  Python 实现过程:

   1 人脸检测
        1.1 使用mtcnn-tensorflow进行人脸检测,会输出face box和landmark5
            face box: [x1,y1,x2,y2]
            landmark5: [left_eye,right_eye, nose, month_left, month_right]
    2 图像变换(使用numpy矩阵库)
        2.1 使用SVD分解, 计算变换矩阵, 
        2.2 进行图像的仿射变换
		==========================================
				
		# 计算旋转矩阵
		def transformation_from_points(points1, points2):
			points1 = points1.astype(np.float64)
			points2 = points2.astype(np.float64)
			c1 = np.mean(points1, axis=0)
			c2 = np.mean(points2, axis=0)
			points1 -= c1
			points2 -= c2
			s1 = np.std(points1)
			s2 = np.std(points2)
			points1 /= s1
			points2 /= s2
			U, S, Vt = np.linalg.svd(points1.T * points2)
			R = (U * Vt).T
			M = np.vstack([
				np.hstack(((s2 / s1) * R, c2.T - (s2 / s1) * R * c1.T)),
				np.asmatrix([0., 0., 1.])])
			return M


		# 关键点对齐
		def landmark_alignment(src_landmark, M):
			M_matrix = np.asmatrix(M)
			assert M_matrix.shape == (3, 3)
			rows, cols = src_landmark.shape
			src_landmark2 = src_landmark.copy()
			for i in range(rows):
				x, y = src_landmark[i, 0], src_landmark[i, 1]
				x1 = x * M_matrix[0, 0] + y * M_matrix[0, 1] + M_matrix[0, 2]
				y1 = x * M_matrix[1, 0] + y * M_matrix[1, 1] + M_matrix[1, 2]
				src_landmark2[i] = [x1, y1]

			src_landmark2 = src_landmark2.astype(np.int)
			return src_landmark2


		# 人脸对齐
		def face_alignment(src_landmark5, image_numpy):
			h, w, c = image_numpy.shape
			# 5点对齐后的基准点
			dst_landmark5 = np.asmatrix([[30.2946, 51.6963],  # left eye
										 [65.5318, 51.6963],  # right eye
										 [48.0252, 71.7366],  # nose
										 [33.5493, 92.3655],  # left month
										 [62.7299, 92.3655]])  # right month
			# 68点对齐后的基准点
			dst_landmark68 = np.asmatrix([[282, 156], [278, 176], [277, 198], [276, 219],
										  [279, 241], [287, 262], [299, 280], [315, 293],
										  [333, 300], [352, 300], [372, 293], [391, 282],
										  [406, 270], [418, 253], [426, 236], [434, 217],
										  [439, 197], [301, 138], [313, 128], [328, 127],
										  [343, 130], [356, 138], [384, 144], [400, 142],
										  [417, 145], [431, 156], [437, 171], [365, 160],
										  [362, 174], [359, 188], [357, 202], [337, 211],
										  [344, 215], [352, 219], [360, 219], [368, 218],
										  [312, 152], [322, 147], [332, 149], [340, 159],
										  [330, 159], [320, 157], [385, 169], [396, 164],
										  [407, 167], [414, 176], [405, 177], [394, 174],
										  [317, 242], [329, 237], [341, 235], [347, 238],
										  [356, 237], [364, 243], [371, 253], [361, 258],
										  [351, 259], [343, 259], [335, 257], [326, 251],
										  [322, 242], [339, 243], [346, 245], [354, 245],
										  [366, 251], [353, 247], [345, 247], [338, 245]])

			# 计算旋转矩阵
			M = transformation_from_points(src_landmark5, dst_landmark5)
			# 旋转图像
			align_image = cv2.warpAffine(image_numpy, M[:2], (w, h))

			# M = cv2.getPerspectiveTransform(np.asarray(src_landmark5, np.float32),
			#                                 np.asarray(dst_landmark5, np.float32))
			# align_image = cv2.warpPerspective(image_numpy, M, (w, h))
			return align_image, M
		==========================================
    3 输出
        3.1 输出的图像就是垂直的人脸图像

  

posted @ 2019-12-28 21:13  dangxusheng  阅读(1625)  评论(1编辑  收藏  举报