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

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

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

```    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;```

```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;

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) {
for (int y = 0; y < newHeight; y++) {
float tx = angleSin * cy + oldXradius;
float ty = angleCos * cy + oldYradius;

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) {
for (int y = 0; y < newHeight; y++) {
float tx = angleSin * cy + oldXradius;
float ty = angleCos * cy + oldYradius;

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) {
for (int y = 0; y < newHeight; y++) {
float tx = angleSin * cy + oldXradius;
float ty = angleCos * cy + oldYradius;

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 模型文件路径 图片路径

0 条评论

• ### 快速均值模糊算法

前段时间在网上看到一个快速均值模糊算法，性能很不错。 源博客： http://www.lellansin.com/super-fast-blur-%E6%A8%...

• ### pytorch 移动端框架 thnets 附c示例代码

前年年前做一个手机移动端图像识别项目的时候， 先后尝试了mxnet,thnets,caffe,tensorflow. 当时的情况是,mxnet内存管理奇差，内存...

• ### 图片文档倾斜矫正算法 附完整c代码

2年前在学习图像算法的时候看到一个文档倾斜矫正的算法。 也就是说能将一些文档图像进行旋转矫正， 当然这个算法一般用于一些文档扫描软件做后处理 或者用于ocr ...

• ### oc 中随机数的用法（arc4random() 、random()、CCRANDOM_0_1()

1)、arc4random() 比较精确不需要生成随即种子        使用方法 ：                  通过arc4random() 获取0到...

• ### 与挖矿斗争的日子，我连 Docker 都删了

接着检查了服务器 CPU 的使用情况，未发现有挖矿程序在运行，CPU 使用率只有 5% 左右。当时笔者存在侥幸的心理，觉得 2C4G 的服务器配置应该不太适合挖...

• ### 一步步搭建基于GTID的MySQL复制

虽然从库可以不需要开启二进制日志功能，这里我们推荐主从库同时开启二进制日志功能，方便主从切换

• ### 《neural network and deep learning》题解——ch03 交叉熵代价函数

http://blog.csdn.net/u011239443/article/details/75091283

• ### 采用 Vue 编写的功能强大的 Swagger-ui 页面

swagger-ui有非常多的版本，觉得不太好用，用postman，每个接口都要自己进行录入。所以在基于think-vuele进行了swagger格式json的...

• ### 采用 Vue 编写的功能强大的 Swagger-ui 页面

swagger-ui有非常多的版本，觉得不太好用，用postman，每个接口都要自己进行录入。所以在基于think-vuele进行了swagger格式json的...

• ### 供应商巴斯夫被窃密，台积电回应无影响

德商巴斯夫在台子公司主要制造高纯度硫酸及氨水等电子级化学品，其中有80%卖给台积电。