見出し画像

Vision Frameworkを使って、iOSで人物の透過画像を作ってみた

こんにちは。スーパーソフトウエア東京オフィス技術部のKoyoです。
弊社のもくもく会で、SwiftのVision Frameworkを使い、人物画像から背景を切り抜いた透過画像を作る方法を勉強しました!


ソースコード

import UIKit
import Vision
public class CreateTransparentImage {

    public func execute(from image: UIImage, to savefile: URL) {
        
        // 背景:黒、人物:白のマスク画像を作る
        let requestHandler = VNImageRequestHandler(cgImage: image.cgImage!, options: [:])
        let request = VNGeneratePersonSegmentationRequest()
        request.qualityLevel = .accurate
        request.outputPixelFormat = kCVPixelFormatType_OneComponent8
        try! requestHandler.perform([request])
        let maskImage = CIImage(cvPixelBuffer: request.results!.first!.pixelBuffer)
        
        // マスク画像が入力画像とサイズが異なる場合、うまく合成できないのでリサイズする
        let originalCIImage = CIImage(cgImage: image.cgImage!)
        let transform = CGAffineTransform(scaleX: originalCIImage.extent.width / maskImage.extent.width,
                                          y: originalCIImage.extent.height / maskImage.extent.height)
        let resizedMask = maskImage.transformed(by: transform)
        
        // マスク画像と入力画像を合成して透過画像を作る
        let createdImage = CIFilter(name: "CIBlendWithMask",
                                    parameters: [ kCIInputImageKey: originalCIImage, kCIInputMaskImageKey: resizedMask])!.outputImage
        
        savePNG(ciImage: createdImage!, filePath: savefile)
    }
    
    // CIImageをpngとして保存する
    private func savePNG(ciImage: CIImage, filePath: URL) {
        let context = CIContext()
        let cgImage = context.createCGImage(ciImage, from: ciImage.extent)!
        let uiImage = UIImage(cgImage: cgImage)
        let pngData = uiImage.pngData()!
        try! pngData.write(to: filePath)
    }
}

エラーハンドリングは飛ばしていますが、上記のコードを実行すると、人物の透過画像が作れます。

ChatGPT作の入力画像(引数で渡した画像)
出力画像(左側に、切り抜きできなかった部分が残ってしまっている)

途中の、request(VNGeneratePersonSegmentationRequest)から取得したマスク画像(maskImage)はこんな感じです。

変数maskImageの中身。左側が一部白い。

ハマったところ

実は、いくつかハマりポイントがあり、もくもく会の時間内に作り切ることができませんでした。

XCodeのシミュレータでは動作しない

カメラ映像を使うFrameworkなどと同様に、VNGeneratePersonSegmentationRequestは、シミュレータでは動作しません。
iOSまたはiPadOS実機で動かしましょう!

背景は透過されるが、人物部分が背景画像になってしまう

VNGeneratePersonSegmentationRequestからの出力画像は、小さくなってしまうことがあるようです。
最初、マスク画像が小さくなっていることに気が付かず、人物部分に背景が合成されたような画像になってしまいました。
入力画像のサイズを使ってマスク画像をリサイズすることで、人物のみが残る画像を作成することができました!

今回使用したAPI

  • VNImageRequestHandler perform(_:)

  • VNGeneratePersonSegmentationRequest


▼採用情報

▼新卒情報はWantedlyで


いいなと思ったら応援しよう!