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

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

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

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

答案肯定是确认的。

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

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

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

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

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

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

有两种方法:

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。

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

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

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据小魔方

图表案例|纵向折线图

今天要跟大家分享的是纵向折线图! 本例中要展示的是纵向折线图的制作技巧! 在excel中折线图、散点图等图表类型是没有办法直接做成纵向的那种的(就像是柱形图和条...

42150
来自专栏CDA数据分析师

AI可能真的要代替插画师了……

事先声明,这篇文章的标题绝不是在耸人听闻。事情的起因是今天早上在朋友圈看到同学在转发一篇论文,名字叫《Create Anime Characters with ...

285100
来自专栏吉浦迅科技

如何在Jetson TX2上用Python捕获摄像头影像,并用Caffe进行推理

本文转载自JK Jung的帖子:https://jkjung-avt.github.io/tx2-camera-caffe/ 如果有侵犯到贴主利益,请立刻跟我联...

69850
来自专栏AI科技评论

开发 | 不用写代码就能实现深度学习?手把手教你用英伟达 DIGITS 解决图像分类问题

引言 2006年,机器学习界泰斗Hinton,在Science上发表了一篇使用深度神经网络进行维数约简的论文 ,自此,神经网络再次走进人们的视野,进而引发了...

47990
来自专栏数据小魔方

think-cell chart系列11——散点图

今天跟大家分享think-cell chart系列第11篇——散点图。 散点图也属于日常应用的高频图表,那么在think-cell chart中该如何制作呢……...

84350
来自专栏量子位

前端利器!让AI根据手绘原型生成HTML | 教程+代码

这么多环节,任何地方出一点问题,都会拉长开发周期。因此,不少公司,比如Airbnb已经开始用机器学习来提高这个过程的效率。

31130
来自专栏机器学习算法与Python学习

KNN算法实战-改进约会网站配对效果

kNN实战之改进约会网站配对效果 引言 简单的说,KNN算法采用测量不同特征值之间的距离方法进行分类。工作原理:存在一个样本数据集,即训练数据集,并且样本集中每...

603100
来自专栏hrscy

GPU 图形绘制管线

图形绘制管线描述 GPU 渲染流程,即"给定视点、三维物体、光源、照明模式和纹理等元素,如何绘制一幅二维图像"。

14240
来自专栏人工智能头条

AI 可能真的要代替插画师了……

15530
来自专栏编程

基于micropython的数字滤波器

刚参加了昨天的硕士研究生考试,专业课考的信号与系统,报考学校今年出题出的有点偏,不是题型偏而是考察知识点有明显的偏重,简单说考纲里所罗列的考点最多只考了百分之三...

37460

扫码关注云+社区

领取腾讯云代金券