Face++人体抠像API V1版本图片返回结果为灰度图,但是并不是可以直接用的结果,我们需要的是保留人体部分的像素数据,背景根据需要可以自由替换的。让我们来看看如何才能还原。
原理:
一、V1版本返回值可还原成灰度图,颜色只有黑白灰三种颜色,颜色越白,表明是人体部分的概率(也叫置信度)越大,颜色越黑,不是人体部分的概率越大,灰色的部分既有可能是人体,也有可能不是。需要判断其概率即置信度。
二、灰度图上每个像素点的颜色值计算过程为
R=G=B=255*⼈体置信度
所以,人体置信度=R/255=G/255=B/255
所以,问题的关键就在于,获取灰度图每个点的像素值(RGB值),这样可计算出每个像素点的人体置信度。
三、基于置信度可知每个像素点为人体的概率,设置一个阈值,大于该阈值,我们认为该像素点是人体的一部分,应该保留,小于该阈值的像素点应该被置为背景色。
步骤一:按文档要求传参数
HumanBody Segment API(V1):https://console.faceplusplus.com.cn/documents/10071567
需要上传的参数有: api_key 、api_secret 、image_url (或者image_file 或者image_base64),请求方式为POST请求方式,其中如果图片参数是image_file,那么需要用 [post multipart / form-data ]的方式上传。
步骤二:获取人体抠像API接口返回值
请求的返回结果格式如下,其中 result 即是我们需要的数据,这个数据实际上是一个base64编码后的字符串,需要我们逆向还原出图片 [原理为:图⽚ -> ⼆进制 -> base64编码的逆过程],还原出来的图片效果如上为灰度图:
{
"image_id": "7OO7N1dYiJjszvV38oKVpw==",
"result": "/9j/2wCEAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYn...",
"request_id": "1470378968,c6f50ec6-49bd-4838-9923-11db04c40f8d",
"time_used": 573
}
步骤三:处理图片,获取bitmap数据,得到RGB值、置信度
基本每种语言都有获取bitmap数据的方法,这里以iOS为例(其他语言请自行搜索关键字“语⾔ + 获取图⽚bitmap数据”)
UInt32 * inputPixels;
CGImageRef inputCGImage = [self CGImage];//self 是UIImage对象
NSUInteger inputWidth = CGImageGetWidth(inputCGImage);
NSUInteger inputHeight = CGImageGetHeight(inputCGImage);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
NSUInteger bytesPerPixel = 4;
NSUInteger bitsPerComponent = 8;
NSUInteger inputBytesPerRow = bytesPerPixel * inputWidth;
inputPixels = (UInt32 *)calloc(inputHeight * inputWidth, sizeof(UInt32));
CGContextRef context = CGBitmapContextCreate(inputPixels, inputWidth, inputHeight,
bitsPerComponent, inputBytesPerRow, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGContextDrawImage(context, CGRectMake(0, 0, inputWidth, inputHeight), inputCGImage);
至此我们就拿到了bitmap数据,数据存放在 inputPixels 里面,其存储格式为 RGBARGBARGBA....RGBA,其中每个单元(一个单元对应图片上的一个像素点,一个像素点包含 RGBA值)RGBA共占4个字节,R,G,B,A分别占1个字节,我们只需要取出每个像素点的RGB值即可,例如:获取第2个点的RGB值:
#define R(x) ( Mask8(x) )
#define G(x) ( Mask8(x >> 8 ) )
#define B(x) ( Mask8(x >> 16) )
#define A(x) ( Mask8(x >> 24) )
UInt32 inputColor = *inputPixel[1]; //从0开始,1表示第2个点
UInt32 Red = R(inputColor);
UInt32 Green = G(inputColor);
UInt32 Blue = B(inputColor);
获取到RGB值后,我们就可以拿到置信度了:
CGFloat confidence = Red / 255.0;
步骤四:基于置信度(confidence)更换背景
根据置信度我们就可以知道,每个像素点是否是人体的概率,然后我们可以遍历每一个像素点,如果是人体那么就保留这个像素点,否则设置颜色为背景色。例如某个像素点的置信度是0.96,那么我们认为这个点是人体的一部分,应该保留; 如果某个像素点的置信度是0.5,该如何判断呢?那就是设置一个阈值,大于该阈值,则认为该像素点是人体的一部分,应该保留,小于等于该阈值的像素点应该被置为背景色。原图也就还原出来了。
设置阈值的方法带来一个问题,边缘太明显,过度不自然,引入一个经典公式,进行再处理,可使人体边缘部分过度的更加平滑。
/* newR 最终像素点的Red值
* originR 原来像素点的Red值
* backgroundR 背景色的Red值
* alpha 是透明度
*/
newR = originR * alpha + backgroundR *(1 - alpha)
newG = originG * alpha + backgroundG *(1 - alpha)
newB = originB * alpha + backgroundB *(1 - alpha)
Face++人体抠像最新版为V2版本,V2版本可以返回一张已经抠好的图片(返回值中的 body_image 字段),其背景色为透明色。用户可以基于该图片直接做后期处理。同时为了兼容V1版本,V1版本返回的结果,V2同样也会返回(返回值中的 result 字段)。具体还原处理方法如上所述。