zoukankan      html  css  js  c++  java
  • 人脸姿态校正算法 附完整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

  • 相关阅读:
    使用 Istio 进行 JWT 身份验证(充当 API 网关)
    DNS 私有域的选择:internal.xxx.com/lan.xxx.com 还是 xxx.local/xxx.srv?
    「Bug」K8s 节点的 IP 地址泄漏,导致 IP 被耗尽
    Linux网络学习笔记(二):域名解析(DNS)——以 CoreDNS 为例
    Linux 发行版的选用(服务器和个人桌面)
    「Bug」VMware 虚拟机的关机测试中,Ubuntu 明显比 CentOS 慢
    VMware vSphere :服务器虚拟化
    「Bug」ubuntu 使用国内 apt 源构建 docker 时提示 hash 不匹配
    留言板
    Idea 自定义快捷代码输入 如syso => System.out.println()
  • 原文地址:https://www.cnblogs.com/cpuimage/p/9011130.html
Copyright © 2011-2022 走看看