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 输出的图像就是垂直的人脸图像
原文:https://www.cnblogs.com/dxscode/p/12099528.html