
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)
}
}
エラーハンドリングは飛ばしていますが、上記のコードを実行すると、人物の透過画像が作れます。


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

ハマったところ
実は、いくつかハマりポイントがあり、もくもく会の時間内に作り切ることができませんでした。
XCodeのシミュレータでは動作しない
カメラ映像を使うFrameworkなどと同様に、VNGeneratePersonSegmentationRequestは、シミュレータでは動作しません。
iOSまたはiPadOS実機で動かしましょう!
背景は透過されるが、人物部分が背景画像になってしまう
VNGeneratePersonSegmentationRequestからの出力画像は、小さくなってしまうことがあるようです。
最初、マスク画像が小さくなっていることに気が付かず、人物部分に背景が合成されたような画像になってしまいました。
入力画像のサイズを使ってマスク画像をリサイズすることで、人物のみが残る画像を作成することができました!
今回使用したAPI
VNImageRequestHandler perform(_:)
VNGeneratePersonSegmentationRequest
▼採用情報
▼新卒情報はWantedlyで