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

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

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

答案肯定是确认的。

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

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

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

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

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

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

有两种方法:

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 = .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 = , int fillColorG = , int fillColorB = ) {
if (sourceData == NULL || destinationData == NULL) return; float oldXradius = (float) (width - ) / ;
float oldYradius = (float) (height - ) / ; float newXradius = (float) (newWidth - ) / ;
float newYradius = (float) (newHeight - ) / ; 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 == ) ? 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 - ;
int xmax = width - ;
if (Channels == ) {
float cy = -newYradius;
for (int y = ; y < newHeight; y++) {
float tx = angleSin * cy + oldXradius;
float ty = angleCos * cy + oldYradius; float cx = -newXradius;
for (int x = ; x < newWidth; x++, dst++) {
float ox = tx + angleCos * cx;
float oy = ty - angleSin * cx; int ox1 = (int) ox;
int oy1 = (int) oy; if ((ox1 < ) || (oy1 < ) || (ox1 >= width) || (oy1 >= height)) {
*dst = fillG;
} else {
int ox2 = (ox1 == xmax) ? ox1 : ox1 + ;
int oy2 = (oy1 == ymax) ? oy1 : oy1 + ;
float dx1 = ;
if ((dx1 = ox - (float) ox1) < )
dx1 = ;
float dx2 = 1.0f - dx1;
float dy1 = ;
if ((dy1 = oy - (float) oy1) < )
dy1 = ;
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 == ) {
float cy = -newYradius;
for (int y = ; y < newHeight; y++) {
float tx = angleSin * cy + oldXradius;
float ty = angleCos * cy + oldYradius; float cx = -newXradius;
for (int x = ; 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 < ) || (oy1 < ) || (ox1 >= width) || (oy1 >= height)) {
dst[] = fillR;
dst[] = fillG;
dst[] = fillB;
} else {
int ox2 = (ox1 == xmax) ? ox1 : ox1 + ;
int oy2 = (oy1 == ymax) ? oy1 : oy1 + ; float dx1 = ;
if ((dx1 = ox - (float) ox1) < )
dx1 = ;
float dx2 = 1.0f - dx1;
float dy1 = ;
if ((dy1 = oy - (float) oy1) < )
dy1 = ;
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[] = (unsigned char) (
dy2 * (dx2 * p1[] + dx1 * p2[]) +
dy1 * (dx2 * p3[] + dx1 * p4[])); dst[] = (unsigned char) (
dy2 * (dx2 * p1[] + dx1 * p2[]) +
dy1 * (dx2 * p3[] + dx1 * p4[])); dst[] = (unsigned char) (
dy2 * (dx2 * p1[] + dx1 * p2[]) +
dy1 * (dx2 * p3[] + dx1 * p4[]));
}
cx++;
}
cy++;
dst += dstOffset;
}
} else if (Channels == ) {
float cy = -newYradius;
for (int y = ; y < newHeight; y++) {
float tx = angleSin * cy + oldXradius;
float ty = angleCos * cy + oldYradius; float cx = -newXradius;
for (int x = ; 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 < ) || (oy1 < ) || (ox1 >= width) || (oy1 >= height)) {
dst[] = fillR;
dst[] = fillG;
dst[] = fillB;
dst[] = ;
} else {
int ox2 = (ox1 == xmax) ? ox1 : ox1 + ;
int oy2 = (oy1 == ymax) ? oy1 : oy1 + ; float dx1 = ;
if ((dx1 = ox - (float) ox1) < )
dx1 = ;
float dx2 = 1.0f - dx1;
float dy1 = ;
if ((dy1 = oy - (float) oy1) < )
dy1 = ;
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[] = (unsigned char) (
dy2 * (dx2 * p1[] + dx1 * p2[]) +
dy1 * (dx2 * p3[] + dx1 * p4[])); dst[] = (unsigned char) (
dy2 * (dx2 * p1[] + dx1 * p2[]) +
dy1 * (dx2 * p3[] + dx1 * p4[])); dst[] = (unsigned char) (
dy2 * (dx2 * p1[] + dx1 * p2[]) +
dy1 * (dx2 * p3[] + dx1 * p4[]));
dst[] = ;
}
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 = .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。

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

邮箱地址是: 
gaozhihan@vip.qq.com

人脸姿态校正算法 附完整C++示例代码的更多相关文章

  1. WebRTC 音频采样算法 附完整C++示例代码

    之前有大概介绍了音频采样相关的思路,详情见<简洁明了的插值音频重采样算法例子 (附完整C代码)>. 音频方面的开源项目很多很多. 最知名的莫过于谷歌开源的WebRTC, 其中的音频模块就包 ...

  2. 基于傅里叶变换的音频重采样算法 (附完整c代码)

    前面有提到音频采样算法: WebRTC 音频采样算法 附完整C++示例代码 简洁明了的插值音频重采样算法例子 (附完整C代码) 近段时间有不少朋友给我写过邮件,说了一些他们使用的情况和问题. 坦白讲, ...

  3. 音频降噪算法 附完整C代码

    降噪是音频图像算法中的必不可少的. 目的肯定是让图片或语音 更加自然平滑,简而言之,美化. 图像算法和音频算法 都有其共通点. 图像是偏向 空间 处理,例如图片中的某个区域. 图像很多时候是以二维数据 ...

  4. mser 最大稳定极值区域(文字区域定位)算法 附完整C代码

    mser 的全称:Maximally Stable Extremal Regions 第一次听说这个算法时,是来自当时部门的一个同事, 提及到他的项目用它来做文字区域的定位,对这个算法做了一些优化. ...

  5. 磨皮美颜算法 附完整C代码

    前言 2017年底时候写了这篇<集 降噪 美颜 虚化 增强 为一体的极速图像润色算法 附Demo程序> 这也算是学习过程中比较有成就感的一个算法. 自2015年做算法开始到今天,还有个把月 ...

  6. 基于RNN的音频降噪算法 (附完整C代码)

    前几天无意间看到一个项目rnnoise. 项目地址: https://github.com/xiph/rnnoise 基于RNN的音频降噪算法. 采用的是 GRU/LSTM 模型. 阅读下训练代码,可 ...

  7. 音频自动增益 与 静音检测 算法 附完整C代码

    前面分享过一个算法<音频增益响度分析 ReplayGain 附完整C代码示例> 主要用于评估一定长度音频的音量强度, 而分析之后,很多类似的需求,肯定是做音频增益,提高音量诸如此类做法. ...

  8. 音频自动增益 与 静音检测 算法 附完整C代码【转】

    转自:https://www.cnblogs.com/cpuimage/p/8908551.html 前面分享过一个算法<音频增益响度分析 ReplayGain 附完整C代码示例> 主要用 ...

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

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

随机推荐

  1. (Tomcat)服务器之web应用的虚拟目录映射和主机搭建

    首先来了解一下web的虚拟目录映射和主机搭建的知识 第一:web的虚拟目录映射 首先我们要知道什么叫做web的虚拟目录映射,这个很好理解的,就是将我们本地硬盘上的web应用映射出一个供外界用户访问的地 ...

  2. 小知识点BeanUtils.copyProperties

     通过BeanUtils.copyProperties可以时间拷贝对象中的值,下面的new String[]{"cid","agreeFlag"," ...

  3. Linux/Android多点触摸协议

    链接点击打开链接 关于Linux多点触摸协议大家可以参考kernel中的文档:https://www.kernel.org/doc/Documentation/input/multi-touch-pr ...

  4. ios入门OC_UI晋级学什么?

    1. OC 语法初步, 你可能学到面向对象最近本的概念, 并且可以大致的建立几个自以为是的类,但这仅仅是开始. 你知道为什么面向对象要有3大特性么.知道他们是用到什么设计模式的么 2. 你可能学到了N ...

  5. (NO.00002)iOS游戏精灵战争雏形(五)

    完成了精灵自己移动之后,我们开始着手实现按住精灵拖动的功能. 要想处理触摸事件,我们需要做2件事: 1.在类的初始化中打开触摸接收属性 2.实现触摸处理回调方法 我们依次来做这2件事. 首先要搞清楚在 ...

  6. JAVA中IO流总结

    本文是在学习中的总结,欢迎转载但请注明出处:http://blog.csdn.net/pistolove/article/details/42119261 我想你对JAVA的IO流有所了解,平时使用的 ...

  7. SpringMVC提供两种校验机制

    本文不讲如何使用SpringMVC提供的两种校验机制,只是简单的说明一下其中的差别而已: 1.创建一个Bean,在Bean的属性中添加校验信息,通过配置LocalValidatorFactoryBea ...

  8. 大型服装集团BI决策系统的分析主题模块

    一般BI商业智能解决方案都包含财务.销售.客户等分析模块,本文分享的是某大型服装集团通过帆软FineBI建设的BI决策系统.该决策系统主要针对财务.资金.采购.生产.库存.物流.销售.渠道.产品.客户 ...

  9. gcc学习(一)[第二版]

    gcc简介 1. gcc是GNU Compiler Collection的缩写.最初是作为C语言的编译器(GNU C  Compiler),作者为Richard Stallman,是GNU项目的奠基者 ...

  10. 百度地图android studio导入开发插件

    百度地图SDK v3.5.0开发包下载地址:http://lbsyun.baidu.com/sdk/download?selected=location 开发工具 Android开发工具很多,在这我们 ...