人脸姿态校正算法 附完整C++示例代码

内容预览:
  • 为了节约时间,直接复用《自动红眼移除算法 附c++完整代码》的代码~
  • 原图: 红眼修复+倾斜矫正: 项目地址: https://github.com/cpuimage/MTC...~
  •   若有其他相关问题或者需求也可以邮件联系俺探讨~

在一些特殊情况下,经常需要依据图像中的人脸,对图片进行倾斜矫正。

 

例如拍照角度幅度过大之类的情况,而进行人工矫正确实很叫人头大。

那是不是可以有一种算法,可以根据人脸的信息对图片进行角度的修复呢?

答案肯定是确认的。

 

再次例如,想要通过人脸的特征对人物的表情和情绪进行精准判断,

那么这个时候如果能确保人脸没有发现严重倾斜,无疑对准确率判断有一定的帮助。

 

那么假如一张图片只有一个人脸,其实很好判断,通过眼睛的位置的坐标,根据两眼的直线角度,

就可以计算出修正的角度。

然后旋转图片到对应角度即可。

但是如果,一张图片存在多张人脸的时候该怎么办?

有两种方法:

1.找到最大的那个人脸,以它为基准

2.找到频次最高的人脸角度,以频次为基准

当然在大多数情况,方法1是比较合理的。

这两个种情况就留给各位看官去实现了。

本人仅仅考虑一张人脸的情况,演示如何实现该功能。

倾斜角度计算的代码如下:

    float diffEyeX = right_eye_x - left_eye_x;
float diffEyeY = right_eye_y - left_eye_y;

float fAngle;
float M_PI = 3.1415926535897932384626433832795f;
if (fabs(diffEyeX) < 0.0000001f)
fAngle
= 0.f;
else
fAngle
= atanf(diffEyeY / diffEyeX) * 180.0f / M_PI;

如果看不明白,需要好好补一下高中数学基础。

为了节约时间,直接复用《自动红眼移除算法 附c++完整代码》的代码。

增加函数如下:

