トップ 検索 一覧 差分 ソース ヘルプ RSS ログイン

x264(mbtree)

このページの全ては誤っているかもしれません。x264関連の記事に関してを読んでください。

x264(mbtree)

r1197でコミットされたMacroblock Tree Ratecontrolの、Doom9のフォーラムに投稿された時点の説明を訳してみた。映像によってはかなりの低ビットレートでもそこそこの画質を保つ(この例はなんと67kbps)模様。

ついでに、mbtreeの機能を解説してみた(和訳の後に掲載)。こんなに早くコミットされるとは思っていなかったので内容は纏まっていないが、おそらくこのまま放置。例によって解説は猫科研究所での理解であり、素人の知ったかぶりでしかないので注意。


=ここから日本語訳=

x264 "Macroblock Tree Ratecontrol" testing

 更新

RC1での変更点

  1. Statsファイルのフォーマット変更;マクロブロックごとに2バイトのみ使用するように。エンディアン変更がサポートされた。エラーの取り扱いを改善。
  2. 更なるコマンドラインチェックの修正。
  3. 伝播係数をオーバーフロー時にクリップ。
  4. メモリ節約のためデフォルトのlookaheadを40に。
  5. Akupenguinの要望で--lookaheadではなく--rc-lookaheadと呼ばれるように。
  6. その他色々、パッチ内のコミットメッセージを読むこと。

0.13での変更点

  1. VBVパッチでバグが修正された。下記にあるように、まだ別途となっている。
  2. B-adapt 1がサポートされた。
  3. 全てのB-adaptコードが、重複するのではなく、MB-treeのコードに統合された。B-adapt 1は以前よりも僅かに(MB-tree不使用時でさえ)改善するはず。全般に、全3つのB-adaptモードが同じ基本的フレームワークを使用するようになった。
  4. MB-tree strengthはqcompで調整可能になった。qcompを上げるとMB-tree strengthは低くなる;qcompはMB-tree以前と同じように作用し、qcomp 1が固定フレームQPを意味する。これはちょっとした追加試験になる;デフォルトのqcomp 0.6はpsy(視覚心理)最適な値ではない--より高くあるべきかもしれない。
  5. PSNR、またはSSIMを改善しないようなx264の全てのpsy最適化を無効にする、新しい--no-psyオプション。これにはpsy-RD/trellisを含むが、最近追加されたchromaラムダオフセットと、MB-treeが非scenecutであるIフレームが存在しないかのように振る舞うようになる、魔法の"num_frames = j"の行をも含む。
  6. "Fast"プリセットが遅くなった。間を埋めるため"faster"プリセットを追加。
  7. MB-treeは"fast"以上でデフォルトになった。Lookahead値はより速いプリセットでより小さい値となる。

0.11での変更点

  1. VBVがサポートされた。MB-treeが持つほとんどのVBVの問題を修正する、別途のVBVパッチを含めた;これはMB-treeを要求しないため、別途コミットされる。例えばアニメやCGの様に、静的な内容の多いクリップの1-pass VBV(特にCBR)の性能をも改善するはず。しかしながら、CBRはMB-treeからあまり多くの恩恵を受けない事に注意。特に小さなバッファでは。
  2. いくつもの半端なパラメータの取り扱いを修正。例えばlookaheadが2ndパスで自動的に無効に。

 FAQ

どうやって使うの?

デフォルトで使用される。--no-mbtreeでOFFになる。

併用できないものは?

--b-pyramid。

どうやって調整するの?

--lookahead;デフォルトは50で、より高くすればより多くのメモリを使用し遅くなるが、よりよい結果になるはず。恐らくほとんどのソースには過剰(訳注:デフォルトの50のこと)だろう。lookaheadが埋まった後すぐにクラッシュする場合、恐らくメモリが足りていない。(32- bit Windowsのx264では最大2GB)

--qcomp;以前の、実際のqcompと同様に動作し、より高い値はMB-tree strengthをより低くする。

その対価は?速度?

少々。酷くはない。速度的なコストは--lookaheadに線形比例する。

どうやってテストを手伝えばいい?

使って。

アサートやクラッシュなど、変なエラーや警告が発生しないことを確認して欲しい。関連する設定(b-adapt, bframes, mbtree, lookahead, 1/2-pass, crf, keyint, scenecut)を、私が考えないような様々な組み合わせで試して欲しい。

