YUV_420_888

12月11日水曜日、晴れ。ときどき雨

会社を出たら降っていた。ええー、聞いてないよ!

* * *

これは画像処理だ。昨日そうおもったので学生時代の(20年以上前の)教科書をひっぱりだして職場に持っていく。残念なことに肝心のところは対象外だった。結局行き帰りに一冊分の重みを加えただけだったという悲しみ。

* * *

Android カメラから取り込む画像データ。

生データは YUV_420_888 という形式で表現されているらしい。輝度成分 Y のプレーン #0、青色信号差分 U のプレーン #1、赤色信号差分 V のプレーン #2。
輝度は画像サイズそのままであることに対して U と V は1ピクセルおき、1ラインおきにサンプリングされている。つまり輝度 2 x 2 に対して 1 サンプル。 Y の 4 ピクセル分に対して U/V が奇数ラインに 2 ピクセル、偶数ラインでは 0 ピクセル分ということで 4、 2、 0 という数字がついているようだ。

人の視覚は色味よりも輝度に対して鋭く働くので、だから輝度成分を重視する──あるいは色情報を落としてデータ量を少なくする。(RGB の3プレーンで表現した場合とくらべてデータ量は半分で済む)

そして YUV_420_888 のプレーン情報。各プレーンはそれぞれ 8 ビット(1バイト)幅でピクセルデータを記録している。これは ByteBuffer に詰められており、 pixel stride と row stride という補足がつく。
1ラインが何バイトになるかを row stride があらわしていて、ライン上のピクセルを何バイトおきに見るかを pixel stride があらわす。

プレーン #0 は pixel stride 1、 row stride は画像幅に等しくなり、プレーン #1 と #2 はカメラによって pixel stride が 2、 row stride が画像幅になる場合と、 pixel stride 1 で row stride が画像幅の半分になる場合とがあるみたいだ。(前者の場合 UV がインターリーブされて格納されている同じバッファを 1 ピクセル分ずらして #1 と #2 が参照しているとか)

* * *

640 x 480 の画像に対してプレーン #0 は 707200 バイト(= 640 x 480)、しかしてプレーン #1 と #2 が 153599 バイトと半端な数字になっていた。
76800(= 320 x 240)でなく、この2倍の 163600 よりひとつだけ小さい。

いったいどういうことかと頭を悩ませたのだけれど pixel stride が 2 で row stride が 640 になっていて納得。
UV は V と U の順でインターリーブされているようで、163600 バイトのバッファーの先頭をプレーン #2 が、バッファー先頭+1バイト目をプレーン #1 が参照していると、そういうことだった。

この記事が気に入ったらサポートをしてみませんか?