yunomuのブログ

趣味のこと

JPEGを読む(11)(終) 画像の成分を組み立てる

 最終回です。

RGBとYCbCr

 JPEGの色空間はYCbCrだけどコンピュータの画面はRGBなので変換する必要がある。YCbCrをRGBに変換するには、単純な行列のかけ算で計算できる。

YUV - Wikipedia

 このうち、Goのimage/colorモジュールではITU-R BT.601を採用している。

color package - image/color - Go Packages

 これに従って変換してもいいけれど、GoのimageモジュールにはYCbCrというデータ構造があるので、レンダリングまで自分でやるわけでなければ単に成分ごとにYCbCr構造体に値を入れればよい。

サンプリング係数に基づいた画素の復元

 サンプリング係数については前に出てきたとおり。

JPEGを読む(7) コンポーネント、サンプリング係数、MCUなど - yunomuのブログ

 Y成分に関しては画素の数だけサンプル値があるので問題無いが、Cb成分とCr成分については間引かれていることが多い。今回利用した画像でも、それぞれ縦と横に1/2ずつ圧縮されていた。これを復元するためには単に縦と横に2倍にしてやればよい。つまり1つのサンプル値を4画素の値として使うということになる。

 けれどこれもGoのimageモジュールのYCbCr構造体を使うとそんな面倒なことは必要なく、NewYCbCrの時に適切なSubsampleRatioを設定してあげれば、それぞれの成分値のスライスを適切なサイズで作っておいてくれるので、あとは値を設定するだけでよい。

おわり

 自分でレンダリングする、またはその気分を味わいたい場合はYCbCr構造体ではなくNRGBA構造体などを使って書いてみても結構楽しい。

 というわけで後半は面倒で図もコードもなくなったけど、ひとまずおわりです。以下は今回手をつけなかったところ。やってもいいけど、普通に仕様を読めばわかるようになったのでもうやる気が無い。

  • 階層構造
  • 算術符号化
  • 可逆変換
  • 画像の縦横のサイズが16×n+a(nは整数, 1≦a≦8)の時どうするの?(これはたぶん仕様に書いてなかった)

おまけ: テストに使った画像とデバッグの日々

 テスト用の画像は最初はダウンロードフォルダにあった適当な画像を使っていたのだけど、ちょっとテストとしてよくなかった(真っ白な部分が多かった)ので、有名なlenna.jpgを再エンコードして単純化したものを使ったんですが(Goのimageモジュールでデコードしてエンコードすると構造が単純になる)、こうやって遊んでいるうちに「サンプル画像としてLennaを使った論文は受理しない」という声明が出たりしてましたね。

 途中の工程を飛ばしたり間違えたりしてめちゃくちゃになった画像が面白かったので紹介。

計算ミスしてる上にレベルシフトを忘れているけど画素の位置は合っている

計算ミスの一部を直したので少しマシになっている

レベルシフトしたけど差分エンコードのリセット周期を間違えてたやつ

 実際はこの画像ではなくて輝度(Y)成分だけの黄色い画像で確認してたんですけど、間違いが目に見える分だけわかりやすくて良かったですね。最終的な画像として出てくるまでのDCTだのハフマン符号テーブルだのが合っているのかどうかなんてわかりにくすぎて、何通りも実装してテストするしか無くて面倒だったし、結局のところそのあたりには全くバグは無かった。yvを書き間違えていたのは3枚目の画像の時でした。これを見つけるのに1ヶ月かかった…