読者です 読者をやめる 読者になる 読者になる

「なぜ iPhone の画像は Android の画像よりもずっと高品質なのか」という記事について

qiita.com

 

「なぜ iPhone の画像は Android の画像よりもずっと高品質なのか」という記事。

タイトルをよむと、品質という言葉が使われているので、いわゆる JPEG の (不可逆圧縮部分による) 画質の話のように読めますが、記事の内容を読むと画質の話ではなく「ファイルサイズ」の話でした。

 

libjpeg の optimize_coding とは

 

記事中では、「Android の libskia では、libjpeg の optimize_coding が常に FALSE なのが問題だ」と書いています。

記事中にも引用がありますが、libjpeg のドキュメントでは optimize_coding は以下のように説明されています。

boolean optimize_coding
    TRUE causes the compressor to compute optimal Huffman coding tables
    for the image.  This requires an extra pass over the data and
    therefore costs a good deal of space and time.  The default is
    FALSE, which tells the compressor to use the supplied or default
    Huffman tables.  In most cases optimal tables save only a few percent
    of file size compared to the default tables.  Note that when this is
    TRUE, you need not supply Huffman tables at all, and any you do
    supply will be overwritten.

 既にこの説明の時点で「In most cases optimal tables save only a few percent of file size compared to the default tables.」=「ほとんどのケースでは最適(ハフマン符号木)テーブルは、デフォルトテーブルに比べてほんの数パーセントのファイルサイズを節約するだけ」と書かれてますので、画質の話ではないということが分かるんですけれど、ちょっと詳しく見ていきます。

 

まず、ハフマン符号化というのは、可逆圧縮のひとつで、JPEG の圧縮においては、

  1. 離散コサイン変換して (情報量は変わらず)
  2. 量子化テーブルを使って量子化して (ここで情報が丸められるので情報量がごっそり減るが情報が劣化=画質が劣化する)
  3. 量子化後のデータをさらにハフマン符号化、(ゼロ)ランレングス符号化して可逆圧縮する

という手順の、最後の部分です。

 

解説はこちらが分かり易いと思います。

データ圧縮の基礎『ハフマン符号化』の仕組みを見てみよう - 道すがら講堂

 

とにかくハフマン符号化つまり「可逆圧縮」に関するオプションなので、ここで画質が劣化する=情報が失われることはありえず、圧縮率=最終的なファイルサイズにしか関係ない。(画質うんぬんは量子化の部分で決まる)

 

で、TRUE causes the compressor to compute optimal Huffman coding tables for the image. と書いてあるので、TRUE にすると、このハフマン符号化に使うハフマン符号木を、libjpeg 側で画像に最適なものを計算するということになります。

 

FALSE の場合は libjpeg がデフォルトで持ってるテーブルか、JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS] というポインタにセットしたテーブルを使うことになる。(libskia でこのポインタにさわれるかどうかは知らない)

 

記事中では「optimize_coding をTRUEに設定すると、より多くのディスクスペースと時間を要するために、デフォルト値はFALSEとされていることがわかります」と書かれているけど、ここで "ディスクスペース" と訳すとなんか結果の JPEG ファイルのサイズのことに思えるけど、そうじゃなくて、画像データを頭からスキャンしてハフマン符号木を構築するためのメモリスペースと計算コストが "たんまり" かかるよということ。(This requires an extra pass over the data and therefore costs a good deal of space and time.)

 

細かいことを言うと、その構築したハフマン符号木のデータが、復号のために結果の JPEG ファイルに追加されるので、その分のファイルサイズは大きくはなるけど、画像全体のデータに比べるとたいした量ではない。

 

つまり、TRUE にすると

  • libjpeg 側で画像に最適なハフマン符号木を計算するので、ハフマン符号化の圧縮率が良くなって、JPEG ファイルのサイズを小さくできるよ
  • でもハフマン符号木を計算するための処理コストが結構かかるのと、あと JPEG ファイルに計算したハフマン符号木を埋め込むのでちょっとだけファイルサイズが大きくなるよ

ということです。

 

Android の libskia ではこれが常に FALSE なので、元の libjpeg のハフマン符号化テーブルのポインタにアクセスできないとすれば、常にデフォルトのハフマン符号化テーブルが使われることになるけれど、いずれにしてもこれは「ハフマン符号化の圧縮率」の話なので画質は関係ない。再三いうけれど画質はハフマン符号化以前の量子化の部分で決まります。

 

仮に、「FacebookTwitterInstagram 等どれを使っていても、写真をとって、フィルタをかけて、ソーシャルネットワーク上に公開すると、いつも Android から投稿される写真は画質が劣化しています。」というのが事実だとすれば (検証してないので分からない)、optimize_coding そのものが原因なのではなく、何か別の話じゃないかと思います。それが何なのかは分からないけど。RGBからYCbCrへの変換処理がヘボいとか、量子化の処理がヘボいとかなのかもしれません。

 

こちらからは以上です。