様々なソースで試して欲しい;mbtree無しの場合と同ビットレートで、--tune ssimを使用し、より悪いSSIM(同様にtune psnrでのGlobal PSNRも)となるポイントがあるか見て欲しい。

視覚的に破綻していないかも確認して欲しい。

また、CRFがこのパッチで再定義されている事に注意--Blackperl(訳注:映画パイレーツ・オブ・カリビアンのクリップ)で以前と同じビットレートになるように設定しているが、違う映像では変わりうる。

実際には何をしているの?

未来のブロックから過去のブロックへの、動きベクタにかかる情報のpropagation(伝播)を追跡する。これは、qcompがシーン(場面)全体ではなく個別のブロックに作用する、qcompのローカライズと捉えることがことができる。

そのため、(現在のx264が行っているように)高度に複雑なシーンで質を下げる代わりに、例えば静的な背景は高質なまま、シーンの複雑な部分の質のみを下げる。また、これは他にも多くの小細工的な影響を含んでおり、幾つかは潜在的にネガティブなものだが、ほとんどはそうではない。

これは全てのビットレートで補助となるが、この67kbpsのアニメクリップのように、これなしでは映像が破綻する様な、驚異的に低いビットレートをも補助する。

このパッチを皆に広め日常的なエンコードに使用させるべき?

いいえ。ここでのコマンドラインオプションは将来的に変わるかも知れず、statsファイル形式やその他のパッチの状況も同様で、このパッチを通常のx264のビルドに入れるものとするのは良いアイディアではない。その上、mbtreeに関係ない何かを壊しているかも知れない。

mbtreeを無効にしても出力が違うのは何故?

mbtreeでよりよい動作をするようB-adapt(全モード)が僅かに変更されており、フレームタイプの決定がいくらか変わっている。

どのくらい良くなりますか?

最大70%のSSIM上昇を得た。

冗談でしょ?

いいえ。

マジで?どーやったら70%も質があがるのよ?

魔法。

OK。君はこの…待て待て、何この新statsファイル、なんでHDDが全部使われてんだ?

ん、そう?きゃはははははは〜

=ここまで日本語訳=


猫科研究所的な解説

猫研的に気になった部分を解説してみる。正確性は一切保証しない。

 使い方の要約

デフォルトでON。OFFにするには--no-mbtreeを指定する。

mbtreeの強度(strength)を指定するために--qcompが流用される。qcompは元々、crfのバランス(CBR的な方向性とCQP的な方向性のバランス)を調整するパラメータとして使用されており、意味合いが似ているので統合された。1にするとCQPとなる点は変わらない。デフォルト値は0.6のままだが、psy(視覚心理)的に最適ではないかも知れないとされているので、調整してみるとよい。

伝播(propagation)範囲を指定するために、--rc-lookaheadが使用される。これはフレーム数を表しており、デフォルト値は40。presetでの値は、fastで30、slowで50、slowerとplaceboで60。最大250までで、keyint_maxより大きい値は無意味なため切り捨てられる。2-passの2nd-passでは自動的に0になる。

他のオプションとの関連

  • 現状で--b-pyramidのみ併用できず、強制的にb-pyramid=OFFになる。
  • presetの変更。これまでのfastがfasterになり(mbtreeはOFF)、fastは定義し直された。新しいfastはデフォルト+ref=2, subme=6, rc-lookahead=30。
  • --no-psyが追加された。一切のpsy処理をまとめてOFFにできる。
  • qcompが1、またはcqp=ONである時、mbtreeはOFFになる。
  • mbtreeがONの時はAQをOFFにしていてもstrength=0の状態でONにされる。
  • (未確認)mbtree=ONの場合、pb-ratioが無効?(後述)

 機能の内容

複数フレームに渡ってMBごとにMV(動きベクタ)の動向を追跡し、その情報を元に、各MBのQP値を変動させる。MVの大きな(動きの大きい)ブロックは高QPで低画質に、MVの小さな(動きの小さい)ブロックは低QPで高画質に保つように動作する。

