« AviUtl拡張編集のLuaスクリプトのデバッグツール | トップページ | ニコエンコ v0.77を使う場合の注意点 »

2011年11月 5日 (土)

AviUtlと拡張x264(GUI)Ex 1.13までは10bit-depthのBT.709エンコードをするのは難しかった(1.14で対応済)

※2011/11/9追記
  2011/11/8にリリースされた「拡張x264(GUI)Ex 1.14」にて、YC48からの色空間変換の設定が実装されました。
  したがって、この記事の内容は既に意味がなくなっています。

  拡張x264(GUI)Ex 1.14で「10bit-depthでのBT.709出力」を行ないたい場合は、
    1.ソース映像を通常どおりプレビューが正しい色になるように読み込む。
    2.拡張x264(GUI)Exの「x264」タブで「10bit depth」にチェックを入れ、10bit-depthのx264.exeを指定する。
    3.拡張x264(GUI)Exの「x264」タブで「colormatrix」に「bt709」を指定する。
    4.拡張x264(GUI)Exの「拡張」タブで「YC48出力」を「BT.601→BT.709」にする。
  という設定を行なえば、YC48からの10bit変換で色空間を考慮した変換が行なわれ、
  問題なく「10bit-depthでのBT.709出力」ができるようになっています。
  作者のrigaya様、実装ありがとうございます。

 
◎2011/11/9追記
   ・拡張x264(GUI)Ex 1.14にてYC48からの色空間変換の設定がサポートされたので記事をクローズ。
    タイトルも過去形に変更。
   ・10bitでの色変換についての参考記事へのリンクを書き忘れていたので追加。
       rigayaの日記兼メモ帳 H.264 high bit depth decoder

◎2011/11/6修正
   ・YC48を「約12bit深度」としましたが実際には「16bit深度」でした。
    輝度や色差のマッピング範囲が約12bit+α相当なのであながち間違っていないかもしれませんが
    YC48のデータ構造自体は16bit深度です。(PIXEL_YC構造体では輝度と色差の値はshortとなっている)
       参考: x264のYUV4:4:4/RGB、AviUtlのYC48とYUY2入出力の仕様 (POP@4bit)
   ・拡張x264出力(GUI)Exの設定画面で出力色空間を指定するなら、拡張タブではなく
    x264タブの「10bit depth」の近くで設定できたほうが自然かもと思ったのでその旨を追記。
    

◎2011/11/5修正
   ・YC48を「12bit+1深度」と表現していたのを「約12bit深度」に修正。
    TVスケール輝度が0~4096になるだけで、フルスケール輝度は-299~4470の範囲を取るため。
   ・Makki氏の「AviUtlの内部形式について」のリンクを追加。
   ・デコーダーについていくつか注意書きを追記。
    比較的新しいものでないと10bit再生には対応していません。

※注意
  記事作成時の各ソフトのバージョンは、
     AviUtl 0.99j2
     拡張x264(GUI)Ex 1.13
  です。

------------------------------記事本文ここから------------------------------

AviUtl」と「拡張x264(GUI)Ex」を使って

   「10bit-depthのBT.709エンコード」

を行なおうとした場合、8bitの時と同じ感覚では間違えるというか、
かなりわかりにくいことをしないと正しくエンコードできないことがわかったのでまとめてみました。
色空間の話になるので、なかなか厄介でわかりにくいです。

うまく対処するには、拡張x264(GUI)Ex側での機能追加が必要な気がします。

この記事にある内容の影響を受けるのは、地デジのTSなどを10bit-depthでエンコードして
保存しているような人になると思います。(HDサイズならBT.709にしているはずですし。)
あとはPV3/PV4を使ってHDキャプチャしたものを10bit-depthでエンコードしている人とか。

なお、ニコニコ動画への投稿には10bit-depthは使ってはいけないので関係ありません。
(FlashPlayerは10bit-depthを正しく再生できないため。右側にマゼンタ色の線が入る上、色も微妙におかしくなる。)

■問題点

  AviUtlと拡張x264(GUI)Exを使って、以下の2つの条件を満たす出力を行なう場合、
  特殊な方法をとらないと正しく出力ができない。

    条件1.8bit-depthでi444出力を行ないたい。
         あるいは10bit-depthで出力(i420,i422,i444)を行いたい。

    条件2.出力色空間をBT.709としたい。

  AviUtlと拡張x264(GUI)Exでの色空間の扱いを詳しく知らないまま
  直感的にやろうとすると、かなりの高確率で間違ったエンコードになると思います。
  特にAviUtlの内部色空間である「YC48(16bit深度)」について理解しておかないとわかりにくいです。

