WWDC Quick Look 💓 By SwiftGGTeam
Enhance your app with machine-learning-based video effects

Enhance your app with machine-learning-based video effects

元の動画を見る

ハイライト

macOS 15.4 から、そして iOS 26 から新たに追加された VTFrameProcessor API は Video Toolbox フレームワークの新機能であり、機械学習ベースの動画処理アルゴリズム群を提供します。これらのアルゴリズムは Apple Silicon 向けに最適化されており、高品質な動画編集(フレームレート変換、超解像、モーションブラー、時間軸ノイズ除去)と、リアルタイム動画拡張(低レイテンシのフレーム補間、低レイテンシの超解像)という 2 つのシナリオをカバーします。


主要内容

動画アプリでスローモーション、超解像、ノイズ除去などを実装しようとすると、これまでは自前でモデルを学習させるか、サードパーティのライブラリを統合するしかなく、工数も大きく、効果も安定しないケースが少なくありませんでした。Video Toolbox は動画アプリでもっとも使われるフレームワークの 1 つですが、Apple Silicon 上で動作する、すぐに使える動画拡張アルゴリズム群はこれまで提供されていませんでした。

今回 Apple は Video Toolbox に VTFrameProcessor API を追加しました。macOS 15.4 で登場し、iOS 26 でも同時に利用可能になっています。開発者は Video Toolbox を import するだけで、高品質動画編集向けのフレームレート変換・超解像・モーションブラー・時間軸ノイズ除去、そしてリアルタイムシナリオ向けの低レイテンシフレーム補間・低レイテンシ超解像といったアルゴリズムを利用できます。すべてのアルゴリズムは Apple Silicon 向けに最適化されており、アプリ側はフレーム単位でデータを供給するだけで済みます。

API の形は 2 ステップに固定されています。まずエフェクトを選び、VTFrameProcessorConfiguration で session を起動。次にフレームごとに処理を行い、VTFrameProcessorParameters で入出力を記述します。それぞれのエフェクトは、Configuration / Parameters の組み合わせと一対一で対応します。フレームワークはフレーム単位のインターフェイスであり、フレームの読み書きや pixel buffer の管理はアプリケーション側の責務です。


詳細

VTFrameProcessor API は合計で 6 種類のエフェクトを提供します。フレームレート変換、超解像、モーションブラー、時間軸ノイズ除去は動画編集シナリオ向け。低レイテンシフレーム補間、低レイテンシ超解像はビデオ会議やライブ配信といったリアルタイムシナリオ向けです。時間軸ノイズ除去は両方のシナリオで利用できます(01:11)。

フレームレート変換はもっともよく使われる入口です。既存のフレームの間に新しいフレームを合成して 1 秒あたりのフレーム数を引き上げる仕組みで、ディスプレイのターゲットフレームレートに合わせる用途や、スローモーション化に利用できます(06:50)。最初のステップは session の作成です(08:06)。

// Frame rate conversion configuration

let processor = VTFrameProcessor()

guard let configuration = VTFrameRateConversionConfiguration(frameWidth: width,
                                                            frameHeight: height,
                                                     usePrecomputedFlow: false,
                                                  qualityPrioritization: .normal,
                                                               revision: .revision1)
else {
     throw Fault.failedToCreateFRCConfiguration
}

try processor.startSession(configuration: configuration)

ポイント:

  • VTFrameProcessor() はステートレスな処理インスタンスで、エフェクトはあとから渡す configuration によって決まります。
  • frameWidth / frameHeight は入力フレームの解像度で、session 起動後は変更できません。
  • usePrecomputedFlow: false を指定するとフレームワークが処理時にオプティカルフローをその場で計算します。アプリに前処理ステップがあれば、VTOpticalFlowConfiguration を使ってオプティカルフローを事前に計算しておき、それを渡すこともできます。
  • qualityPrioritization は画質とパフォーマンスのトレードオフを制御します。
  • revision: .revision1 でアルゴリズムのバージョンを固定でき、本番環境での結果再現に役立ちます。
  • startSession を呼んで初めて、フレームを処理できる状態のプロセッサが手に入ります。

続いてバッファの確保です。入力・出力フレームの pixel buffer はすべて呼び出し側が確保する必要があり、configuration の sourcePixelBufferAttributes および destinationPixelBufferAttributes を使って CVPixelBuffer pool を作成できます(08:56)。

// Frame rate conversion buffer allocation

//use sourcePixelBufferAttributes and destinationPixelBufferAttributes property of VTFrameRateConversionConfiguration to create source and destination CVPixelBuffer pools

sourceFrame = VTFrameProcessorFrame(buffer: curPixelBuffer, presentationTimeStamp: sourcePTS)
nextFrame = VTFrameProcessorFrame(buffer: nextPixelBuffer, presentationTimeStamp: nextPTS)

// Interpolate 3 frames between reference frames for 4x slow-mo
var interpolationPhase: [Float] = [0.25, 0.5, 0.75]

//create destinationFrames
let destinationFrames = try framesBetween(firstPTS: sourcePTS,
                                           lastPTS: nextPTS,
                            interpolationIntervals: intervals)

ポイント:

  • VTFrameProcessorFrame は CVPixelBuffer と PTS をひとまとめにしたもので、フレームワーク全体で統一されたフレームの受け渡し型です。
  • interpolationPhase 配列の各要素は 0〜1 の浮動小数点値で、2 つのフレーム間のどの相対位置にフレームを挿入するかを表します。[0.25, 0.5, 0.75] は 2 フレームの間に 3 フレーム挿入することを意味し、結果として 4 倍のスローモーションが得られます。
  • 配列の長さがそのまま出力フレーム数になります。
  • destinationFramesinterpolationPhase と同じ長さの出力フレーム配列で、呼び出し側がバッファを事前に確保しておく必要があります。

