3
1

空間ビデオ(Spatial Video)を青赤メガネで立体視する

Last updated at Posted at 2024-01-25

空間ビデオ(Spatial Video)を青赤メガネで立体視する

iOS 17.2から、iPhone 15 ProとiPhone 15 Pro Maxは、空間ビデオを撮影できるようになりました

ざっくりまとめると、iPhoneの2つのカメラで同時に撮影し、撮影した映像をApple Vision Proでそれぞれ左右の目に提示することで、立体的に動画を視聴することができるようです、たぶん(すごい!)
しかし、せっかく空間ビデオで撮影してもApple Vision Proは日本では未発売(2024年1月現在)なので、Spatialな空間感を感じることができません
そこで、空間ビデオの左右の目用のそれぞれの映像を取得し、伝統的?な立体視の手法である赤青のメガネで見ると立体的に見えるアナグリフで表示することで、空間ビデオを立体視してみました

リポジトリはこちら

空間ビデオの撮影

iOS17.2以降にアップデートし、「設定」>「カメラ」>「フォーマット」の「Apple Vision Pro用の空間ビデオ」をオンにします
カメラアプリのビデオ撮影モードの右下にApple Vision Proのようなアイコンが表示されるので、有効化して撮影します

空間ビデオをアナグリフで表示

空間ビデオの取得

普通に?Photos Frameworkを使ってAVAssetを取得します

let assets: PHFetchResult = PHAsset.fetchAssets(with: .video, options: nil)
let asset = assets.lastObject!

let resources = PHAssetResource.assetResources(for: asset)
print("originalFilenames: \(resources.map({ $0.originalFilename }))")

let options = PHVideoRequestOptions()
options.isNetworkAccessAllowed = true
options.version = .original
options.deliveryMode = .highQualityFormat
PHImageManager().requestAVAsset(forVideo: asset, options: options) { [unowned self] (asset: AVAsset?, _: AVAudioMix?, _: [AnyHashable : Any]?) in
    handle(asset: asset!)
}

左右の目用の映像の取得

AVPlayerVideoOutput という仕組みがiOS17.2から追加されたようです
stereoscopicForVideoOutputを利用できるように設定しておきます
また、画面の更新に合わせて動画の表示を更新するために、CADisplayLinkを使います

func handle(asset: AVAsset) {
    let player = AVPlayer(playerItem: AVPlayerItem(asset: asset))

    let specification = AVVideoOutputSpecification(tagCollections: [.stereoscopicForVideoOutput()])
    let videoOutput = AVPlayerVideoOutput(specification: specification)
    player.videoOutput = videoOutput

    let displayLink = CADisplayLink(target: self, selector: #selector(onDisplayLink(link:)))
    displayLink.add(to: .main, forMode: .common)
    player.play()
}

videoOutput.taggedBuffers から左右の目用のpixelBufferを取得します
この2つのpixelBufferをアナグリフとして見えるように合成します

@objc func onDisplayLink(link: CADisplayLink) {
    guard let taggedBuffers = videoOutput.taggedBuffers(forHostTime: CMClockGetTime(CMClockGetHostTimeClock())) else { return }

    let buffL = taggedBuffers.taggedBufferGroup.first { $0.tags.contains(.stereoView(.leftEye)) }!
    let buffR = taggedBuffers.taggedBufferGroup.first { $0.tags.contains(.stereoView(.rightEye)) }!

    guard case let .pixelBuffer(pbL) = buffL.buffer,
          case let .pixelBuffer(pbR) = buffR.buffer else { return }

    let ciL = CIImage(cvPixelBuffer: pbL)
    let ciR = CIImage(cvPixelBuffer: pbR)

    // ciLとciRからアナグリフの画像を作成
}

アナグリフに変換

左目のRGBのGBを右目のGBに置き換えて表示します
これで、左目の映像はRのみ、右目の映像はGBのみとなります

Metal Shaderを利用するために、Build Settingsで Other Metal Compiler Flags-fcikernelMTLLINKER_FLAGS-cikernel を追加します

#include <metal_stdlib>
using namespace metal;
#include <CoreImage/CoreImage.h>

extern "C" {
    namespace coreimage {
        float4 anaglyph(coreimage::sample_t l, coreimage::sample_t r, coreimage::destination dest) {
            l.gb = r.gb;
            return l;
        }
    }
}

CIKernelを用いて適用します

let kernel: CIColorKernel = {
    let url = Bundle.main.url(forResource: "default", withExtension: "metallib")!
    let data = try! Data(contentsOf: url)
    return try! CIColorKernel(functionName: "anaglyph", fromMetalLibraryData: data)
}()

let context = CIContext()

@objc func onDisplayLink(link: CADisplayLink) {
    guard let taggedBuffers = videoOutput.taggedBuffers(forHostTime: CMClockGetTime(CMClockGetHostTimeClock())) else { return }

    let buffL = taggedBuffers.taggedBufferGroup.first { $0.tags.contains(.stereoView(.leftEye)) }!
    let buffR = taggedBuffers.taggedBufferGroup.first { $0.tags.contains(.stereoView(.rightEye)) }!

    guard case let .pixelBuffer(pbL) = buffL.buffer,
          case let .pixelBuffer(pbR) = buffR.buffer else { return }

    let ciL = CIImage(cvPixelBuffer: pbL)
    let ciR = CIImage(cvPixelBuffer: pbR)

    // ciLとciRからアナグリフの画像を作成
    let ci = kernel.apply(extent: ciL.extent, arguments: [ciL, ciR])!
    let cg =  context.createCGImage(ci, from: ci.extent)!
    image = UIImage(cgImage: cg)
}

アナグリフの計算はこちらを参考にさせて頂きました
https://3dtv.at//knowhow/anaglyphcomparison_en.aspx

結果

作成したアナグリフの動画がこちらです
赤青メガネで見てみると、それっぽい立体感を感じる気がします

output-palette.gif

画像を重ねると確かに左右の目用に異なる画像が撮影されていることがわかります
また、手前にいる某ペンギンさんは左右の目用の画像の差が大きく、遠くにある枝は差が小さく、さらに遠くにあるビルは差がほとんどないこともわかります

Frame 295.png

まとめ

iOS 17.2から、iPhone 15 ProとiPhone 15 Pro Maxで撮影できるようになった空間ビデオをアナグリフで表示してみました
空間ビデオをサポートするそのまんまの名前のAPIが新規に追加されており、シンプルに実現できました
もしよろしければ、Apple Vision Proを手に入れるまで、赤青メガネで空間ビデオを楽しんでみるのはいかがでしょうか?w

(余談)Cinematic Modeとかビデオトラックの話

Appleの映像の3Dの技術と聞いてCinematic Modeを思い浮かべる方も多いかもしれません

Cinematic ModeもiOS17からCinematicというframeworkが追加されました

こちらのframeworkも近々遊んでみようと思っているのですが、とりあえず空間ビデオとCinematic Modeでどのような動画の構造になっているかざっくり見てみました

空間ビデオはAppleのドキュメントに追加されています

HEVC Stereo VideoBeta を見てみると、どうやら1つのトラックに複数の映像を埋め込んでいるようです

また、空間ビデオを作成についてQ&Aで触れられていました
正しく作れているか確認する手段がないので、試してないのですがCMTaggedBufferを使うといい感じに作れそうな波動を感じています

一方で、Cinematic Modeは通常のビデオトラックとは別にauxiliaryな?ビデオトラックがあるようです

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1