|TNKS1407 解説
← 解説一覧へ

写真が小さくなる仕組み ── JPEGは、何を捨てているか

2026.06.25

JPEGは“捨てて”小さくする圧縮、PNGは“捨てずに”小さくする圧縮。写真の8×8ブロックをDCTで波に分解し、細かい波を量子化で捨てる ── その計算を一歩ずつ数字で追い、崩れ(ブロック・リンギング)がどこから来るのかを、触れる形で開ける。

▶ 画像フォーマットを開けるラボを全画面で開く

スマホで撮った写真、よく見ると一枚で何百万画素もある。なのにファイルは数MBで済んでいる。── その「小さくする」をやっているのが、画像の圧縮だ。今日は、その箱を開けてみる。

まず、圧縮しないとどれくらい大きいのか。横 WW・縦 HH ピクセルの写真は、各ピクセルが赤・緑・青の3つの数を持つから、素のサイズは

素のサイズ=W×H×3 バイト\text{素のサイズ} = W \times H \times 3\ \text{バイト}

たとえば 4000×30004000\times3000 の写真なら、それだけで 36003600 万バイト=約 36MB。これをそのまま持ち歩くのは重い。だから縮める。縮め方には、性格の違う 二つの道 がある。

二つの道 ── 捨てる圧縮と、捨てない圧縮

「PNG って何?」の答えは、ここにある ── JPEG が“画質”というつまみを持つのに対し、PNG にはそれが無い。捨てないから、画質を選ぶ余地がそもそも無いんだ。そのぶん写真ではファイルが大きくなりがちだけど、一文字も劣化しない。

下のラボで、まず JPEG の「捨てる」を体で見てほしい。「JPEG 画質」のつまみを下げると、写真が崩れていく ── と同時に、サイズが激減する。PNG(無劣化)のサイズと並べてあるから、二つの道の違いがそのまま数字で出る。

崩れを下げきると、ファイルは元の数%まで縮む。「差分(崩れ)」ボタンを押すと、元と JPEG の違いだけが白く浮かぶ ── JPEG が どこで嘘をついたか が、はっきり見える。ブロックの境目、円や文字の縁。なぜそこなのか。それは、JPEG の中の計算を開けると分かる。

なぜ“捨てて”いいのか

その前に、ひとつ。なぜ捨てて平気なのか。── 人の目は、広いなだらかな明暗には敏感だけど、細かい急な変化には鈍い。空のグラデーションのわずかな段差はすぐ気づくのに、髪の毛一本の微妙なコントラストは見落とす。JPEG は、この「目の鈍いところ」を狙って削る。だから、うまくやれば、捨てたことに気づかれにくい。捨てるけれど、目に分からない範囲で捨てる ── それが JPEG の賭けだよ。

問題は、「細かい変化」と「なだらかな変化」を、どうやって分けて、細かいほうだけ捨てるか。そこで出てくるのが 波への分解(DCT) だ。

JPEG の中身を、一歩ずつ開ける

JPEG は、写真を 8×8 ピクセルの小さなブロック に切り分けて、ブロックごとに次の手順を踏む。

① 波に分解する(DCT)。 8×8 の明暗 f(x,y)f(x,y) を、「縞模様の波」の重ね合わせとして書き直す。各波の強さ F(u,v)F(u,v) は、

F(u,v)=14C(u)C(v)x=07y=07f(x,y)cos ⁣(2x+1)uπ16cos ⁣(2y+1)vπ16F(u,v)=\frac14\,C(u)\,C(v)\sum_{x=0}^{7}\sum_{y=0}^{7} f(x,y)\,\cos\!\frac{(2x+1)u\pi}{16}\,\cos\!\frac{(2y+1)v\pi}{16}

(C(0)=12,C(k)=1 (k1))\Big(\,C(0)=\tfrac{1}{\sqrt2},\quad C(k)=1\ (k\ge 1)\,\Big)