void RotateBilinear(unsigned char *sourceData, int width, int height, int Channels, int RowBytes,
unsigned
char *destinationData, int newWidth, int newHeight, float angle, bool keepSize = true,
int fillColorR = 255, int fillColorG = 255, int fillColorB = 255) {
if (sourceData == NULL || destinationData == NULL) return;

float oldXradius = (float) (width - 1) / 2;
float oldYradius = (float) (height - 1) / 2;

float newXradius = (float) (newWidth - 1) / 2;
float newYradius = (float) (newHeight - 1) / 2;

double MPI = 3.14159265358979323846;
double angleRad = -angle * MPI / 180.0;
float angleCos = (float) cos(angleRad);
float angleSin = (float) sin(angleRad);

int srcStride = RowBytes;
int dstOffset = newWidth * Channels - ((Channels == 1) ? newWidth : newWidth * Channels);

unsigned
char fillR = fillColorR;
unsigned
char fillG = fillColorG;
unsigned
char fillB = fillColorB;

unsigned
char *src = (unsigned char *) sourceData;
unsigned
char *dst = (unsigned char *) destinationData;

int ymax = height - 1;
int xmax = width - 1;
if (Channels == 1) {
float cy = -newYradius;
for (int y = 0; y < newHeight; y++) {
float tx = angleSin * cy + oldXradius;
float ty = angleCos * cy + oldYradius;

float cx = -newXradius;
for (int x = 0; x < newWidth; x++, dst++) {
float ox = tx + angleCos * cx;
float oy = ty - angleSin * cx;

int ox1 = (int) ox;
int oy1 = (int) oy;

if ((ox1 < 0) || (oy1 < 0) || (ox1 >= width) || (oy1 >= height)) {
*dst = fillG;
}
else {
int ox2 = (ox1 == xmax) ? ox1 : ox1 + 1;
int oy2 = (oy1 == ymax) ? oy1 : oy1 + 1;
float dx1 = 0;
if ((dx1 = ox - (float) ox1) < 0)
dx1
= 0;
float dx2 = 1.0f - dx1;
float dy1 = 0;
if ((dy1 = oy - (float) oy1) < 0)
dy1
= 0;
float dy2 = 1.0f - dy1;

unsigned
char *p1 = src + oy1 * srcStride;
unsigned
char *p2 = src + oy2 * srcStride;

*dst = (unsigned char) (dy2 * (dx2 * p1[ox1] + dx1 * p1[ox2]) +
dy1
* (dx2 * p2[ox1] + dx1 * p2[ox2]));
}
cx
++;
}
cy
++;
dst
+= dstOffset;
}
}
else if (Channels == 3) {
float cy = -newYradius;
for (int y = 0; y < newHeight; y++) {
float tx = angleSin * cy + oldXradius;
float ty = angleCos * cy + oldYradius;

float cx = -newXradius;
for (int x = 0; x < newWidth; x++, dst += Channels) {
float ox = tx + angleCos * cx;
float oy = ty - angleSin * cx;

int ox1 = (int) ox;
int oy1 = (int) oy;

if ((ox1 < 0) || (oy1 < 0) || (ox1 >= width) || (oy1 >= height)) {
dst[
0] = fillR;
dst[
1] = fillG;
dst[
2] = fillB;
}
else {
int ox2 = (ox1 == xmax) ? ox1 : ox1 + 1;
int oy2 = (oy1 == ymax) ? oy1 : oy1 + 1;

float dx1 = 0;
if ((dx1 = ox - (float) ox1) < 0)
dx1
= 0;
float dx2 = 1.0f - dx1;
float dy1 = 0;
if ((dy1 = oy - (float) oy1) < 0)
dy1
= 0;
float dy2 = 1.0f - dy1;

unsigned
char *p1 = src + oy1 * srcStride;
unsigned
char *p2 = p1;
p1
+= ox1 * Channels;
p2
+= ox2 * Channels;

unsigned
char *p3 = src + oy2 * srcStride;
unsigned
char *p4 = p3;
p3
+= ox1 * Channels;
p4
+= ox2 * Channels;

dst[
0] = (unsigned char) (
dy2
* (dx2 * p1[0] + dx1 * p2[0]) +
dy1
* (dx2 * p3[0] + dx1 * p4[0]));

dst[
1] = (unsigned char) (
dy2
* (dx2 * p1[1] + dx1 * p2[1]) +
dy1
* (dx2 * p3[1] + dx1 * p4[1]));

dst[
2] = (unsigned char) (
dy2
* (dx2 * p1[2] + dx1 * p2[2]) +
dy1
* (dx2 * p3[2] + dx1 * p4[2]));
}
cx
++;
}
cy
++;
dst
+= dstOffset;
}
}
else if (Channels == 4) {
float cy = -newYradius;
for (int y = 0; y < newHeight; y++) {
float tx = angleSin * cy + oldXradius;
float ty = angleCos * cy + oldYradius;

float cx = -newXradius;
for (int x = 0; x < newWidth; x++, dst += Channels) {
float ox = tx + angleCos * cx;
float oy = ty - angleSin * cx;

int ox1 = (int) ox;
int oy1 = (int) oy;

if ((ox1 < 0) || (oy1 < 0) || (ox1 >= width) || (oy1 >= height)) {
dst[
0] = fillR;
dst[
1] = fillG;
dst[
2] = fillB;
dst[
3] = 255;
}
else {
int ox2 = (ox1 == xmax) ? ox1 : ox1 + 1;
int oy2 = (oy1 == ymax) ? oy1 : oy1 + 1;

float dx1 = 0;
if ((dx1 = ox - (float) ox1) < 0)
dx1
= 0;
float dx2 = 1.0f - dx1;
float dy1 = 0;
if ((dy1 = oy - (float) oy1) < 0)
dy1
= 0;
float dy2 = 1.0f - dy1;

unsigned
char *p1 = src + oy1 * srcStride;
unsigned
char *p2 = p1;
p1
+= ox1 * Channels;
p2
+= ox2 * Channels;

unsigned
char *p3 = src + oy2 * srcStride;
unsigned
char *p4 = p3;
p3
+= ox1 * Channels;
p4
+= ox2 * Channels;

dst[
0] = (unsigned char) (
dy2
* (dx2 * p1[0] + dx1 * p2[0]) +
dy1
* (dx2 * p3[0] + dx1 * p4[0]));

dst[
1] = (unsigned char) (
dy2
* (dx2 * p1[1] + dx1 * p2[1]) +
dy1
* (dx2 * p3[1] + dx1 * p4[1]));

dst[
2] = (unsigned char) (
dy2
* (dx2 * p1[2] + dx1 * p2[2]) +
dy1
* (dx2 * p3[2] + dx1 * p4[2]));
dst[
3] = 255;
}
cx
++;
}
cy
++;
dst
+= dstOffset;
}
}
}

void facialPoseCorrection(unsigned char *inputImage, int Width, int Height, int Channels, int left_eye_x, int left_eye_y,
int right_eye_x, int right_eye_y) {
float diffEyeX = right_eye_x - left_eye_x;
float diffEyeY = right_eye_y - left_eye_y;

float fAngle;
float M_PI = 3.1415926535897932384626433832795f;
if (fabs(diffEyeX) < 0.0000001f)
fAngle
= 0.f;
else
fAngle
= atanf(diffEyeY / diffEyeX) * 180.0f / M_PI;
size_t numberOfPixels
= Width * Height * Channels * sizeof(unsigned char);
unsigned
char *outputImage = (unsigned char *) malloc(numberOfPixels);
if (outputImage != nullptr) {
RotateBilinear(inputImage, Width, Height, Channels, Width
* Channels, outputImage, Width, Height, fAngle);
memcpy(inputImage, outputImage, numberOfPixels);
free(outputImage);
}
}

上效果图片。

原图:

红眼修复+倾斜矫正:

项目地址:

https://github.com/cpuimage/MTCNN

命令行参数:

mtcnn 模型文件路径 图片路径

例如: mtcnn ../models ../sample.jpg

 

用cmake即可进行编译示例代码,详情见CMakeLists.txt。

 

若有其他相关问题或者需求也可以邮件联系俺探讨。

邮箱地址是: 
[email protected]

以上就是:人脸姿态校正算法 附完整C++示例代码 的全部内容。

本站部分内容来源于互联网和用户投稿,如有侵权请联系我们删除,谢谢。
Email:[email protected]


编程语言
0 条回复 A 作者 M 管理员
    所有的伟大,都源于一个勇敢的开始!
欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论