JPEGはなぜ劣化するのか — DCTと量子化を1から
写真が10分の1になるとき、何が捨てられているのか。色空間変換・8×8ブロック・DCT・量子化テーブル・ジグザグ走査を順に追い、「品質90」という数字の正体と、ブロックノイズ/モスキートノイズが生まれる場所まで降りていきます。最後は、画像を扱う人が実際に決めることへ落とします。
比喩: 捨てる前に、並べ替える
引っ越しで荷物を半分にするとき、目をつぶって箱を捨てる人はいません。まず「毎日使う/たまに使う/たぶん一生使わない」の順に並べ替え、後ろから捨てます。捨てる量が同じでも、並べ替えてから捨てるほうが痛みは桁違いに小さい。
JPEGはこれと寸分違わぬことをします。画像を「人の目が敏感な成分」から「鈍い成分」の順に並べ替え、後ろから捨てる。並べ替える道具がDCT、捨てる操作が量子化です。
全体像: 工程は5つしかない
- 色空間変換 — RGBを、明るさ1本と色2本(YCbCr)に組み替える
- クロマサブサンプリング — 色の2本だけ解像度を落とす
- 8×8ブロックへの分割とDCT — 各ブロックを「波の強さ」の並びに書き換える
- 量子化 — 波の強さを粗い目盛りに丸める
- ジグザグ走査とエントロピー符号化 — 丸めた結果を可逆に詰める
このうち非可逆なのは2と4だけです。1・3・5は原理的に元へ戻せます。「JPEGの劣化」と呼ばれる現象は、全部この2箇所から出てきます。
明るさと色を分ける
人間の視覚は、明るさの変化には鋭く、色そのものの変化には鈍い。この非対称性を使うため、JPEGはまずRGBを (輝度)、・(青系・赤系の色差)に組み替えます。
やっていることを日常の言葉にすると、ある画素の赤・緑・青の値を、それぞれ「その色が人にどれくらい明るく見えるか」で重み付けして足しているだけです。出てきた が「この画素はどれくらい明るいか」を表す1つの数になります。
この式が言っているのは要するに、緑がいちばん明るく見え、青がいちばん暗く見えるという人間の感度をそのまま重みにした、ということです。色差は 、、つまり「明るさを引いた残りの色味」。YCbCrに移すと情報が に集中し、 は0付近に寄ります。
クロマサブサンプリング: 色の解像度を半分にする
次にJPEGは と の画素数だけを間引きます。表記は 4:2:0(縦横とも半分=色の画素数が4分の1)、4:2:2(横だけ半分)、4:4:4(間引かない)。
4:2:0 なら1画素あたりのデータ量は が1、 が合わせて0.5で計1.5バイト相当。RGBの3バイトに対して半分です。DCTにも量子化にも触れていないのに、もう半分。風景写真ではまず気付けませんが、赤い細線や色付きの文字では色の輪郭が滲みます。
8×8ブロックとDCT
各成分は8×8画素のブロックに切り分けられ、以降は完全にブロック単位で、隣を一切参照せずに処理されます。この独立性が、後でブロックノイズの原因になります。
各ブロックに掛けるのが離散コサイン変換(DCT)です。名前は難しそうですが、やっていることは1つ。
つまり、64種類の縞模様を1つ持ってきてブロックに重ね、「このブロックはこの縞にどれくらい似ているか」を測って答えを書き留める、という作業です。 と はどの縞を選んだか(横方向・縦方向にそれぞれ何本の縞か)、二重の総和が「どれくらい似ているか」をブロックの64画素ぶん合計する部分にあたります。これを64種類ぶん繰り返せば変換は終わりです。
は元の画素値、 が変換後の係数、 で他は1です。式の見た目に怯む必要はありません。64個の画素値を、64種類の「縞模様」の強さに書き換えているだけです。
その64種類(基底)は、左上が「一様な塗り」、右へ行くほど横縞が細かく、下へ行くほど縦縞が細かくなります。左上の はDC係数と呼ばれ、そのブロックの平均的な明るさそのもの。残り63個がAC係数で、細かい変化の量を表します。
ここが肝心です。DCTは情報を1ビットも捨てていません。64個の数を別の64個に書き換えただけで、逆変換すれば完全に元へ戻ります(線形代数の記事でいう直交変換=基底の取り替えです)。ではなぜやるのか。自然画像ではエネルギーが左上のごく少数の係数に集中するからです。滑らかな部分が多い写真では、右下の高周波係数はほぼ0に近い。並べ替えの結果「捨ててよいもの」が右下にまとまる——これがDCTの唯一にして最大の仕事です。
コメント
コメントにはログインが必要です