これだけの説明では、CRFやAQと同じような機能に感じるだろう。その感覚はある意味で正解で、猫科研究所としては、mbtreeをCRFとAQの間を埋める処理だと説明したい。まずは、CRFとAQとmbtreeを比較してみる。

(2009/08/10:削除)以下のCRF/AQ/mbtreeに関する比較の記述は正しくない可能性が高いため、大部分を削除とします。申し訳ありません。「仲間内の備忘録レベルのつもりがちょっと有名になってきてビビった」とも言う…。もっと正確に書けるようになったら書き直します。

CRF vs mbtree

CRFはフレームごとのQP値を判断する機能で、mbtreeのように各MBごとの判断が入ることはない点が異なる。フレームとフレームを比較して、時間軸でQPの割り振りを考えるのがCRFだ。

AQ vs mbtree

AQは、MBごとのQPを判断する点はmbtreeと同じであるが、フレーム間に跨る情報を使用しない点が異なる。CRFで判断されたQPに対し、フレーム内のMBごとの複雑さに応じて、空間軸でQPの割り振りを考えるのがAQだ。

ここでAQの判断基準を複雑さと表現しているのがポイントで、AQではMVで判断しているわけではないac_energyという独自の基準で判断しており、これはブロックに動き補償や諸々の処理が行われた後、量子化が行われる前の係数値から求められる。iブロックや、動き検索が上手くヒットしなかったp/bブロックなどは係数値が大きく、複数範囲に広がるため、このac_energyが大きくなる傾向がある。ただし、ほぼDC成分しかないような場合など、DCT的に単純なブロックではac_energyは小さくなるので、MBが見た目的に複雑であるか(というのも語弊があるが)も重要な点だ。

ac_energyは動き検索がヒットしているか否かによっても変動してくるため、実際には時間軸情報も含んではいるのだが、QPの配分自体はフレーム内で行われる。

mbtreeのメリット

mbtreeは、時間軸の要素を持つMVを基準に使用する点で、CRFと似た処理だ。しかし、空間的にMBごとの判断を行う点では、AQに似た処理だ。この両者の中間的な性質により、それぞれに対するアドバンテージが生まれる

CRFを基準にするならば、1フレームの中で動きの大きな箇所と動きの小さな箇所で画質に差を付けることで、視覚心理的に重要である静止部分の画質を向上させられる。AQを基準にするならば、複雑さという視覚心理的に間接的な基準ではなく、動きというより直接的な基準で画質に差を付けることができる。

例えばあるフレームにおいて、少数のMBが激しい動きをしているが、動き検索は高確率でヒットしているケースを想定しよう。この場合、フレーム全体としては動きが少ないため、CRFはフレームに対し低QP(高画質)を割り振るだろう。そしてAQは、動き検索がヒットおり動くMBの差分情報は単純であるため、静止部分と差がない低QP(高画質)を割り振るだろう。しかし、この場合の理想的なQP配分としては、「動いているMBには高QP(低画質)を割り当てたい」のではないのだろうか。mbtreeはこういったケースで非常に有効に働いてくれるはずだ。

いずれにしても、この3者は将来的に統合されるかも知れない。その目的が、QPを変動させる点にあることは変わらないからだ。

 内部(コードレベル)の話

基本的にはRC1のコードで確認している。

機能の中心はslicetype.cにあり、x264_slicetype_analyseが変更の起点になっている。ここからx264_macroblock_tree→x264_macroblock_tree_propagateと下位の関数が呼び出されていく。

x264_slicetype_analyseの変更は大きいが、mbtreeに固有というよりは、mbtreeをマージする上で都合の良いように変形したら、大きく変えることになったという雰囲気だ。

疑問点

Bフレームは被参照フレームより低いQP(高画質)を使用すべきではない、という考え方は正しいと思うが、これを覆す修正がある。この意図は?

