ARKitでカメラからの映像にCIFilterでエフェクトをかける

こちらのポストQiitaの投稿を参考に、カメラからの映像にCIFilterを使ってエフェクトをかけようとしたのだが、手元のiPhone Xで試してみたところ映像に歪みが発生してしまった。公式ドキュメントetc.から正しい対処法を見つけられなかったのだが、普通にアスペクト比を揃えて中央でクロップすれば良さそうだったので、やり方を記しておく。

歪んでしまう理由を調べてみると、カメラからの入力画像(1920 x 1440)とiPhone Xの画面のサイズ(812 x 375)のアスペクト比が違うことが問題のようだった。

private let ciContext = CIContext()
private let gammaAdjustFilter = CIFilter(name: "CIGammaAdjust")!

func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
    guard let currentFrame = sceneView.session.currentFrame else { return }
    let ciImage = CIImage(cvPixelBuffer: currentFrame.capturedImage).oriented(.right)
    gammaAdjustFilter.setValue(ciImage, forKey: kCIInputImageKey)
    gammaAdjustFilter.setValue(3, forKey: "inputPower")
        
    let screenSize = UIScreen.main.bounds.size
    let scaleW = ciImage.extent.width / screenSize.width
    let scaleH = ciImage.extent.height / screenSize.height
    let scale = min(scaleW, scaleH)
    let cropWidth = screenSize.width * scale
    let cropHeight = screenSize.height * scale
        
    if let gammaAdjusted = gammaAdjustFilter.outputImage,
        let cgImage = ciContext.createCGImage(gammaAdjusted, from: CGRect(x: (ciImage.extent.width - cropWidth) * 0.5, y: (ciImage.extent.height - cropHeight) * 0.5, width: cropWidth, height: cropHeight)) {
        sceneView.scene.background.contents = cgImage
    }
}

処理後の画像からCGImageを生成する箇所で、画面のアスペクト比にあわせて中央部分のみをクロップする(Aspect Fill)ようにしてからbackground.contentsにセットするように変更したところ、歪まず正しいアスペクト比で表示されるようになった。

全画面表示ではない場合はUIScreen.main.boundsではなくsceneView.boundsを使う。ただその場合、sceneView.boundsをレンダリングスレッドから参照するとエラーになってしまうので、viewDidLayoutSubviewsなどでsceneView.boundsをどこかにコピーしておく必要がある。

単純に中央のクロップで問題ないのか(Hit Testとかでずれたりしないか)若干心配ではあるが、今のところ問題なく動いていそう。

Pocket

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です