PNG最適化の最前線 2025 — パレット化と無損失圧縮の実務

公開: 2025年9月19日 · 読了目安: 2 · 著者: Unified Image Tools 編集部

はじめに

UIやロゴ、アイコンの多くは依然としてPNGが有利です。ここではパレット化と無損失圧縮の手順をまとめ、最終サイズを安定的に削るワークフローを紹介します。JPEG/WebP/AVIFとの使い分けは フォーマット変換の戦略 2025 — WebP/AVIF/JPEG/PNG を使い分ける指針 を参照。

判断基準

  • 透過が必要か? → PNG/ロスレスWebP
  • 色数は少ないか? → パレット化(8-bit以下)で大幅削減
  • 細いエッジ/文字があるか? → 無損失優先、過度な前処理は避ける

実務フロー

  1. 元データを sRGB 正規化(詳細は 正しいカラー管理とICCプロファイル戦略 2025 ─ Web画像の色再現を安定させる実践ガイド)。
  2. パレット化で色を削減。
  3. 冗長チャンクを削除(EXIF等)。
  4. 無損失圧縮を実施。

単発なら 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 ステップ:

  1. sRGB 正規化 → 2) パレット化(用途別に色数/ディザ調整) → 3) 冗長チャンク削除
  2. 無損失圧縮(フィルタ/圧縮を反復探索) → 5) 互換/配信条件込みで WebP/AVIF と比較

この型を CI に組み込めば、ブレのない軽量化を継続できます。

関連

関連記事