フレームレート変換の最後のステップは、parameters の生成と実行です(09:48)。

// Frame rate conversion parameters

guard let parameters = VTFrameRateConversionParameters(sourceFrame: sourceFrame,
                                                         nextFrame: nextFrame,
                                                       opticalFlow: nil,
                                                interpolationPhase: interpolationPhase,
                                                    submissionMode: .sequential,
                                                 destinationFrames: destinationFrames)
else {
     throw Fault.failedToCreateFRCParameters
}

try await processor.process(parameters: parameters)

ポイント:

  • sourceFramenextFrame が補間に使う 2 つの参照フレームです。
  • opticalFlow: nil を指定するとプロセッサが自動でオプティカルフローを計算します。session で usePrecomputedFlow: true を設定している場合は、ここに事前計算済みのオプティカルフローフレームを渡す必要があります。
  • submissionMode: .sequential はフレームを順番に投入することを示し、フレームワークは前後のフレームの連続性を活用できます。順序通りでない投入の場合は .random を使います。
  • process(parameters:) は async 関数で、戻ってきたときには destinationFrames のバッファに出力フレームが書き込まれた状態になっています。

モーションブラーの場合は、前のフレームと次のフレームの 2 つの参照フレームが必要になります。スローシャッターをシミュレートして残像を作り出すことで、タイムラプス映像をより自然に見せたり、高速な動きにスピード感を加えたりできます(11:23)。Parameters の書き方は次のとおりです(12:35)。

// Motion blur process parameters

sourceFrame = VTFrameProcessorFrame(buffer: curPixelBuffer, presentationTimeStamp: sourcePTS)
nextFrame = VTFrameProcessorFrame(buffer: nextPixelBuffer, presentationTimeStamp: nextPTS)
previousFrame = VTFrameProcessorFrame(buffer: prevPixelBuffer, presentationTimeStamp: prevPTS)
destinationFrame = VTFrameProcessorFrame(buffer: destPixelBuffer, presentationTimeStamp: sourcePTS)

guard let parameters = VTMotionBlurParameters(sourceFrame: currentFrame,
                                                nextFrame: nextFrame,
                                            previousFrame: previousFrame,
                                          nextOpticalFlow: nil,
                                      previousOpticalFlow: nil,
                                       motionBlurStrength: strength,
                                           submissionMode: .sequential,
                                         destinationFrame: destinationFrame) 
else {
    throw Fault.failedToCreateMotionBlurParameters
}

try await processor.process(parameters: parameters)

ポイント:

  • モーションブラーは 1 回の処理で 1 つの出力フレームを生成し、出力は配列ではなく単数の destinationFrame です。
  • 最初のフレームでは previousFrame を nil に、最後のフレームでは `nextFrame` を nil にする必要があります。
  • nextOpticalFlowpreviousOpticalFlow の両方を nil にすると、プロセッサが自動でオプティカルフローを計算します。
  • motionBlurStrength は 1〜100 の範囲で指定し、値が大きいほど残像が長くなります。
  • destinationFrame の PTS は sourceFrame と同じにします。これは「現在の時刻に対応する出力」であることを示すためです。

リアルタイム系のインターフェイスはより軽量です。低レイテンシ超解像は LowLatencySuperResolutionScalerConfiguration と対応する parameters のみを使います。configuration には幅・高さ・スケール比を指定するだけ、parameters にはソースフレームとデスティネーションフレームを渡すだけで済みます(13:53)。これはビデオ会議向けに最適化されており、ネットワーク状況が悪いときでも符号化の劣化を抑え、エッジをシャープに保てます。


重要ポイント

  • やること: フレームレート変換で古い動画から高品質なスローモーションを作る

    • なぜやる価値があるか: interpolationPhase 配列はそのままスローモーション倍率に対応します。[0.25, 0.5, 0.75] は 4x で、サードパーティの補間ライブラリを調整する必要はありません。
    • 始め方: VTFrameRateConversionConfiguration で session を起動し、source / next の 2 フレームの PTS を準備し、目標倍率を 0〜1 の相対位置の配列に変換して渡します。
  • やること: 低レイテンシ超解像をビデオ会議やライブ配信 SDK に組み込む

    • なぜやる価値があるか: API はとてもシンプルで、幅・高さとスケール比を指定するだけで、弱回線下での圧縮ノイズを減らし、人物のエッジをシャープにできます。
    • 始め方: 受信側でデコード後に LowLatencySuperResolutionScalerConfiguration を組み合わせ、デコードしたフレームごとに process を呼び出し、出力フレームをそのままレンダリング層に渡します。
  • やること: モーションブラーでタイムラプスのカクつきを補正する

    • なぜやる価値があるか: motionBlurStrength を 1〜100 のレンジで 1 段階調整するだけで、静止画の連続を連続的な動きに変換でき、シャッター残像を手作業で合成する手間を省けます。
    • 始め方: previous / current / next の 3 フレームを順番に取得し、フレームワークにオプティカルフローを自動計算させ、最初は弱めの strength で効果を確認しつつ調整します。
  • やること: 動画編集パイプラインでオプティカルフローを事前計算する

    • なぜやる価値があるか: オプティカルフローの計算コストは大きいため、前処理ステージで一度計算しておけば、後続の複数のエフェクト(モーションブラー、フレームレート変換)で再利用でき、レンダリング段階の安定性が向上します。
    • 始め方: VTOpticalFlowConfigurationVTOpticalFlowParameters を使って単独でオプティカルフローを計算し、結果を保存します。その後、エフェクトの session で usePrecomputedFlow を true に設定し、parameters にオプティカルフローフレームを渡します。

関連セッション

コメント

GitHub Issues · utterances