ハイライト
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 倍のスローモーションが得られます。- 配列の長さがそのまま出力フレーム数になります。
destinationFramesはinterpolationPhaseと同じ長さの出力フレーム配列で、呼び出し側がバッファを事前に確保しておく必要があります。
フレームレート変換の最後のステップは、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)
ポイント:
sourceFrameとnextFrameが補間に使う 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 にする必要があります。 nextOpticalFlowとpreviousOpticalFlowの両方を 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 で効果を確認しつつ調整します。
- なぜやる価値があるか:
-
やること: 動画編集パイプラインでオプティカルフローを事前計算する
- なぜやる価値があるか: オプティカルフローの計算コストは大きいため、前処理ステージで一度計算しておけば、後続の複数のエフェクト(モーションブラー、フレームレート変換)で再利用でき、レンダリング段階の安定性が向上します。
- 始め方:
VTOpticalFlowConfigurationとVTOpticalFlowParametersを使って単独でオプティカルフローを計算し、結果を保存します。その後、エフェクトの session でusePrecomputedFlowを true に設定し、parameters にオプティカルフローフレームを渡します。
関連セッション
- Capture cinematic video in your app — Cinematic Video API を App に統合し、システム提供のアルゴリズムで被写界深度のフォーカス切り替えを伴う動画を撮影する。
- Enhancing your camera experience with capture controls — iPhone 上のカメラキャプチャコントロールをカスタマイズし、撮影系 App にハードウェアボタンの体験を追加する。
- What’s new in Metal 4 — Metal 4 の新しいパイプラインとリソース管理。動画エフェクトと組み合わせて GPU 後処理を行う際に役立ちます。
- Discover machine learning & AI frameworks on Apple platforms — Apple プラットフォーム上のさまざまな機械学習フレームワークの概観。VTFrameProcessor が技術スタック全体のどこに位置するかを把握できます。
コメント
GitHub Issues · utterances