AV Foundation人像模式

1,831 阅读4分钟

本节主要内容有:

  1. 静态人像数据
  2. 静态人像分割数据
  3. 静态人像数据保存

静态人像数据

在WWDC18上,也就是iOS 12上,苹果继景深模式的基础上推出了人像功能,并开放了人像的相关API。目前人像数据只能静态捕获,无法同景深那样做到实时捕获。

首先我们聊一下人像API的一些注意:

  • 系统版本需要iOS 12以上(包含iOS 12),设备限制目前需要在iPhone X以上(包含iPhone X)
  • 需要摄像头的参与,不要求一定是景深摄像头,但保险在有可用的景深摄像头时先用景深摄像头
  • 只能作静态图像捕获
  • 需要人的参与,无人不成图
  • 结果是一张掩码图,可以理解成灰度图

静态人像的捕获同静态景深的捕获一样so easy,AVCapturePhotoOutput在iOS 12的时候封装了一些新的关于人像的属性,如一下图所示:

截屏2021-01-11 下午4.51.26.png

  • 首先需要判断需要根据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获取的人像数据,相比景深获取的人像数据,有什么优势?我们请看:

截屏2021-01-11 下午4.55.51.png 我们可以这得出几个结论:

  • 人像模式的分辨率远远高于景深,细节保留更加丰富,这多亏了芯片的机器学习;
  • 人像数据由于是机器学习的产物,也解决了部分不支持景深的设备也能享受人像功能。

静态人像分割数据

在iOS 13,苹果又给人像模式带来了新的功能——人像分割,与iOS 12相比,除了人像掩码数据之外,还新增了肤色区域、头发区域、牙齿区域掩码图(最后一张黑的原因是因为女主没露牙)。 未命名_meitu_2.jpg 人像分割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)

结语

关于人像的知识暂时总结到这里,由于我们有了前面景深的知识,人像的使用相对容易很多,而且也没有太多复杂的情景。后面笔者还是会继续追踪人像相关的新技术,继续维护和更新文档。若文章有什么描述不准确和不恰当,欢迎指正。