gazou-compressor.jp

プログレッシブJPEG最適化:baselineとの違い・mozjpeg/Sharpの実務

JPEGは今でも“最後の互換フォーマット”。その見え方の早さを底上げするのがprogressive(段階描画)です。この記事では baseline との違い、品質とサブサンプリングの決め方、 Sharp+mozjpegでの一括変換レシピをまとめます。

先に結論
  • 写真は Q=70–82 & 4:2:0 & progressive が起点。LCP画像は priority 付与。
  • 文字/図版は Q+5 & 4:4:4 で滲みを抑える(関連:文字にじみ対策)。
  • 極小画像(<15KB)やサムネ一覧は baseline でもOK(ヘッダ増で逆効果になりやすい)。
  • 色ズレを避けるためにsRGB化→メタデータ削除(EXIF/ICC)(関連:sRGBガイド)。

要点(TL;DR)

1. なぜ progressive を使うのか

baseline は“上から順に”一発描画、progressive は“全体を粗く→段階的に精細化”。低帯域でも全体像がすぐ見えるため、 体感は軽くなります。逆に、極小サムネやアイコンではヘッダ増分が相対的に効いてサイズが増えがちで、恩恵が薄いです。

2. 最短フロー(実務)

  1. 基準づくり:写真は Q=78 / 4:2:0、文字がある画像は Q=83 / 4:4:4 を基準に。
  2. 一括変換:下の Sharp スクリプトで progressive JPEG を出力。
  3. 検品:人物の目元・斜め線・文字で劣化確認(必要に応じて Q±5)。
  4. 配信:LCP画像は priority と適切なキャッシュ(SWR/immutableの使い分け)。

3. 実装レシピ(コピペOK)

3.1 Sharp で progressive JPEG を一括生成

// scripts/jpeg-progressive.ts
// 依存: npm i -D sharp tsx
// 使い方:
//   $env:Q=78; $env:SUB='4:2:0'; npx tsx scripts/jpeg-progressive.ts
//   ※ Windows PowerShell 前提(SUB= '4:4:4' も可)

import fs from "node:fs/promises";
import path from "node:path";
import sharp from "sharp";

const INPUT = process.env.IN ?? "input";   // 入力フォルダ
const OUTPUT = process.env.OUT ?? "output"; // 出力フォルダ
const Q = Number(process.env.Q ?? 78);
const SUB = (process.env.SUB ?? "4:2:0") as "4:2:0" | "4:4:4";

async function main() {
  await fs.mkdir(OUTPUT, { recursive: true });
  const files = (await fs.readdir(INPUT)).filter(f => /\.(jpe?g|png|webp)$/i.test(f));

  for (const f of files) {
    const src = path.join(INPUT, f);
    const out = path.join(OUTPUT, path.parse(f).name + ".jpg");

    await sharp(src)
      .rotate()                       // EXIFの回転を適用
      .toColourspace("srgb")          // ICCを落としても色が変わらないよう sRGB 化
      .jpeg({
        quality: Q,                   // 目安: 70–82
        progressive: true,            // Progressive JPEG を有効化
        mozjpeg: true,                // mozjpeg が有効なら最適化を使用
        chromaSubsampling: SUB        // 4:2:0(写真)/ 4:4:4(文字・イラスト)
      })
      .toFile(out);

    console.log("→", out);
  }
}

main().catch(err => { console.error(err); process.exit(1); });

3.2 package.json と実行例(PowerShell)

// package.json(抜粋)
{
  "devDependencies": { "sharp": "^0.33.0", "tsx": "^4.0.0" },
  "scripts": {
    "jpeg:progressive": "tsx scripts/jpeg-progressive.ts"
  }
}
// 実行例(PowerShell)
//   $env:Q=80; $env:SUB='4:2:0'; npm run jpeg:progressive
//   # 入出力パスの変更
//   $env:IN='assets/raw'; $env:OUT='public/img'; npm run jpeg:progressive

3.3 next/image の基本(LCP対策)

// app/(example)/page.tsx(LCP画像の例)
// Progressive JPEG を用意したうえで next/image で配信
import Image from "next/image";

export default function Example() {
  return (
    <Image
      src="/img/hero.jpg"            // Progressive JPEG
      alt="Hero"
      width={1600}
      height={900}
      priority                          // LCP対策(初回表示は優先読み込み)
      sizes="(min-width: 1024px) 1200px, 100vw"
      placeholder="empty"               // LQIPと併用するなら blurDataURL を設定
    />
  );
}

4. 応用と使いどころ

5. 公開前チェック

6. まとめ

Progressive JPEG は“先に見える”を作る実戦的な選択です。まずは Q=78 / 4:2:0 / progressive を基準に一括処理し、 文字や図版だけ 4:4:4 で微調整。画像そのものの最適化は /compressor、配信は Cache-Controlガイド と組み合わせて総合最適にしましょう。

公開:2025-09-02

gazou-compressor.jp 編集部

画像圧縮・変換・背景除去などの実践テクニックと、Webで“速く・軽く・崩さない”ためのノウハウを発信しています。

関連記事