@@ -953,7 +956,7 @@ void x264_ratecontrol_mb( x264_t *h, int bits )
         if( y < h->sps->i_mb_height-1 )
         {
             int i_estimated;
-            int avg_qp = X264_MAX(h->fref0[0]->i_row_qp[y+1], h->fref1[0]->i_row_qp[y+1])
+            int avg_qp = X264_MIN(h->fref0[0]->i_row_qp[y+1], h->fref1[0]->i_row_qp[y+1])
                        + rc->pb_offset * ((h->fenc->i_type == X264_TYPE_BREF) ? 0.5 : 1);
             rc->qpm = X264_MIN(X264_MAX( rc->qp, avg_qp), 51); //avg_qp could go higher than 51 due to pb_offset
             i_estimated = row_bits_so_far(h, y); //FIXME: compute full estimated size

このコードの直前にはmbtree以前から以下のようにコメントが付いている。

    if( h->sh.i_type == SLICE_TYPE_B )
    {
        /* B-frames shouldn't use lower QP than their reference frames.
         * This code is a bit overzealous in limiting B-frame quantizers, but it helps avoid
         * underflows due to the fact that B-frames are not explicitly covered by VBV. */
        if( y < h->sps->i_mb_height-1 )
        {
            int i_estimated;

追記:このコード修正は、VBV修正のr1196側に含まれたようだ。


r1091でなされたpred_b_from_p->coeffをキャップする処理が削除されている。これに加えて、pbratioは恐らく無効になっている。mbtreeがONの場合pから情報を得てbを評価するのではなく、あくまでMV基準だからということなのかも知れないが、 mbtreeがOFFの場合でさえ、削除でよいものだろうか?

@@ -1153,10 +1156,6 @@ void x264_ratecontrol_end( x264_t *h, int bits )
             {
                 update_predictor( rc->pred_b_from_p, qp2qscale(rc->qpa_rc),
                                   h->fref1[h->i_ref1-1]->i_satd, rc->bframe_bits / rc->bframes );
-                /* In some cases, such as completely blank scenes, pred_b_from_p can go nuts */
-                /* Hackily cap the predictor coeff in case this happens. */
-                /* FIXME FIXME FIXME */
-                rc->pred_b_from_p->coeff = X264_MIN( rc->pred_b_from_p->coeff, 10. );
                 rc->bframe_bits = 0;
             }
         }

追記:このコード修正は、VBV修正のr1196側に含まれたようだ。


前項と関連して以下のコードが気になる。

@@ -310,6 +326,14 @@ int x264_ratecontrol_new( x264_t *h )
     else
         rc->fps = 25.0;
 
+    if( h->param.rc.b_mb_tree )
+    {
+        h->param.rc.f_pb_factor = 1;
+        rc->qcompress = 1;
+    }
+    else
+        rc->qcompress = h->param.rc.f_qcompress;
+
     rc->bitrate = h->param.rc.i_bitrate * 1000.;
     rc->rate_tolerance = h->param.rc.f_rate_tolerance;
     rc->nmb = h->mb.i_mb_count;

「魔法の"num_frames = j"の行」とは以下のことを指している。コメントの意味は分かるものの、その実態をまだ理解し切れていない。jは、スライスのタイプが未決定なフレームの数。コメント中の"non-scenecut keyframe"は、シーンカット適用ではないが、keyintの関係でキーフレーム(IDR)になってしまうものを指しており、そこまでの残り枚数が keyint_limitに格納される。本来num_framesはこのkeyint_limitとjの小さい方を採っており、1度の x264_slicetype_analyseの呼び出しでスライスタイプを決定できるフレーム数がnum_framesである。それをjに変更しているということは、IDRを超えてスライスタイプ判定処理を実施していることになる。

+    /* This is important psy-wise: if we have a non-scenecut keyframe,
+     * there will be significant visual artifacts if the frames just before
+     * go down in quality due to being referenced less, despite it being
+     * more RD-optimal. */
+    if( h->param.analyse.b_psy && h->param.rc.b_mb_tree )
+        num_frames = j;

そしてスライスタイプ判定の完了後に、過剰に判定したフレーム分を未決定に戻している。reset_startは、B以外であると判定されたフレームの次のフレームを指している。

+    /* Restore frametypes for all frames that haven't actually been decided yet. */
+    for( j = reset_start; j <= num_frames; j++ )
+        frames[j]->i_type = X264_TYPE_AUTO;

x264_macroblock_treeはスライスタイプ決定の後に呼ばれ、決定したスライスタイプに基づいてBフレームの固まりに分割し、それらに対して後ろから順にx264_slicetype_frame_costとx264_macroblock_tree_propagateを呼んでいる。

最終更新時間:2009年10月27日 07時06分35秒