圧縮の本質:同じ情報をより少ないデータで表現する
可逆圧縮と不可逆圧縮の違いに入る前に、そもそも圧縮とは何をするものかを理解しておく必要があります。
圧縮の核心は、同じ情報をより少ないデータで表現することです。テキスト、画像、音声、動画といったあらゆるデータには「冗長性」が含まれています。冗長性とは、情報の意味を損なわずに削除できる一部を指します。圧縮アルゴリズムの仕事は、この冗長性を特定して排除することです。
簡単な例を考えてみましょう。文字列 AAAAABBBCC は10バイトを占めます。しかし「各文字とそれが連続して繰り返される回数」を記録することで、同じ情報をよりコンパクトに表現でき、5A3B2C となります。わずか6バイトです。データサイズは40%減少しますが、意味はすべて同じです。これが最も原始のな圧縮の形であり、ランレングス符号化(RLE)として知られています。
情報理論の観点から、クロード・シャノンは「情報エントロピー」の概念を導入しました。データストリームの最小平均符号長は、そのエントロピーによって上限が定まります。可逆圧縮アルゴリズムはこの理論的な限界を超えることはできません。つまり、すでに圧縮されたデータを再圧縮てもほとんど効果は得られません。冗長性はすでに取り除かれているからです。
この本質を理解すると、根本のに異なる2つの技術の道筋が見えてきます。可逆圧縮は完全な忠実度を目指し、不可逆圧縮は人間の目や耳が容易に知覚できない細部を意図のに切り捨て、それとはるかに高い圧縮率を引き換えにします。
可逆圧縮:すべてのビットを復元する
可逆圧縮の中核となる約束は、展開されたデータが元のデータとバイト単位で完全に一致することです。1ビットたりとも違ってはいけません。これらのアルゴリズムは「統計の冗長性」と「構造的冗長性」だけを排除し、情報の意味内容には決して触れません。
DEFLATE:最も広く使われている可逆圧縮アルゴリズム
DEFLATEは、ZIP、gzip、zlibなど多くのツールの基盤となるアルゴリズムです。2つの古典のな技術を巧みに組み合わせています。
- LZ77:スライディングウィンドウを使って、すでに処理されたデータ中の繰り返し一部文字列を検索し、「距離+長さ」のポインタに置き換えます。例えば、テキストファイル中に繰り返し現れる「compression」という単語は、最初の出現位置への後方参照に置き換えることができます。
- ハフマン符号化:LZ77が生成したシンボルストリームに可変長符号化を適用します。頻出するシンボルには短い符号が、稀なシンボルには長い符号が割り当てられます。これにより符号化をエントロピー限界に近づけます。
DEFLATEの長所は、そのシンプルさ、速度、そして普遍的な互換性です。ほぼすべてのプログラミング言語の標準ライブラリに実装が含まれています。
LZMA:コンテキストモデリングによるより高い圧縮率
LZMA(Lempel-Ziv-Markov chain Algorithm)は、LZ77を基礎としつつ、はるかに大きなスライディングウィンドウ(最大数GB)、より精緻な確率モデル、レンジ符号化を採用しています。DEFLATEと比較して、LZMAは一般的に高い圧縮率を実現しますが、圧縮速度が遅くメモリ使用量が多くなります。7z形式はLZMAに基づいています。
LZMAの重要な改善点は、静的な確率テーブルに依存しないことです。代わりに、コンテキストに基づいて次のシンボルの確率を動的に予測します。この「コンテキストモデリング」により、符号化を理論的なエントロピー限界にはるかに近づけます。
PNGフィルタリング:画像データの前処理
PNGは可逆画像形式ですが、生のピクセル値に直接DEFLATEを適用するわけではありません。圧縮の前に、PNGは各ピクセル行に「フィルタ」を適用し、データをDEFLATEにとって扱いやすいものに変換します。
一般的なフィルタは、各ピクセルとその左、上、左上の隣接ピクセルとの差分を格納します。自然画像は局所のに類似したピクセルを持つため、これらの差分はゼロ周辺に集中する傾向があります。この変換されたデータはより高い冗長性を持ち、DEFLATEがはるかに効率的に圧縮できるようになります。
これが、ZIPがJPEG画像を効率的に圧縮できない理由も説明しています。JPEGはすでにDCTとエントロピー符号化によって高度に圧縮されており、DEFLATEが利用できる統計の冗長性がほとんど残っていません。JPEGにZIPを実行すると、通常はサイズが削減されず、数バイトのヘッダーオーバーヘッドが追加されるだけです。
不可逆圧縮:知覚の閾値を下回るトレードオフ
不可逆圧縮の中心となる考え方は、人間の知覚には限界があるということです。切り捨てられる細部が知覚の閾値を下回っていれば、視覚の・聴覚のな差異は無視できます。しかしデータ削減は極めて大きくなります。
DCT:JPEGの数学的基盤
離散コサイン変換(DCT)は、JPEG画像圧縮の心臓部です。その役割は、画像を「空間領域」から「周波数領域」へ変換することです。
空間領域では、画像はピクセルの輝度値で表現されます。周波数領域では、画像は異なる周波数のコサイン波成分のととして分解されます。低周波成分は大きな滑らかな領域を表現し、高周波成分はエッジや微細なディテールを表現します。
DCT自体は可逆であり、情報を失いません。実際の「損失」は次のステップである量子化で発生します。
量子化:損失の根源
量子化は、高精度のDCT係数を限られた離散値のセットに対応付けるプロセスです。具体的には、各係数を量子化テーブルの値で割り、整数に丸めます。人間の目は高周波のディテールに対して感度が低いため、量子化テーブルは高周波係数に大きな除数を適用し、その多くをゼロにします。
このステップは不可逆な情報損失を引き起こします。一度係数がゼロに丸められると、復元できません。しかしこれと同時に、圧縮の大きな機会も生み出します。ゼロの長い連続は、ランレングス符号化で非常ににににに効率的に符号化できます。
量子化テーブルの設計は芸術です。除数を大きくすれば圧縮率は高まりますが、品質低下が目立つようになります。これがまさにJPEGの品質パラメータ(例えば quality=80)が制御しているものです。量子化テーブルを拡大縮小しています。
フレーム間予測:動画圧縮の鍵
動画圧縮は静止画像からさらに一歩進みます。動画の連続するフレームはしばしばほとんど変化しません(例えば、動く人物と静止した背景)。そのためすべてのフレームを完全に保存する必要はありません。
フレーム間予測は「動き推定」を用いて、現においてのフレームと参照フレーム間の変化を見つけ出し、「動きベクトル」と「残差」だけを保存します。この時間のな冗長性の排除により、動画の圧縮率は数百倍、甚至は数千倍に達することができ、単一フレームの画像圧縮がまで達できる範囲をはるかに超えます。
可逆 vs 不可逆:比較図
以下のフローチャートは、2つの圧縮ファミリーの主にな分岐と典型的な応用を示しています。
DCTの原理図
以下の図は、DCTが8x8ピクセルブロックを空間領域から周波数領域へ変換し、量子化によって低周波を保持しつつ高周波を切り捨てる様子を簡略化して示しています。
コード例
zlibによる可逆圧縮
import zlib
# 強い反復を含む元のテキストデータ
original = b"compression compression compression " * 50
# 圧縮
compressed = zlib.compress(original, level=9)
print(f"元のサイズ: {len(original)} バイト")
print(f"圧縮後サイズ: {len(compressed)} バイト")
print(f"圧縮率: {len(original) / len(compressed):.2f}x")
# 展開してデータが一致することを検証
decompressed = zlib.decompress(compressed)
assert decompressed == original, "データが一致しません!"
print("検証成功: 展開されたデータは元のデータと同一です")
このコードを実行すると、通常10倍以上の圧縮率が得られ、展開されたデータは元のデータとバイト単位で完全に一致します。これが「可逆」の意味です。
PILによる不可逆圧縮
from PIL import Image
import io
# 高周波ディテールに富むテスト画像を作成
img = Image.new("RGB", (512, 512))
pixels = img.load()
for x in range(512):
for y in range(512):
# 高周波ディテールを持つパターンを生成
pixels[x, y] = ((x * 7) % 256, (y * 5) % 256, ((x + y) * 3) % 256)
# 可逆PNGとして保存
img.save("test_lossless.png")
png_size = len(open("test_lossless.png", "rb").read())
print(f"PNG(可逆)サイズ: {png_size} バイト")
# 効果を強調するため品質20の不可逆JPEGとして保存
buffer = io.BytesIO()
img.save(buffer, format="JPEG", quality=20)
jpeg_size = buffer.tell()
print(f"JPEG(不可逆)サイズ: {jpeg_size} バイト")
print(f"圧縮率: {png_size / jpeg_size:.2f}x")
# JPEGを再読み込みしてピクセルの差異を比較
buffer.seek(0)
img_jpeg = Image.open(buffer)
diff_count = sum(
1 for x in range(512) for y in range(512)
if img.getpixel((x, y)) != img_jpeg.getpixel((x, y))
)
print(f"変化したピクセル数: {diff_count} / {512 * 512}")
print("結論: JPEG圧縮はピクセル値を不可逆的に変更します")
このコードは、不可逆圧縮の特徴を示しています。ファイルはより小さいですが、ピクセル値が不可逆的に変更されています。
比較表
| 次元 | 可逆圧縮 | 不可逆圧縮 |
|---|---|---|
| コア原理 | 統計の・構造的冗長性を排除 | 知覚の閾値を下回る情報を切り捨て |
| 情報忠実度 | 100%可逆、展開後に完全一致 | 不可逆、回復できない損失が存において |
| 代表のなアルゴリズム | DEFLATE, LZMA, Brotli, Zstandard | DCT+量子化, ウェーブレット変換, MDCT |
| 主に技術 | LZ77, ハフマン符号化, レンジ符号化 | 変換符号化, 量子化, 予測符号化 |
| 典型的な応用 | ZIP, GZIP, 7Z, PNG, FLAC | JPEG, WebP, H.264, MP3, AAC |
| 最適な用途 | テキスト、ソースコード、アーカイブ、医療画像 | 写真、動画、音楽、ストリーミング |
| 典型的な圧縮率 | 通常2倍〜5倍 | 通常10倍〜100倍以上 |
| 不適切な用途 | すでに圧縮済みのデータ(例:JPEGファイル) | 正確な再現が必要なデータ(例:法律文書) |
よくある質問
Q1:なぜZIPはJPEG画像を圧縮できないのですか?
JPEGが作成される時点で、すでにDCT変換、量子化、エントロピー符号化を経ています。データ内の統計の冗長性はほぼ完全に排除されています。DEFLATEは利用できる反復パターンを見つけられないため、JPEGにZIPを実行してもサイズが削減されないばかりか、数バイトのヘッダーオーバーヘッドが追加されることすらあります。
Q2:同じ画像で、PNGは常にJPEGより大きいですか?
必ずしもそうではありません。単色の広い領域や限られたパレットを持つ画像(ロゴ、アイコン、スクリーンショットなど)では、PNGのフィルタ+DEFLATEの組み合わせがJPEGより小さくなることが多く、完全な品質も保たれます。しかし色豊かな自然写真では、JPEGのDCT+量子化の組み合わせが通常10倍以上の圧縮率を達成し、PNGよりはるかに小さくなります。
Q3:同じファイルを可逆圧縮アルゴリズムで繰り返し圧縮できますか?
できません。可逆圧縮の各パスは、データをエントロピー限界に近づけます。最初のパスの後、冗長性はほぼなくなり、2回目のパスにはもはや利用すべきものがほとんど残っていません。追加されるメタデータの使いでわずかに大きくなることすらあります。だからこそ「すでに圧縮されたファイルを圧縮する」ことは無意味なのです。
まとめ
可逆圧縮と不可逆圧縮は対立する陣営ではなく、異なる情報特性に対して選ばれた最適な選択です。
- 可逆圧縮は「1ビットの誤りも許されない」データ(テキスト、コード、アーカイブ)に対応します。LZ77やハフマン符号化などの技術を用いて統計の冗長性を排除しつつ、完全な再構築を保証します。
- 不可逆圧縮は「目や耳が知覚できない」細部(写真、動画、音楽)に対応します。DCT、量子化、フレーム間予測を用いて知覚の閾値を下回るトレードオフを行い、制御された情報損失と引き換えに圧縮率を桁違いに改善します。
これら2つの道の根本のな違いを理解することは、すべての圧縮技術を習得するための基盤です。後続の記事では、それぞれの道に沿った具体的なアルゴリズムの実装を掘り下げ、圧縮の精妙さを一行ずつ理解していきます。
ファイル圧縮をお探しですか?SmartSlim をお試しください
独自開発のRust圧縮エンジンに基づき、PDF/画像/動画/Office/OFDなど10大分類40以上の形式をサポート。ローカル圧縮でデータは外部に持ち出されません。