本节主要内容有:
- 静态人像数据
- 静态人像分割数据
- 静态人像数据保存
静态人像数据
在WWDC18上,也就是iOS 12上,苹果继景深模式的基础上推出了人像功能,并开放了人像的相关API。目前人像数据只能静态捕获,无法同景深那样做到实时捕获。
首先我们聊一下人像API的一些注意:
- 系统版本需要iOS 12以上(包含iOS 12),设备限制目前需要在iPhone X以上(包含iPhone X)
- 需要摄像头的参与,不要求一定是景深摄像头,但保险在有可用的景深摄像头时先用景深摄像头
- 只能作静态图像捕获
- 需要人的参与,无人不成图
- 结果是一张掩码图,可以理解成灰度图
静态人像的捕获同静态景深的捕获一样so easy,AVCapturePhotoOutput在iOS 12的时候封装了一些新的关于人像的属性,如一下图所示:
- 首先需要判断需要根据AVCapturePhotoOutput的isDepthDataDeliverySupported和isPortraitEffectsMatteDeliverySupported判断设备是否开启景深和人像模式,设置AVCapturePhotoOutput的isDepthDataDeliveryEnabled和isPortraitEffectsMatteDeliveryEnabled;
- 上一步建议在session.addOutput(output)后调用,否则isPortraitEffectsMatteDeliverySupported可能会一直为false。
- 接着在capture的时候设置AVCapturePhotoSettings的isDepthDataDeliveryEnabled和isPortraitEffectsMatteDeliveryEnabled;
- 在photoOutput(_:didFinishProcessingPhoto:error:)中获取人像数据;
对于Capture时启动捕获人像数据这样的操作,前面已经描述了很多类似的场景了,这里就不再做过多的描述。我们着重了解一下人像数据的获取,示例代码如下:
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
// 获取人像封装数据
if var portraitEffectsMatte = photo.portraitEffectsMatte {
// 根据原图的成像方向
if let orientation = photo.metadata[ String(kCGImagePropertyOrientation) ] as? UInt32 {
// 调整人像掩码图方向
portraitEffectsMatte = portraitEffectsMatte.applyingExifOrientation(CGImagePropertyOrientation(rawValue: orientation)!)
}
// 获取Buffer
let portraitEffectsMattePixelBuffer = portraitEffectsMatte.mattingImage
}
}
通过如上操作,我们便可以拿到人像掩码图数据。
捣鼓过景深图的读者可能会有这样一个疑问,当使用前置景深拍照的时候,也可以拿到类似的人像数据,那么这次新的人像API获取的人像数据,相比景深获取的人像数据,有什么优势?我们请看:
我们可以这得出几个结论:
- 人像模式的分辨率远远高于景深,细节保留更加丰富,这多亏了芯片的机器学习;
- 人像数据由于是机器学习的产物,也解决了部分不支持景深的设备也能享受人像功能。
静态人像分割数据
在iOS 13,苹果又给人像模式带来了新的功能——人像分割,与iOS 12相比,除了人像掩码数据之外,还新增了肤色区域、头发区域、牙齿区域掩码图(最后一张黑的原因是因为女主没露牙)。 人像分割API也有一定的使用限制,同人像API使用限制类似:
- 系统版本需要iOS 13以上(包含iOS 13),设备限制目前需要在iPhone XR以上(包含iPhone XR)
- 需要摄像头的参与,不要求一定是景深摄像头,但保险在有可用的景深摄像头时先用景深摄像头
- 只能作静态图像捕获
- 需要人的参与,无人不成图
- 结果可以获取多张掩码图
那么如何开启这功能?代码示例如下:
// 设置捕获使支持人像分割的类型,目前只支持皮肤、头发、牙齿的分割
self.photoOutput.enabledSemanticSegmentationMatteTypes = self.photoOutput.availableSemanticSegmentationMatteTypes
// 设置AVCapturePhotoSettings
captureSetting.enabledSemanticSegmentationMatteTypes = self.photoOutput.enabledSemanticSegmentationMatteTypes
最后在photoOutput(_:didFinishProcessingPhoto:error:)中获取人像分割数据:
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?){
for ssmType in output.enabledSemanticSegmentationMatteTypes {
if let segmentationMatte = photo.semanticSegmentationMatte(for: ssmType) {
let pixelBuffer = segmentationMatte.mattingImage
}
}
}
静态人像数据保存
静态人像数据和景深数据一样作为图片辅助信息,基本上存储方式和景深类似,我这里就以人像为例,至于皮肤、头发等,也就只是在保存的时候更换一下保存的特征类型:
guard let destination = CGImageDestinationCreateWithData(incrementData, "public.jpeg" as CFString, 1, nil) else { return }
CGImageDestinationAddImage(destination, cgImage, metaDataDic as CFDictionary)
// 获取人像的专属类型
var portraitAuxDataType :NSString?
let portraitAuxData = portraitEffectMatte.dictionaryRepresentation(forAuxiliaryDataType: &portraitAuxDataType)
// 写入到原图
CGImageDestinationAddAuxiliaryDataInfo(destination, portraitAuxDataType!, portraitAuxData! as CFDictionary)
结语
关于人像的知识暂时总结到这里,由于我们有了前面景深的知识,人像的使用相对容易很多,而且也没有太多复杂的情景。后面笔者还是会继续追踪人像相关的新技术,继续维护和更新文档。若文章有什么描述不准确和不恰当,欢迎指正。