Jun
22
WWDC20221讲到iOS15在相机部分新增了不少识别相关的功能,一种有一项就是人物聚焦——对应背景模糊。同时,我们排查SDK变化时,注意到CIFilter新增了一个personSegmentationFilter,看起来就是对应这个功能的实现了。不过翻阅文档https://developer.apple.com/documentation/coreimage/cifilter/3750390-personsegmentationfilter?language=objc,苹果真是啥都没有讲。尝试了之后,我得到了能实现人物聚焦(背景虚化)的效果的CIFilter使用方式。
过程
首先提供的原图是如下,刚才随手拍的同事:
1. 第一步即通过personSegmentationFilter处理得到识别图像,并缩放到和原图相同的大小。
这里用了两个CIFilter:
a. 其中第一个即personSegmentationFilter,它有一个参数是qualityLevel,没有文档,尝试后发现这个参数控制的识别精度,而识别精度决定了输出的识别图像的分辨率,一张普通的相册图片,按512这样的值去识别甚至能得到1w+像素的结果,当然这个值也影响到识别速度了。多次尝试后,128应该是一个精度足够高的合适的值。
b. 既然第一步说personSegmentationFilter丢出来的图像尺寸是质量决定的,因此补充一个lanczosScaleTransformFilter,把输出的识别图像resize到源氏图像大小。
结果得到:
2. 第二步,通过maskedVariableBlurFilter,对非人物区域进行模糊。这个filter可以根据一个mask入参来决定模糊的区域与大小,文档:https://developer.apple.com/library/archive/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html#//apple_ref/doc/filter/ci/CIMaskedVariableBlur。
因此涉及到2步:mask生成与模糊执行。
a. 根据如上文档,mask是一个灰度图,其中黑色代表不进行模糊,白色是100%模糊。这里使用一个简单的CIKernel来实现personSegmentationFilter转换得到mask:
其伪shader逻辑其实就是把红色图转换为反转的红色值的灰度图即可。
b.然后执行maskedVariableBlurFilter即可
3. 第三步有一定后期处理。问题包括:a. maskedVariableBlurFilter会得到一张区域拓展的带模糊延展区域的图像,需要裁减,另外模糊会导致图片边缘半透明。
因此todo明确了:在图像下面垫上原始图像,再裁减到原始图像大小即可:
这样就得到最终的效果图了。
完整流水线为:
实际效果判断
1. 经过多次各种网络图片与自己拍摄的图片测试,只要分辨率足够(人物大小在500px+),有相当高的识别率。不过人物身上的物品识别还不够准确,比如上图同事的眼镜被裁减掉了。而且配合模糊效果,由于识别边界过于清晰,反倒是不自然,有可能需要对mask再做一次模糊。
2. 虽然是gpu执行的,如上整个过程性能还是比较慢,肯定不建议实时使用。目前多次测速,每次这个流水线执行时间约0.045s左右,再包括流的输入输出处理,肯定远超过0.05s了,也就是说如果实时使用,fps至少低于20,体验还是比较差的。
过程
首先提供的原图是如下,刚才随手拍的同事:
1. 第一步即通过personSegmentationFilter处理得到识别图像,并缩放到和原图相同的大小。
CIFilter<CIPersonSegmentation> *personFilter = [CIFilter personSegmentationFilter];
personFilter.inputImage = sourceImage;
personFilter.qualityLevel = 128;
CIFilter<CILanczosScaleTransform> *personResizeFilter = [CIFilter lanczosScaleTransformFilter];
personResizeFilter.inputImage = personFilter.outputImage;
personResizeFilter.scale = sourceImage.extent.size.height / personResizeFilter.inputImage.extent.size.height;
personResizeFilter.aspectRatio = sourceImage.extent.size.width / (personResizeFilter.inputImage.extent.size.width * personResizeFilter.scale);
CIImage *personResizeImg = personResizeFilter.outputImage;
这里用了两个CIFilter:
a. 其中第一个即personSegmentationFilter,它有一个参数是qualityLevel,没有文档,尝试后发现这个参数控制的识别精度,而识别精度决定了输出的识别图像的分辨率,一张普通的相册图片,按512这样的值去识别甚至能得到1w+像素的结果,当然这个值也影响到识别速度了。多次尝试后,128应该是一个精度足够高的合适的值。
b. 既然第一步说personSegmentationFilter丢出来的图像尺寸是质量决定的,因此补充一个lanczosScaleTransformFilter,把输出的识别图像resize到源氏图像大小。
结果得到:
2. 第二步,通过maskedVariableBlurFilter,对非人物区域进行模糊。这个filter可以根据一个mask入参来决定模糊的区域与大小,文档:https://developer.apple.com/library/archive/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html#//apple_ref/doc/filter/ci/CIMaskedVariableBlur。
因此涉及到2步:mask生成与模糊执行。
a. 根据如上文档,mask是一个灰度图,其中黑色代表不进行模糊,白色是100%模糊。这里使用一个简单的CIKernel来实现personSegmentationFilter转换得到mask:
NSString *maskKernelString =
@"kernel vec4 color( __sample s ) { \n"
@" float r = 1.0 - s.r; \n"
@" return vec4(r, r, r, 1.0); \n"
@"}";
CIKernel *maskKernel = [CIKernel kernelWithString:maskKernelString];
CIImage *maskImage = [maskKernel applyWithExtent:personResizeImg.extent roiCallback:^CGRect(int index, CGRect rect) {
return personResizeImg.extent;
} arguments:@[personResizeImg]];
其伪shader逻辑其实就是把红色图转换为反转的红色值的灰度图即可。
b.然后执行maskedVariableBlurFilter即可
CIFilter<CIMaskedVariableBlur> *peopleImgFilter = [CIFilter maskedVariableBlurFilter];
peopleImgFilter.inputImage = ciImage;
peopleImgFilter.mask = maskImage;
peopleImgFilter.radius = 10.0;
3. 第三步有一定后期处理。问题包括:a. maskedVariableBlurFilter会得到一张区域拓展的带模糊延展区域的图像,需要裁减,另外模糊会导致图片边缘半透明。
因此todo明确了:在图像下面垫上原始图像,再裁减到原始图像大小即可:
CIFilter<CICompositeOperation> *peopleAddedFilter = [CIFilter sourceOverCompositingFilter];
peopleAddedFilter.inputImage = peopleImgFilter.outputImage;
peopleAddedFilter.backgroundImage = sourceImage;
CIImage *peopleBlurImg = [peopleAddedFilter.outputImage imageByCroppingToRect:CGRectMake(0, 0, sourceImage.extent.size.width, sourceImage.extent.size.height)];
这样就得到最终的效果图了。
完整流水线为:
实际效果判断
1. 经过多次各种网络图片与自己拍摄的图片测试,只要分辨率足够(人物大小在500px+),有相当高的识别率。不过人物身上的物品识别还不够准确,比如上图同事的眼镜被裁减掉了。而且配合模糊效果,由于识别边界过于清晰,反倒是不自然,有可能需要对mask再做一次模糊。
2. 虽然是gpu执行的,如上整个过程性能还是比较慢,肯定不建议实时使用。目前多次测速,每次这个流水线执行时间约0.045s左右,再包括流的输入输出处理,肯定远超过0.05s了,也就是说如果实时使用,fps至少低于20,体验还是比较差的。