PIXEL_YC
構造体の定義この文書はAviUtl内部で使われている画像表現形式について解説するものです。 ただし、著者はAviUtlの一ユーザにすぎず、公開されている情報と実際の外部動作を元にした想像で書いています。 そのため実際のAviUtl内部の動作とは異なる可能性が多分にあることに留意してください。 執筆時点のAviUtlの最新版は0.99g3※0で、特記しない場合このバージョンとして構いません。 また、0.99以降で採用されたYUY2フィルタモードについては触れません。
この文書がAviUtl利用者、特にプラグイン開発者の助けになれば幸いです。 内容の間違いや誤植はMakKiまで連絡ください。
AviUtl内部での画像表現は、PIXEL_YC
構造体の配列として保持されています。
この形式はかなり以前※1から採用されていたもので、
AviUtlの大きな特徴の一つとなっています。
また、入出力では'YC48'というFourCCが使われています※2。
PIXEL_YC
構造体の定義AviUtlプラグインSDK filter.hより引用
// YC構造体 typedef struct { short y; // 画素(輝度 )データ ( 0 ~ 4096 ) short cb; // 画素(色差(青))データ ( -2048 ~ 2048 ) short cr; // 画素(色差(赤))データ ( -2048 ~ 2048 ) // 画素データは範囲外に出ていることがあります // また範囲内に収めなくてもかまいません } PIXEL_YC;
YUVを拡張したような形式で、Yを0-4096,UVを-2048-2048として各16bitで表しています。 具体的には、完全な白(RGB=FFFFFF)は y=4096,cb=0,cr=0、完全な黒(RGB=000000)は y=0,cb=0,cr=0 と表されます。 これは広く一般に用いられている各要素8bitの形式に比べて、約16倍の精度を持っていることになります。 これによりフィルタリングによる丸め込み誤差が小さく抑えられており、 AviUtlが高精度と言われる大きな理由となっています。
AviUtl内部形式の各要素は12bitの4096階調であるとよく誤解されていますが、 見ての通り実は各4097階調であり、12bitでは1足りません。 他の形式に変換する際はこの点に注意を払う必要があります。
注釈にもある通り、0未満や4097以上の輝度の他、-2049以下や2049以上の色差も認められているため、 YUVのデータ範囲外の値に相当する値も保持することが可能です。 したがって、フィルタプラグインはこのような範囲外の値も正しく扱えなければなりません。 ただし、Yを0-4096の範囲に丸め込むといった対処方法は、 YUVのダイナミックレンジを潰してしまうことになるため好ましくありません。 時々極端な範囲外の値を持つ画素を渡すと誤動作するプラグインを見ます。 特に名をあげることはしませんが、修正されることを願います。
現在のAviUtl内部での変換式は、0.98でYUY2入出力対応とともに確立されたもので、 RGBとの変換式はそれ以前に何度か変更されています※3。 この項目では最新バージョンを含む0.98以降での変換と同等な計算式を示します。 (整数演算のみで表しました。)
YUY2は各要素8bitの256階調ですが、一般的にはYを16-235の220階調、UVを16-240の225階調として扱います。 したがって、YUY2の各要素をそのまま16倍しただけでは、YC48の0-4096を有効範囲としたスケールには合致しません。 これがAviSynthのwarpsharpパッケージ のConvertYUY2ToAviUtlYCの抱えていた問題です※4。 正確を期すには、次の式を使う必要があります。 (AviUtl内部の変換と全く同じ結果が得られます。またシフト演算子は算術シフトです。)
// YUY2->YC48
y = ((Y * 1197)>>6) - 299;
cb = ((U - 128)*4681 + 164) >> 8;
cr = ((V - 128)*4681 + 164) >> 8;
YUY2では横に並ぶ2画素づつ色差を共有しており、AviUtlでは、
左側の画素(左端を0とした時の偶数ピクセル)にその色差の値をそのまま格納、
右側の画素(同、奇数ピクセル)は両側のピクセルの値の中間値にするという補間処理しています。
左端から k
ピクセルめの色差を C[k]
として、この補完処理を式で表すと次のようになります。
C[2n+1] = ( C[2n] + C[2n+2] ) >> 1;
AviUtlでは2で割るところを1bitの算術シフトで計算しているようです(-∞方向への丸め込み)。 最右端の奇数画素の色差はその左の画素と等しくなります。 ちなみに、AviUtl 0.99 で見られた色差処理のバグ※5は、 この補完処理の間違いが原因でした。
// YC48->YUY2
Y = ((y*219 + 383)>>12) + 16;
U = (((cb + 2048)*7 + 66)>>7) + 16;
V = (((cr + 2048)*7 + 66)>>7) + 16;
逆変換では、色差はleft-originとして左の画素のみを使用し、右の画素の値は捨てられます。 YUY2→YC48で補間しているため、こちらでは補完処理はしていません。 こちらでも左右の色をマージしてしまうと、結果的にさらに隣のピクセルの色が混ざることになり可逆にならないからです。
式からも明らかなように、Yの16-235が0-4096,UVの16-240が-2048-2048に対応する、 いわゆる伸張圧縮変換を行っています※6。 また、Yの0-15,236-255およびUVの0-15,241-255に相当する値も範囲外の値としてYC48に保持されており、 YUVのダイナミックレンジは損なわれません。 YUY2とYC48だけの変換であれば可逆であることもわかります。
24bit RGBとYC48の相互変換はAviUtlがフィルタプラグインに提供する EXFUNC
の
rgb2yc()
, yc2rgb()
で行えます。
この関数と同等の変換式は次のようになります。
// YC48->RGB
R = ( 255*y + ( ((22881*cr>>16)+3) <<10) ) >>12;
G = ( 255*y + ( ((-5616*cb>>16)+(-11655*cr>>16)+3) <<10) ) >>12;
B = ( 255*y + ( ((28919*cb>>16)+3) <<10) ) >>12;
// RGB->YC48
y = (( 4918*R+354)>>10) + (( 9655*G+585)>>10) + (( 1875*B+523)>>10);
cb = ((-2775*R+240)>>10) + ((-5449*G+515)>>10) + (( 8224*B+256)>>10);
cr = (( 8224*R+256)>>10) + ((-6887*G+110)>>10) + ((-1337*B+646)>>10);
これら式は、ITU-R BT.601 のアナログRGB-YCbCr変換の式を、各要素0-255を有効範囲とした24bit-RGBとYC48のスケールに合わせたものと思われます。 このため、BT.709のようなカラーマトリクスの異なる形式で記録されたソースを用いる場合には注意が必要です。 また、RGBとYC48だけの相互変換ならば完全に可逆であり、劣化しません。 もちろん間にYC48→YUY2変換を挟むと、RGB→YUY2変換と同様の劣化が起こります。
上記のように、AviUtlではRGB各要素の0-255、YUY2の16-235/16-240をYC48の有効範囲に割り当てています。 AviUtlを通してRGBとYUY2の相互変換をする場合、一度内部形式を経由しますが、 結果としては伸張圧縮変換されることになります。
AviUtlが直接扱えるのは、RGB,YUY2,YC48 の3つの形式ですが、 ユーザが実際に映像として目にするのはRGBかYUY2のどちらかになるでしょう。 特にこの2つの形式では色の表現できる範囲と階調が異なるため、どちらで入出力されているかを知らないと、 画面表示と出力結果で色合いが異なる、出力するとソースや編集中は見られなかったバンディングが現れる、 といった現象に悩まされることになります。 この項目では、AviUtlでの処理の流れを簡単に解説します。
AviUtlへの入力は、内臓されたAVI/BMP読み込み機能のほか、入力プラグインとVFAPIが利用できます。 VFAPIからの入力はRGBに限定されていますが、内臓機能と入力プラグインでは RGB,YUY2,YC48 と、 インストールされた VCM※7 のコーデックの各種形式が扱えます。 それぞれの入力形式から内部形式への変換までを順に説明します。
RGBでデータを受け取ったAviUtlは、3.2節のようにYC48への変換を行い、内部形式とします。
AviUtlへデータがYUY2で渡されると、3.1節で示したようにYC48へ変換され、内部形式として利用されます。
入力プラグイン等がYC48でAviUtlにデータを渡す場合、そのままコピーされて内部形式として利用されます。
上記以外の形式でデータが渡される場合、AviUtlは VCMを利用してRGBもしくはYUY2へのデコードを試みます。 デコード可能なコーデックが見つからない場合は、ファイルの読み込みに失敗します。 どちらの形式へデコードするかは「ファイル→環境設定→コーデックの設定」の「YUY2で展開する」チェックボックスで設定できます。
RGBとYUY2のどちらでデコードされているかは、「表示→ファイルの情報」の「ビデオ展開形式」で確認できます。
AviUtlから映像を出力する方法は、AVI出力、出力プラグイン、VFAPIの3つです。
標準機能のAVI出力では、ダイアログの「ビデオ圧縮」ボタンを押すことで、出力形式を指定できます。 ここで「未圧縮」を指定すると24bit-RGBへ、「YUY2」を指定するとYUY2形式へとYC48から変換されてAVIファイルに書き込まれます。 他にもインストールされているコーデックも指定できますが、コーデックに渡される形式は入力と同様、 「ファイル→環境設定→コーデックの設定」の「YUY2で圧縮する」チェックボックスで指定できます。
YUY2を受け付けないコーデックでは、「YUY2で圧縮する」チェックボックスがあらかじめ無効にされており、 常に24bit-RGBで受け渡しを行います。
出力プラグインからAviUtl本体に画像を要求する際、RGB、YUY2、YC48のいずれかの形式を指定することができます。 AviUtlは出力プラグインの要求に従ってYC48をそのまま、もしくはRGB、YUY2へ変換し、プラグインへ渡します。
VFAPIによる出力は仕様で24bit-RGBに限定されています。aviutl.vfpはYC48からRGBへ変換し、呼び出し元に画像を渡します。
編集画面への表示方法は、「表示→オーバーレイ表示」のチェックの有無で決まります。
クリップボードを利用したコピー&ペーストは DIB (Device Independent Bitmap) 形式で行われており、 必然的にRGBでの受け渡しになります。 クリップボードへのコピーでは、まずYC48からRGBへと変換し、そのビットマップをクリップボードへと渡します。 ペーストにおいて受け取ったRGBのビットマップは、そのまま内部に保持され、YC48へ変換して利用されます。 プロジェクト保存をする際には、元のRGBのビットマップがaupファイルへ書き出されます。
AviUtlという素晴らしいソフトを開発、公開してくださったKENくん様、 並びに様々なプラグインを開発、時にはソースまで公開してくださったプラグイン開発者の皆様、 そしてこの文書を読んでいるあなたに多大な感謝を捧げます。