長いので折りたたみます。
 

■用語

  YC48・・・AviUtl内部で使われているYUVフォーマット。
        YUV4:4:4で各ピクセルごとに輝度と色差を保持している。
        輝度や色差は16bitの深度(※)をもつので、色を高精度に扱える。
        1pixelにつきY(16bit)+Cb(16bit)+Cr(16bit)の48bitのデータを持つので「YC48」。
        詳細については
            「AviUtlの内部形式について」(Makki氏)
            「x264のYUV4:4:4/RGB、AviUtlのYC48とYUY2入出力の仕様」(POP氏)
            「H.264 high bit depth decoder」(rigaya氏。記事コメント欄での議論がポイント。)
        がとても参考になる。

          ※データ構造としては16bitだが、例えば輝度のマッピングは以下のように12bit+α程度になっている。
               TVスケール時の輝度範囲(16~235)が0~4096
               フルスケール時の輝度範囲(0~255)が-299~4470

  8bitモード・・・拡張x264(GUI)Exにて、「10bit depth」のチェックを外した状態。
          8bit-depthのx264.exeを使用する。
          (筆者が勝手につけた仮の呼び方。)

  10bitモード・・・拡張x264(GUI)Exにて、「10bit depth」のチェックをつけた状態。
           10bit-depthのx264.exeを指定できるようになり、エンコード時に
              --input-depth 10
           というオプションが渡されるようになる。
           (筆者が勝手につけた仮の呼び方。)

  i420,i422,i444,rgb・・・拡張x264(GUI)Exのというかx264の出力色フォーマット指定。(--output-csp)

 
