PNG最適化の最前線 2025 — パレット化と無損失圧縮の実務
公開: 2025年9月19日 · 読了目安: 2 分 · 著者: Unified Image Tools 編集部
はじめに
UIやロゴ、アイコンの多くは依然としてPNGが有利です。ここではパレット化と無損失圧縮の手順をまとめ、最終サイズを安定的に削るワークフローを紹介します。JPEG/WebP/AVIFとの使い分けは フォーマット変換の戦略 2025 — WebP/AVIF/JPEG/PNG を使い分ける指針 を参照。
判断基準
- 透過が必要か? → PNG/ロスレスWebP
- 色数は少ないか? → パレット化(8-bit以下)で大幅削減
- 細いエッジ/文字があるか? → 無損失優先、過度な前処理は避ける
実務フロー
- 元データを sRGB 正規化(詳細は 正しいカラー管理とICCプロファイル戦略 2025 ─ Web画像の色再現を安定させる実践ガイド)。
- パレット化で色を削減。
- 冗長チャンクを削除(EXIF等)。
- 無損失圧縮を実施。
単発なら PNG ロスレス最適化、バッチなら バッチ最適化Plus を推奨。
注意点
- 文字エッジのにじみ:前処理のぼかしは厳禁。
- 色の破綻:パレット色数を段階的に調整。レビュには 画像比較スライダー。
どこまで色を削るか(しきい値)
- ロゴ/アイコン: 8–32 色でも成立することが多い。アンチエイリアス境界の踏み抜きに注意。
- UI スクリーンショット: 64–128 色のパレット化で目視等価になりやすい。
- 写真風イラスト: PNG のままは非推奨。WebP ロスレス/有損、AVIF を検討。
アルファ付き(透過)で縁がギザつくときは、輪郭に 0.5–1px の描画上の余白(同色アウトライン)を追加すると崩れにくくなります。
CLI レシピ(無損失系)
# oxipng: 速度と圧縮のバランスが良い
oxipng -o 4 --strip all input.png -o output.png
# zopflipng: より強力だが時間がかかる
zopflipng -m --iterations=50 --filters=0me input.png output.png
# pngquant: 有損(パレット化)だが高品質。--quality で目標設定
pngquant --quality=70-95 --speed 1 --strip --force --output output.png input.png
ヒント:
- まず pngquant で色数を減らし、その後に oxipng/zopflipng の無損失圧縮をかけると小さくまとまりやすい。
--strip all
でテキスト/時間などの不要チャンクを落とす。
フィルタ戦略と圧縮パラメータ
PNG はスキャンラインごとにフィルタ(None/Sub/Up/Average/Paeth)を選び、その後に zlib 圧縮します。最適なフィルタ列と zlib パラメータの組み合わせで容量は数%〜数十%変わります。
- フィルタ自動探索:
zopflipng -m --iterations=50 --filters=0me
のように複数パターンを試す - zlib レベル: 9 が最強だが時間がかかる。実務では 5–7 を起点に反復
- 連続トーン領域は Paeth/Average が効く傾向、フラット領域は None/Sub が有利なことも
覚えておくと良い観察ポイント:
- 背景が単色の UI → Sub/None が有利でブロック化も抑えやすい
- 細かなテクスチャ → Up/Paeth の組み合わせで良化しやすい
ディザリング(量子化の粒状感)
パレット化時の「ディザ(網点)」はバンディングを和らげますが、文字やシャープなエッジではノイズとして見えます。
- Floyd–Steinberg: 最も一般的。自然画像に向く
- Ordered: 規則的なパターン。UI では目立つことがある
- 無し: 文字/ロゴで最適。フラットなベタ面で有利
pngquant では --floyd
(0..1)で強度、--ordered
で有無を切替可能。UI のアイコン群はディザ無し、スクショは弱いディザが無難です。
アルファとプレマルチ(暗縁対策)
透過 PNG は、背景合成時のガンマ/プレマルチの扱いで「暗縁」が出ることがあります。前段の色空間と合わせて、必要に応じてアンプレマルチ処理を入れましょう。
// sharp でのアンプレマルチ例(概念)
import sharp from 'sharp'
await sharp('input.png')
.removeAlpha() // 背景合成が既定のときはフラット化
.png({ compressionLevel: 9 })
.toFile('flat.png')
await sharp('input.png')
.ensureAlpha() // 透明維持
.png({ compressionLevel: 9 })
.toFile('alpha.png')
背景に置く先(ダーク/ライト)でにじみ方が変わるため、実ページ上で確認するのが安全です。
CI/CD への組み込み(例)
# .github/workflows/png-optimize.yml(概略)
name: Optimize PNG
on:
pull_request:
paths:
- 'assets/png/**.png'
jobs:
optimize:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: denoland/setup-deno@v1
- run: sudo apt-get update && sudo apt-get install -y pngquant zopfli oxipng
- name: Lossy palette + lossless squeeze
run: |
find assets/png -name '*.png' -print0 | xargs -0 -I{} bash -c '
pngquant --quality=70-95 --speed 1 --strip --force --output {} {}
oxipng -o 4 --strip all {}
zopflipng -m --iterations=30 {} {}
'
- name: Commit optimized
run: |
git config user.email ci@example.com
git config user.name png-bot
git add -A && git commit -m "chore: optimize png" || echo "no changes"
互換フォーマット比較(運用観点)
- WebP ロスレス: PNG より小さくなることが多い。だが配布先の再圧縮や互換条件を確認
- AVIF ロスレス: さらに小さくなる場合も。エンコード時間とデコード速度のバランスに注意
- SVG: 単色ロゴや幾何学図形は最軽量。ラスタライズ不要なら第一候補
Node パイプライン(sharp + child_process)
// scripts/optimize-png.mjs
import sharp from 'sharp'
import { execFile } from 'node:child_process'
import { promisify } from 'node:util'
import { readdir } from 'node:fs/promises'
import { join, extname } from 'node:path'
const run = promisify(execFile)
const SRC = 'assets/png'
const OUT = 'public/png'
for (const f of await readdir(SRC)) {
if (extname(f).toLowerCase() !== '.png') continue
const input = join(SRC, f)
const tmp = join(OUT, f)
await sharp(input).png({ compressionLevel: 9, palette: true, quality: 95 }).toFile(tmp)
await run('oxipng', ['-o', '4', '--strip', 'all', tmp])
}
注意:
palette: true
は色数が多いスクリーンショットで破綻することがあるため、閾値を設けて条件分岐が無難。quality
はパレット量子化の閾値であり、絵柄に応じて 75–95 の間で目視調整。
よくある落とし穴(デバッグ)
- 半透明境界の暗縁: sRGB/ガンマの扱いミス。前段の色空間統一を再確認。
- 透明ピクセルの色にじみ: アルファプリマルチの誤用。明示的にアンプレマルチ化する。
- インターレース on: 転送的メリットが薄い。無効化でサイズ削減。
QA チェックリスト
- 透過の有無と背景色の組み合わせを実ページで検証
- 重要なエッジでのギザつき/にじみがない
- 色数削減後のブランドカラー ΔE が許容範囲
- 余分なテキスト/時間チャンクが残っていない
- 代替フォーマット(WebP ロスレス/有損)との比較で PNG 採用が妥当
FAQ(よくある質問)
- Q. スクリーンショットで文字がにじむのは?
- ディザ強すぎ/色数過少が原因。色数を 128 以上に、ディザ弱め/無しを試す
- Q. 透過 PNG が暗く見える
- 背景との合成で暗縁が出ている可能性。プレマルチ/ガンマの扱いと背景色を再確認
- Q. さらに削りたい
- パレット化→無損失の順で上限まで攻め、なお厳しければ WebP ロスレス/有損を比較
まとめ
PNG は「透過とエッジ保全」の実務選択肢です。型は次の 5 ステップ:
この型を CI に組み込めば、ブレのない軽量化を継続できます。