萧邦之殇

If not me who, If not now when

Face++人脸检测与识别初试

项目背景:

最近被人民日报换军装的H5刷爆朋友圈,于是有汽车厂商想做类似的项目。用于旅游照片换脸分享朋友圈,植入汽车公众号进行宣传。

可是之前没有类似的项目经验,于是搜集相关代码和当前人脸识别市场进行调研。主要有以下几大阵营:

  1. Face++https://www.faceplusplus.com.cn/
  2. 腾讯优图和天天P图。天天P图和腾讯优图是不同部门负责http://open.youtu.qq.com/welcome/experience
  3. 百度AI开发平台http://ai.baidu.com/tech/face
  4. 其他阵营,如美图秀秀等

人民日报换军装的H5,就是由天天P图公司制作的。

对比API和技术支持

Face++、腾迅优图API以及百度AI平台图像处理方面产品线都比较全,都在进行产品线更新。

  1. 首先联系的Face++, Face++不支持换军装定制开发。
  2. 然后联系腾讯优图,说会转个天天P图的同事,然后一直没有回音。
  3. 最后在腾讯优图找到了换军装的Demo,里面找到了作者联系方式,报价10+万。
  4. 没有联系百度公司。

估计成本太高,客户后面就没有了反馈。
等待总是漫长的,作为技术总是爱折腾和尝试。以下是进行的折腾之旅:

思路一 Face++背景融合:

用户上传个人全身照,选择场景进行图片叠加处理。

1. 注册开发者获取api_key和api_secret

进行接口权限认证和数据请求需要用到。

2. 上传原始图片

facetest2

3. 获取灰度图

发送原始图片,获取返回的灰度图。

var params = $('form').serializeArray();
$.ajax({
    url: 'https://api-cn.faceplusplus.com/humanbodypp/beta/segment',
    type: 'POST',
    data: params,
    dataType: 'json',
    success: function(data){
        console.log(data);
        var grayImg =  'data:image/jpeg;base64,'+data.result;
        $('#resultImg').attr('src', grayImg);
        $('#addedImg').attr('src', clipImgWithGrayImg( grayImg));
    },
    error: function (msg) {
        console.log(msg);
    }
});

调用接口返回二进制数据流,增加data:image/jpeg;base64,前缀,即可使用img读取图片。

灰度图

4. 裁剪原始图片与背景图片融合

根据获取的灰度图,使用canvas对原图进行像素级操作,裁剪原图。针对非人像部分,进行像素透明操作。可以进行对边界部分进行参数调节操作,如下:

<!-- 已知灰度图裁切原始图片 -->
function clipImgWithGrayImg(grayImg) {
    var canvas = document.getElementById("myCanvas");
    var image = document.getElementById("resultImg");
    image.crossOrigin="Anonymous";

    <!-- 将得到的图像绘制在Canvas对象中的代码如下:-->
        canvas.width=image.width;
    canvas.height=image.height;
    var ctx=canvas.getContext("2d");
    ctx.drawImage(image, 0, 0);
    <!-- 从Canvas对象中获取图像像素数据的代码如下:-->

        var canvasData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    <!-- 读取像素值与实现灰度计算的代码如下:-->

        for ( var x = 0; x < canvasData.width; x++) {
            for ( var y = 0; y < canvasData.height; y++) {

                // Index of the pixel in the array
                var idx = (x + y * canvasData.width) * 4;
                var r = canvasData.data[idx + 0];
                var g = canvasData.data[idx + 1];
                var b = canvasData.data[idx + 2];

                if (r < 128 || g < 128 || b < 128) {
                    canvasData.data[idx + 3] = 0; // Alpha channel
                }
            }
        }

    var data = canvasData.data;
    ctx.clearRect(0,0, canvas.width, canvas.height);
    var srcImage = document.getElementById("srcImg");
    ctx.drawImage(srcImage, 0, 0);
    srcImage.crossOrigin="Anonymous";

    <!-- 从Canvas对象中获取图像像素数据的代码如下:-->
        var canvasData2 = ctx.getImageData(0, 0, canvas.width, canvas.height);

    for ( var j = 0; j < canvasData2.width; j++) {
        for ( var k = 0; k < canvasData2.height; k++) {
            // Index of the pixel in the array
            var idx = (j + k * canvasData2.width) * 4;
            canvasData2.data[idx + 3] = data[idx + 3];
        }
    }

    ctx.putImageData(canvasData2,0,0);
    return canvas.toDataURL();
}