身構えなくていい。やっていることは「このブロックには、どの縞の波が、どれだけ入っているか」を測っているだけだ。cos\cos の中の u,vu, v が大きいほど、細かく波打つ縞になる。だから F(0,0)F(0,0) は波打たない=ブロックの平均の明るさ(DC 成分)u,vu,v が大きい右下ほど、細かい縞の強さを表す。離散コサイン変換(Discrete Cosine Transform)と呼ぶけれど、心はフーリエと同じ ── 絵を、波の足し算に開く ことだ。

② 細かい波を捨てる(量子化)。 ここが圧縮の心臓だ。各波の強さ F(u,v)F(u,v) を、量子化表 Q(u,v)Q(u,v) という決まった数で割って、四捨五入する:

B(u,v)=round ⁣(F(u,v)Q(u,v))B(u,v)=\operatorname{round}\!\left(\frac{F(u,v)}{Q(u,v)}\right)

肝は、Q(u,v)Q(u,v)細かい波(右下)ほど大きい こと。大きい数で割って丸めれば、もともと小さい細かい波の強さは、ぱたぱたと 0 になる。0 になった波は、もう記録しない ── これが「捨てる」の正体だ。画質のつまみは、この QQ を全体に大きく/小さくスケールしているだけ。画質を下げる=QQ を大きくする=もっと 0 にする、ということ。

③ 戻す(逆DCT)。 受け取った側は、残った(0 でない)波だけを足し合わせて、ブロックを描き直す。細かい波を捨てたぶん、元とは少しズレる。そのズレが、劣化だ。

下のラボの「中の計算を見る」モードで、この①〜④を、本物の 8×8 ブロックの数字で一歩ずつ追える。②で平均(DC)が大きく出て、③で細かい波の多くが 0 に落ちる ── その瞬間が、目で見える。

image-formats / 中の計算全画面で開く ↗

面白いのは、画質を高め(Q が小さめ)にしても、けっこうな数の係数が 0 になる こと。それでも見た目はほとんど変わらない ── 人の目が、その細かい波を見ていないからだ。JPEG は、目に見えないところを、堂々と捨てている。

そして、さっき差分で見えた「崩れがブロックの境目・縁に集中する」理由も、これで分かる。ブロックごとに別々に波を捨てるから、ブロックの継ぎ目で段差ができる(ブロックノイズ)。鋭い縁は、本来たくさんの細かい波が要るのに、それを捨てるから、縁の周りに さざ波(リンギング) が残る。文字の周りのちらつき(モスキートノイズ)も、同じ理由だ。

PNG ── 捨てない道

いっぽう PNG は、一切捨てない。やることは二段だ。① 予測:各ピクセルを、すぐ隣(左や上)のピクセルから予測して、実際との差だけ を残す。なめらかな絵なら隣とほぼ同じだから、差は 0 ばかりになる。② 可逆圧縮(DEFLATE):その「0 だらけの差の列」を、 zip と同じ仕組みで畳む。0 が続くほど、よく縮む。

捨てていないから、復元は完全に元どおり。さらに PNG は 透過(α チャンネル) を持てる。だから ──

ラボの数字でいうと、写真っぽい合成シーンでは「低画質 JPEG < PNG < 無圧縮」という並びになる。PNG は無劣化なのに、低画質 JPEG より大きい ── その代わり、一画素も嘘をついていない。

まとめ

同じ「小さくする」でも、二つの道がある。JPEG は、目に分からない範囲で細かい波を捨てて、うんと小さくする道(写真向き)。PNG は、何も捨てずに無駄だけ畳む道(線画・文字・透過向き)。どちらが正解ということはなくて、何を運びたいか で選ぶ。

JPEG は確かに嘘をつく。でも、つく場所(細かい波)と量(量子化表)を、人の目に合わせて選んでいる ── どこを捨てたかは、差分を見ればいつでも暴ける。捨てたことを隠さずに済む、というのが、私はけっこう好きなんだ。