iOSで「髪」「肌」「歯」「空」をセグメンテーションする
"Matte"は、背景合成等において前景と背景を分離するために使用する、マスク専用の画像を意味します。
iOS 13以降で「Semantic Segmentation Matte」というものを取得できるようになったのですが、これがどういうものかを知るために、まずはiOS 12からある「Portrait Effect Matte」をおさらいしましょう。
おさらい: Portrait Effect Matte
Portrait Effect Matte(以下PEM)は、iOS 12から取得できるようになった、「人間の全身」のセグメンテーション[1]に特化したマスク画像です。

デュアルカメラやTrueDepthカメラでの撮影時に取得できるデプス(深度)情報だけでなく、機械学習も使用しており、高精細であることが特長です。[2]

髪の毛の部分の再現度も高い
Semantic Segmentation Matteの種類
そしてiOS 13から取得できるようになったSemantic Segmentation Matte(以下SSM)は、人の「髪」「肌」「歯」といった部位をセマンティックセグメンテーションするためのマスクです。

左から、Hair / Skin / Teeth
これらを使って、たとえば髪の色を変える、肌の色を変える、歯を白くする、といったことができます。

SSMを使用して肌の色を変える
Semantic Segmentation Matteの取得方法
PEMの場合は以下のように取得できました。
let info = CGImageSourceCopyAuxiliaryDataInfoAtIndex(
   source, 
   0, 
   kCGImageAuxiliaryDataTypePortraitEffectsMatte
) as? [String : AnyObject]
CGImageSourceオブジェクトから、CGImageSourceCopyAuxiliaryDataInfoAtIndex関数を利用して、kCGImageAuxiliaryDataTypePortraitEffectsMatteを指定してPEMを取得しています。[3]
SSMもほぼPEMと同様の方法でCGImageSourceオブジェクトから取得します。それぞれ次のようなAuxiliaryDataTypeが定義されており、
kCGImageAuxiliaryDataTypeSemanticSegmentationHairMatte
kCGImageAuxiliaryDataTypeSemanticSegmentationSkinMatte
kCGImageAuxiliaryDataTypeSemanticSegmentationTeethMatte
CGImageSourceCopyAuxiliaryDataInfoAtIndexの第3引数でこれらを指定するだけです。
let info = CGImageSourceCopyAuxiliaryDataInfoAtIndex(
   source, 
   0, 
   kCGImageAuxiliaryDataTypeSemanticSegmentationHairMatte   // Hair
) as? [String : AnyObject]
AVSemanticSegmentationMatte
AVFoundationでPEMを扱うクラスとして、AVPortraitEffectsMatteが用意されており、次のように(上述のCGImageSourceCopyAuxiliaryDataInfoAtIndex関数から取得した)Dictionaryを渡して初期化することができました。
let matte = AVPortraitEffectsMatte(fromDictionaryRepresentation: info)
iOS 13ではSSMを扱うクラスとしてAVSemanticSegmentationMatteが追加されました。イニシャライザは次のように定義されています。
convenience init(
    fromImageSourceAuxiliaryDataType imageSourceAuxiliaryDataType: CFString, 
    dictionaryRepresentation imageSourceAuxiliaryDataInfoDictionary: 
        [AnyHashable : Any]) throws
AVPortraitEffectsMatteと同様に、[AnyHashable : Any]型のDictionaryを渡して初期化できるようになっています。
第1引数にimageSourceAuxiliaryDataTypeを渡す点だけが違っていて、kCGImageAuxiliaryDataTypeSemanticSegmentationHairMatte,
〜SkinMatte,
〜TeethMatteのいずれかを渡します。
let skinMatte = AVSemanticSegmentationMatte(
    fromImageSourceAuxiliaryDataType: 
        kCGImageAuxiliaryDataTypeSemanticSegmentationSkinMatte, 
    dictionaryRepresentation: info)
AVSemanticSegmentationMatteもAVPortraitEffectsMatteと同様にmattingImageというPEMのピクセルデータを保持するプロパティを持ち、
var mattingImage: CVPixelBuffer { get }
これがCVPixelBuffer型なので、Core ImageやMetalといった既存のフレームワークを利用してこのピクセルバッファにある画像データを処理することができます。
SSMをCIImage経由で取得する
前述のCGImageSourceからSSMを取得する方法以外に、CIImageとして取得する方法もあります。
CIImageの各種イニシャライザはCIImageOptionを渡せるようになっているので、
init?(contentsOf url: URL, 
options: [CIImageOption : Any]? = nil)
次のCIImageOptionのいずれかをキーに、
static let auxiliarySemanticSegmentationHairMatte: CIImageOption
static let auxiliarySemanticSegmentationSkinMatte: CIImageOption
static let auxiliarySemanticSegmentationTeethMatte: CIImageOption
値にtrueをセットすることでCIImageオブジェクトとしてSSMを取得できます。
「空」のSSM
iOS 14.1で、kCGImageAuxiliaryDataTypeSemanticSegmentationSkyMatte が追加されました。
名前の通り、「空」をセグメンテーションするためのマスク画像が得られます。
guard let info = CGImageSourceCopyAuxiliaryDataInfoAtIndex(self, 0, kCGImageAuxiliaryDataTypeSemanticSegmentationSkyMatte) as? [String : AnyObject] else { return nil }
let skyMatte = try? AVSemanticSegmentationMatte(fromImageSourceAuxiliaryDataType: kCGImageAuxiliaryDataTypeSemanticSegmentationSkyMatte, dictionaryRepresentation: info)
関連:
- 
画像の領域を分割する処理。 ↩︎ 
- 
取得のための実装方法等の詳細は、拙著「Depth in Depth - iOSデプス詳解」(https://booth.pm/ja/items/1313752)をご参照ください。 ↩︎ 
- 
"Auxiliary"は「補助」という意味。デプス等のデータはメインの画像ではなく、補助的な画像データであるという意味でこう命名されているのでしょう。 ↩︎ 





Discussion