与背景图合成结果:

图像融合

总结:边界判断还需要优化,不能够满足商用的完美融合需求。

思路二 人脸检测与人脸替换:

使用换脸的正常流程: 人脸检测–>人脸特征提取–>模型脸型匹配–>替换人脸–>肤色更换

1. 获取人脸检测数据

上传图片,调用人脸检测的接口,获取人脸特征数。

var params = $('form').serializeArray();
$.ajax({
    url: 'https://api-cn.faceplusplus.com/facepp/v3/detect', // 人脸检测API
    type: 'POST',
    data: params,
    dataType: 'json',
    crossDomain: true,
    success: function(data){
        console.log(data);
        if (data.faces.length > 0) {
            drawFace(data.faces[0].face_rectangle, data.faces[0].landmark);
        }
    },
    error: function (msg) {
        console.log(msg);
    }
});
2. 识别人脸

调用Face++人脸识别接口,返回的特征可以正常匹配,但是人脸的位置异常。

人脸识别

代码如下:

<!-- 显示面部区域draw face -->
function drawFace(rect, landmark) {
    var canvas = document.getElementById("myCanvas");
    var image = document.getElementById("srcImg");
    image.crossOrigin="Anonymous";
    var points = [
        {'x': landmark.contour_left1.x, 'y':landmark.contour_left1.y},
        {'x': landmark.contour_left2.x, 'y':landmark.contour_left2.y},
        {'x': landmark.contour_left3.x, 'y':landmark.contour_left3.y},
        {'x': landmark.contour_left4.x, 'y':landmark.contour_left4.y},
        {'x': landmark.contour_left5.x, 'y':landmark.contour_left5.y},
        {'x': landmark.contour_left6.x, 'y':landmark.contour_left6.y},
        {'x': landmark.contour_left7.x, 'y':landmark.contour_left7.y},
        {'x': landmark.contour_left8.x, 'y':landmark.contour_left8.y},
        {'x': landmark.contour_left9.x, 'y':landmark.contour_left9.y},
        {'x': landmark.contour_chin.x, 'y':landmark.contour_chin.y},
        {'x': landmark.contour_right9.x, 'y':landmark.contour_right9.y},
        {'x': landmark.contour_right8.x, 'y':landmark.contour_right8.y},
        {'x': landmark.contour_right7.x, 'y':landmark.contour_right7.y},
        {'x': landmark.contour_right6.x, 'y':landmark.contour_right6.y},
        {'x': landmark.contour_right5.x, 'y':landmark.contour_right5.y},
        {'x': landmark.contour_right4.x, 'y':landmark.contour_right4.y},
        {'x': landmark.contour_right3.x, 'y':landmark.contour_right3.y},
        {'x': landmark.contour_right2.x, 'y':landmark.contour_right2.y},
        {'x': landmark.contour_right1.x, 'y':landmark.contour_right1.y},
        {'x': landmark.right_eyebrow_right_corner.x, 'y':landmark.right_eyebrow_right_corner.y},
        {'x': landmark.right_eyebrow_upper_middle.x, 'y':landmark.right_eyebrow_upper_middle.y},
        {'x': landmark.left_eyebrow_upper_middle.x, 'y':landmark.left_eyebrow_upper_middle.y},
        {'x': landmark.left_eyebrow_left_corner.x, 'y':landmark.left_eyebrow_left_corner.y}
    ];

    <!-- 将得到的图像绘制在Canvas对象中的代码如下:-->
        canvas.width=image.width;
    canvas.height=image.height;
    var ctx=canvas.getContext("2d");
    ctx.drawImage(image, 0, 0);
    ctx.beginPath();
    ctx.rect(rect.top, rect.left, rect.width, rect.height);
    $.each(points, function (index, item) {
        if (index === 0) {
            ctx.moveTo(item.x, item.y);
        } else {
            ctx.lineTo(item.x, item.y);
        }
    });
    ctx.closePath();
    ctx.strokeStyle = "green";
    ctx.stroke();
}
3. 人脸替换

待完成

4. 制作滤镜,更换肤色

待完成

总结

换脸H5中,还是有过程需要完成。当前主要涉及Face++的接口调用和Canvas图片处理能力。商用过程,还有很多情况需要考虑,比如人脸位置检测与校验、滤镜换肤。听说腾讯优图1个月左右会开放换脸API,敬请期待。

发表回复

Your email address will not be published. Required fields are marked *.

*
*

鄂ICP备2025138472号-1