■間違ったエンコードの例

  例えば以下のような手順で、「BT.709の10bit-depthのi420エンコード」を行なったとします。

    手順1.BT.709のソース映像を用意。

    手順2.ソース映像をYUY2でAviUtlに入力。

    手順3.「設定→色空間の設定→入力」を「BT.709」にする。
         プレビュー画面は正しい色に見えている。

    手順4.「設定→色空間の設定→出力」を「BT.709」にする。

    手順5.拡張x264(GUI)Exの設定画面で、x264.exeの指定欄の上にある
         「10bit depth」にチェックを入れ、10bit-depthのx264.exeを指定する。

    手順6.拡張x264(GUI)Exの設定画面で、「色空間」の「colormatrix」に、「bt709」を指定する。

    手順7.拡張x264(GUI)Exの設定画面下部のコマンド表示は最も単純なケースでこんな感じ。
            --input-depth 10 --crf 23 --colormatrix bt709

    手順8.拡張x264(GUI)Exによるエンコードを実行。

  ソース映像も入力も出力もBT.709に統一してcolormatrixもつけているので
  別に何も問題ないだろうと考える人も結構いると思います。

  しかし、このエンコードによって出来上がるのは

     「データはBT.601なのに、BT.709としてデコードするよう要求する、おかしなH.264ストリーム」

  となってしまいます。(理由は下で説明します)

  そのため、「--colormatrixの指示を反映してデコードしてくれる環境(※1)」で
  再生すると、色が変わって見えてしまいます。
  RGB(0,255,0)だった部分がRGB(0,214,0)になるなど、主に緑系が暗くなる感じ。

  ※1・・・colormatrixに対応したデコーダーでYUV→RGB変換が行なわれるよう、
       使っているデコーダーで以下のように設定してみてください。
       (常にこうしておくのが良いというわけではありません。確認のため。)
       (比較的新しいバージョンでないと、10bit-depthの再生には対応していません。

    ●FlashPlayerであれば、「ハードウェアアクセラレーションを有効にする」のチェックを外す。
      (ただし最初に書いたとおり10bit-depthの映像を再生すると色が微妙に変わるので確認には向かない)

    ●ffdshow tryoutsでデコードしているなら「ビデオデコーダーの設定」で以下のように設定する。
        「"出力→サポートされた出力色空間"で"RGB32"だけチェックを残し、他は外す」
        「"RGB変換→YCbCrの指定"を"自動"にする」
        「"RGB変換→入力レベル"を"自動"にする」(これはfullrange関係なので関係ないけど一応)

    ●LAV Video Decoderでデコードしているなら以下のように設定する。
        「Video SettingsのOutput FormatsでRGB32だけチェックを残し、他は外す」

    ●CoreAVCでデコードしている場合は以下のように設定する。
        「Output formatsで、RGB32だけチェックを残し、他は外す」
        「Input ColorspaceをAuto Detectにする」
        「Input levelsをAuto Detectにする」(これはfullrange関係なので関係ないけど一応)

 
■説明

  拡張x264出力(GUI)Ex 1.13の時点での10bit-depth出力は、基本的には
    「x264.exe選択欄の上にある"10bit depth"にチェックを入れ、10bit-depthのx264.exeを指定する」
  という設定を行なうことで実行できます。(仮に10bitモードと呼ぶ)

  例として「10bit-depthのi420出力」を考えた場合、以下のような流れでエンコードが行なわれます。

     処理1.x264guiEx.auoが、AviUtlからYC48のデータを受け取る。

     処理2.x264guiEx.auoの内部で「YC48→nv12p(10bit)」の変換を行う。

     処理3.x264guiEx.auoは、変換で得られたnv12p(10bit)のデータを
             --input-depth 10 --input-csp nv12
          というオプション付きで、10bit-depthのx264.exeに渡す。

     処理4.あとは10bit-depthのx264.exeが指示に従ってエンコードを行なう。

  この処理では、YC48(16bit)から直接10bitデータに変換されるので高精度の変換が期待できます。

  しかし、ここで以下の点が問題になります。

    A.AviUtlは基本的にYC48にBT.601相当のデータを格納して扱っている。
      「色空間の設定」の「入力」を「BT.709」にした状態で、BT.709のソース映像を
      YUY2読み込みすると、
          YUY2(データはBT.709)→YC48(データはBT.601)
      という変換が行なわれる。
      この時点でYC48のデータはBT.601になるというところが問題のポイント。

    B.処理1でx264guiEx.auoにYC48データを直接渡しているため、
      AviUtlの「設定→色空間の設定→出力」の設定は、この場合意味を持たない。
      つまり上に書いた「手順4」は無意味。
      (この設定は、AviUtl本体から出力プラグインにYUY2形式でデータを渡す場合の
       YC48→YUY2変換の変換式を指定するためのものであるため。)

    C.処理2でx264guiEx.auoが行なっている「YC48→nv12p(10bit)」の変換は、
      YC48(16bit)からnv12p(10bit)へのサンプリングおよび深度の変換のみであり、
      BT.709やBT.601の色空間変換は考慮していない。

  つまり、上の流れでは、BT.601のYC48データをそのままx264guiEx.auoに渡し、
  さらにそれがそのまま10bitに変換されてx264.exeに渡されるため、

     「BT.709でエンコードしようとしたつもりだったのに、データはBT.601になってしまう」

  という現象が発生してしまうのです。

 
■備考

  YC48自体は16bit深度のYUV色空間にすぎないので、
  BT.601のデータもBT.709のデータも格納できるのだが、
  AviUtlでは「YC48のデータはBT.601」という前提で動作することが多い。
  例えばプレビュー画面を表示する場合のYC48→RGB変換には、
  BT.601の計算式が使用される。
  (ただしオーバーレイ表示を有効にしている場合はまた別の話になる)

  また、フィルタの中にも、YC48をBT.601だという前提で扱うものがある。
  例えば拡張色調補正でRGB調整を行なう場合は、
  YC48→RGB→YC48の変換が入るが、この時の変換式はBT.601である(はず)。
  そのため、YC48にBT.709データが格納されていると正常に動作しない。
  (少なくとも完全に意図どおりの動作にはならないはず)

  AviUtl本体に読み込んだ動画に拡張編集プラグインで字幕や図形などを重ねるような場合でも、
  拡張編集プラグインのRGB⇔YC48変換はBT.601なので、
  YC48に格納されているデータがBT.709だと色の不整合が起きる。

  もともとAviUtlはBT.601ベースで開発されたものであり、
  BT.709の入出力をサポートしたのは比較的最近であるという事情もある。

 
■根本的な対処案

  なるべく影響の少ない方法で対策を考えると、拡張x264(GUI)Ex側で、

     「YC48(16bit)→???(10bit)の10bit変換を行なう場合に使用する
      色空間変換式(BT.601 or BT.709)を設定できるようにし、
      それに従って色空間変換も含めた10bit変換を実行するようにする。」

  という機能追加を行なっていただくのが解としてはスマートなのかなと思います。
  (無論、対応していただけるかどうかは作者様の意向次第ですが。)

  AviUtlの「色空間の設定→出力」の設定を取得できるならそれを利用する手もありますが
  おそらく出力プラグイン側でそれを取得することはできないと思うので、
  拡張x264(GUI)Exの設定画面に独自に追加する必要があると思います。
  (x264タブの「10bit depth」の近くに設定欄をつけるか、拡張タブあたりに追加することになるでしょうか。)

  こうしておけば、AviUtl内部のYC48はBT.601にしておくことができ、
  プレビューやフィルタの動作も問題ないと思われます。

 
■現在のバージョン(1.13)で正しく「10bit-depthのBT.709」にエンコードするための暫定対策案

 ●対策案1(BT.709ソースを読み込む場合)
    「色空間の設定」の「入力」を「BT.601」にして読み込む。
    こうすることで、BT.709のデータがそのままYC48に格納されるので、
    あとは普通に拡張x264(GUI)Exで10bitモードでのエンコードをすればよい。

    ◎デメリット
      ・プレビューの色は見た目上おかしくなる
      ・フィルタの動作がおかしくなる可能性がある。

 ●対策案2(BT.601ソースを読み込む場合)
    「AviUtl プラグイン フィルタ By うえぽん」様が公開している「補間なし平均」プラグインを導入し、
    「色空間の設定」の「入力」「補間なし平均(内部BT.709)(BT.601)」にして読み込む。
    こうすることで、YC48のデータをBT.709にすることができるので、
    あとは普通に拡張x264(GUI)Exで10bitモードでのエンコードをすればよい。

    ◎デメリット
      ・プレビューの色は見た目上おかしくなる
      ・フィルタの動作がおかしくなる可能性がある。

 ●対策案3
    「色空間の設定」の「入力」にはソース映像の色空間を指定し、
    「出力」には、出力したい色空間を指定する。(ここまでは普通の使い方)
    拡張x264(GUI)Exでは、「10bit depth」のチェックを外した状態(仮に8bitモードと呼ぶ)で
    10bit-depthのx264.exeを指定して使う。
    (本来8bit-depthのx264.exeを指定する場所に
     
10bit-depthのx264.exeを指定するという特殊な使い方)

    こうすると、i420出力やi422出力では、

       1.AviUtl本体で、「色空間の設定」の「出力」の設定にしたがって
         YC48→YUY2(8bit)変換を行い、x264guiE.auoに渡す。

       2.x264guiEx.auoは、そのYUY2(8bit)データをnv12(8bit)またはnv16(8bit)に変換して、
         10bit-depthのx264.exeに渡す。

       3.x264.exeでは、受け取ったデータが8bitなので、内部で8bit→10bit変換を行い、
         そのデータをエンコードする。

    という処理となる。
    (つまり10bitへの変換を行なうのはx264guiEx.auoではなくx264.exeとなる。)

    ◎問題点
      ・8bitモードでも、i444出力を行なう場合はYUY2ではなく
       YC48でx264guiEx.auoに渡されるようになっているので、
       BT.601でエンコードされてしまうという問題が発生する。
       従って、i444出力を行なう場合はこの対策案3は使えない。

    ◎注意点
      ・x264.nlにあるx264.exeにはr2106の時点ではまだ10bit深度の色変換バグが残っているので、
       JEEB氏がビルドしているパッチ適用済みのx264.exeを使う必要がある。

    ◎メリット
      ・YC48のデータはBT.601となるため、プレビューや各種フィルタの動作に問題は発生しない。
      ・AviUtlの「色空間の設定」を有効に使える。

    ◎デメリット
      ・YC48(16bit深度)→YUY2(8bit深度)→10bit深度という変換になるので
       YUY2にした時点で精度が落ちてしまい、YC48の高精度が生かせない。
      ・RGBソースを扱う場合もこの方法で対処できるが、同じように一度YUY2に変換されるため
       YC48の高精度が生かせない。

 
■参考: x264guiEx.auoの入力パターンと出力パターン
  (AviUtl本体からどういう形式で受け取って、どういう形式に変換してx264に渡すか)

  ●8bit-depthの場合
     i420出力・・・YUY2で受け取りnv12pに変換してx264へ渡す
     i422出力・・・YUY2で受け取りnv16に変換してx264へ渡す
     i444出力・・・YC48で受け取りYUV444に変換してx264へ渡す
     rgb出力・・・RGBで受け取り、順番だけ並び替えてそのままRGBでx264へ渡す

  ●10bit-depthの場合
     i420出力・・・YC48で受け取りnv12p(10bit)に変換してx264へ渡す
     i422出力・・・YC48で受け取りnv16(10bit)に変換してx264へ渡す
     i444出力・・・YC48で受け取りYUV444(10bit)に変換してx264へ渡す

  参考:
    拡張x264(GUI)Ex 1.13のauo_video.cppの49行目にあるget_aviutl_color_format()関数

int get_aviutl_color_format(int use_10bit, int output_csp) {
    //Aviutlからの入力に使用するフォーマット
    switch (output_csp) {
        case OUT_CSP_YUV444:
            return CF_YC48;
        case OUT_CSP_RGB:
            return CF_RGB;
        case OUT_CSP_YUV420:
        case OUT_CSP_YUV422:
        default:
            return (use_10bit) ? CF_YC48 : CF_YUY2;
    }
}

|

« AviUtl拡張編集のLuaスクリプトのデバッグツール | トップページ | ニコエンコ v0.77を使う場合の注意点 »

コメント

ども。x264guiEx作者のrigayaです。

YC48のBT.709出力についてのご指摘ありがとうございます。
BT.601→BT.709変換で追加してみましたのでよろしければ使ってみてください。

内部YC48をBT.709のままにした時の話ですが、
内部BT.709をちゃんとプレビューするには
nilpo氏がBT.709プレビュープラグイン(http://nilposoft.info/archives/435)を
作ってくださっているのでプレビューだけならこれでいいかなーと。
RGB変換を伴う編集をする場合の問題は解決できませんが。

ではでは。

投稿: rigaya | 2011年11月 9日 (水) 00時02分

>rigaya様

x264guiEx 1.14での実装ありがとうございました。
x264 r2106 10bit-depth(x264.nl)とLAV Filters 0.38での組み合わせで
ちゃんとBT.709で出力されていることを確認いたしました。

nilpo氏のプラグインのことは知りませんでした。色々あるのですねえ。
こちらの情報もありがとうございました。

投稿: 金の髭 | 2011年11月 9日 (水) 02時08分

すみません質問なんですが私はavisynthでAviUtlのプラグインを使う時があるのですが
ConvertToYUY2()
ConvertYUY2ToAviUtlYC()
AviUtlのplugin
ConvertAviUtlYCToYUY2()
ConvertToYV12()
としてBT.709のソースをx264に--colormatrix bt709 --fullrange offとして渡して10bitエンコードしてもこちらに書かれている問題は起こるのでしょうか?

投稿: | 2011年11月18日 (金) 05時50分

反応が遅れてすみません。

>問題は起こるのでしょうか?

「AviUtlのplugin」のところで特殊なことをやらない限り問題は起きないはずです。
ConvertYUY2ToAviUtlYC()は、
  「8bitのYUV4:2:2データ(YUY2形式)を、符号付き16bit(有効約12bit)のYUV4:4:4データ(YC48形式)に変換する」
という処理を行ないます。ConvertAviUtlYCToYUY2()はその逆です。
この処理ではBT.601⇔BT.709変換の処理は入らず、BT.601ならBT.601のまま、BT.709ならBT.709のままになります。

  ConvertToYUY2() →データをYUY2にする(YUVデータはBT.709相当)
  ConvertYUY2ToAviUtlYC() →データをYC48にする(YUVデータはBT.709相当)
  AviUtlのplugin
  ConvertAviUtlYCToYUY2() →データをYUY2にする(YUVデータはBT.709相当)
  ConvertToYV12() →データをYV12にする(YUVデータはBT.709相当)

という流れになりますので、データはずっとYUVのままで、BT.709相当の値のまま処理されます。

投稿: 金の髭 | 2011年11月22日 (火) 23時31分

コメントを書く



(ウェブ上には掲載しません)




トラックバック

この記事のトラックバックURL:
http://app.f.cocolog-nifty.com/t/trackback/1278146/42860357

この記事へのトラックバック一覧です: AviUtlと拡張x264(GUI)Ex 1.13までは10bit-depthのBT.709エンコードをするのは難しかった(1.14で対応済):

« AviUtl拡張編集のLuaスクリプトのデバッグツール | トップページ | ニコエンコ v0.77を使う場合の注意点 »