たまりば

パソコン・インターネット パソコン・インターネット三鷹市 三鷹市

JPEG圧縮を繰り返しても際限なく劣化するわけではない
2017年02月10日 01:47

ビデオテープのダビングやコピー機でのコピーは世代を重ねるごとに劣化が蓄積する。
同様にJPEG画像も、保存→開く→保存…と繰り返すと、
JPEG劣化_オリジナル
オリジナル
JPEG劣化_ずらし_1世代目
保存1回目
JPEG劣化_ずらし_9世代目
保存9回目
JPEG劣化_ずらし_17世代目
保存17回目
JPEG劣化_ずらし_49世代目
保存49回目
JPEG劣化_ずらし_97世代目
保存97回目
JPEG劣化_ずらし_201世代目
保存201回目
JPEG劣化_ずらし_497世代目
保存497回目
JPEG劣化_ずらし_1001世代目
保存1001回目
JPEG劣化_ずらし_2001世代目
保存2001回目
JPEG劣化_ずらし_5001世代目
保存5001回目
JPEG劣化_ずらし_10001世代目
保存10001回目
といった具合に劣化が蓄積して画質が際限なく落ちていき、最後には一面のノイズになってしまう…

…と誤った認識を持っている人が多い。

実際に試してみよう。使用するソフトは定番のImageMagick。
magick convert -sampling-factor 1x1 -quality 95 orig.png gen1.jpg
magick convert -sampling-factor 1x1 -quality 95 gen1.jpg gen2.jpg
magick convert -sampling-factor 1x1 -quality 95 gen2.jpg gen3.jpg
このようなバッチファイルをExcelとテキストエディタで作成し、実行する。
結果は次のとおり。
JPEG劣化_オリジナル
オリジナル
JPEG劣化_1世代目
保存1回目
JPEG劣化_17世代目
保存17回目

以上だ。
3000回くらい回していたのだが、確認してみると18回目で既に前回と同一の画像が出力されていた。だいぶ時間を無駄にした。
このように、繰り返し保存による劣化は早々に止まることが分かる。
なお今回は1種類に収束したが、以前試した時は3種類のローテーションになったこともあった。
また、見て分かる(分からない)とおり、1回目と17回目で差はほとんど無い。
念のため差分をとってみよう。
JPEG劣化_差分_1-17
全く見えないので64倍に強調したものも置いておく。
JPEG劣化_差分_1-17(×64)

そもそも非可逆圧縮の原理は、元データを少々変えて「圧縮しやすいデータ」を作り、それを可逆圧縮するものと解釈できる。
よって一度非可逆圧縮したデータを展開したデータは、既に「圧縮しやすいデータ」になっているため、同じ設定で圧縮すれば完全に可逆圧縮になってもおかしくない。
むしろ17回まで劣化が続くことの方が不思議といえる。
ではなぜかだが、JPEGの圧縮の根幹である離散コサイン変換(DCT)は本来は可逆なのだが、実際には有限の精度で計算するために丸め誤差が発生する。おそらくこれがJPEG再圧縮が不可逆な原因だと思う。
またどうもJPEGの仕様はあまり厳密でないようで、エンコードでは演算の精度をソフトの自由で選べたり、デコードもソフトによって結果が違ったりする。これも一因かもしれない。
(2/11追記)RGB→YUVの変換も丸め誤差が発生するので一因かもしれない。

さて次にパラメータを少し変えてみる。
JPEG劣化_オリジナル
オリジナル
JPEG劣化_サンプリング2×2_1世代目
1回目
JPEG劣化_サンプリング2×2_10世代目
10回目
JPEG劣化_サンプリング2×2_20世代目
20回目
JPEG劣化_サンプリング2×2_50世代目
50回目
JPEG劣化_サンプリング2×2_103世代目
103回目

今度は劣化が103回目まで続く。劣化の度合いも目に見えて分かる。色が褪せていて、特に赤が大幅にくすんでしまっている。
差分をとってみる。
JPEG劣化_サンプリング2×2_差分_1-103
少し見にくいので4倍に強調しておこう。
JPEG劣化_サンプリング2×2_差分_1-103(×4)
闇夜に光る眼のようで可愛い。

何を変えたかというと、カラーサブサンプリングだ。
カラーサブサンプリングは、クロマサブサンプリングや色成分間引きと呼ばれたり、単にサンプリングやサンプリング比という名の設定項目で設定したりする。名前が定まっておらず面倒な概念だ。
先の17回で劣化が止まったものは、カラーサブサンプリングがOff、別の言い方をするとサンプリングが1x1や4:4:4と呼ばれるものだ。この設定では輝度成分と色成分を元の解像度のまま処理する。この時、画像は8×8のブロックごとに処理され、隣のブロックとの間で影響を及ぼすことはない。
一方103回の方は、カラーサブサンプリングがOn、サンプリングが2x2や4:2:0と呼ばれる設定であり、輝度成分は元解像度で、色成分は縦横1/2に縮小してから処理をする。
この縮小で単純に2×2ピクセルを平均するのであれば16×16のブロック内に影響が留まるだろうが、どうもそうではなく線形補間か何かをされているようで、ドットの色が16×16pxの境界を越えてにじむ現象が見られる。これにより隣のブロックの色が圧縮に影響し、次回は隣の隣のブロックにまで波及し、と際限なく影響を与え合う。厳密なことは分からないがこれが劣化が長く続く理由ではないかと思う。

なので劣化が蓄積するというのは、カラーサブサンプリングOnの設定においてはある程度正しい。
ただしそれにしても際限なく劣化するということはないし、JPEGが全てそのように劣化するわけでもないことは上で示したとおりだ。

さてもうひとつ、冒頭の例のように完全なノイズにまで際限なく劣化させるにはどうすればよいだろう。劣化しそうな方法をいろいろ試してみた。
まず圧縮率を毎回変えたら量子化の閾値が変わることで劣化が続くのではないかと考えたのだが、駄目であった。1万回繰り返してもほぼ劣化が見えなかった。(同じ画像が続くことは無かったので何パターンかのローテーションになっていたようだ)
次に90度回転を試したがこれも同様であった。今回使ったJPEGの量子化行列は縦と横で対称でないのでそれによるゆらぎを期待したのだが…。
そこで毎回画像の位置をずらしてみることにした。7回右下へ1pxずらしたあと、1回左上に7pxずらして戻すの繰り返し。これは見事に成功、これでできたのが冒頭の画像だ。
なぜこれが上手くいくかは、正確なことは分からないが、特定のドットに注目するとそのドットは8×8のブロック内で位置が毎回変わるようになっており、これで誤差が波及し続けるのが一因だろう。
サンプリング2x2の時は止まってしまったのでそれ以外に何かあるのだろうが、よく分からない。何か誤差が発振するような機構があるのだろうか…。
なお90度回転も、画像サイズが8×8の倍数でない場合はずらすのと同じことになる。

以上、まとめると
・単純に同じ設定で圧縮を繰り返すだけでは際限なく劣化することはない。
・ただしサンプリングを落とした場合は少々劣化が続く。
・設定を変えても際限なく劣化はしてくれない。
・位置の移動を伴うと際限なく劣化した。

JPEGの劣化は奥が深い。