猫の手ツール

Algorithm & Optimization

容量収束アルゴリズムと
ブラウザ内HEIC変換の舞台裏

指定KBに近づけるため、画像の縮尺とJPEG品質を順に変えて候補を作ります。保存容量は画像の内容やブラウザのエンコーダにも左右されるため、処理の仕組みと、保存後に確認したい条件を分けて説明します。

このツールの入力から出力まで

現行コードの確認日:

入力
写真と目標のKB数を指定します。ファイルの容量と縦横のピクセル数は別の条件です。
処理
JPEG品質を二分探索し、目標以下の候補がなければ縦横を0.8倍にして再試行します。容量はBase64長から概算します。
出力
出力はJPEGです。

まず確認する点:候補が得られないと最後の低品質画像を返すため、目標値の達成を画面表示だけで判断せず、保存ファイルの容量も確認します。

仕上がりの例

目標50KBで圧縮し、完成画面で47KBと表示された例です。

加工前・加工後
左は横になった愛猫の元写真。右は容量を小さくした同じ構図の写真。

容量と画質は写真によって変わります。

加工前・加工後

左は横になった愛猫の元写真。右は容量を小さくした同じ構図の写真。

掲載画像はサイトにある既存の公開作例です。保存済みの資料では運営者の愛猫の写真を使用しています。今回の確認日が、この作例を実行した日を表すわけではありません。

保存済み検証記録の条件を見る

資料の種類は「ツールの出力画像を保存した記録」です。公開作例と保存済み記録で素材や色などが異なる場合があるため、下の数値は保存済み検証記録の条件として読んでください。

記録の入力画像
800×751px (JPEG)
記録の出力画像
800×751px (JPEG)
保存済み記録の主な操作・出力条件
画面の項目記録の値現行コードの初期値・範囲
記録の目標容量50KB—
記録時の結果表示47KB—

資料ファイルの作成日は実行日として扱っていません。個別端末の処理速度や、ほかの写真に対する検出・加工の精度を測定した資料でもありません。

実装の確認箇所:src/pages/image/compress/index.astro。以下のコード抜粋と出力条件は、この本番実装に基づいて説明します。

仕組みを読み進めるときは、メモリ管理と画像処理(画素バッファ、縮小と容量予算)、Canvas APIと画像処理(画像寸法、drawImage、画素と透明度の扱い)が参考になります。

1. 縮尺と画質を二段階で追い込む「容量収束ロジック」

画像のファイルサイズを特定のKB数以下に抑えるには、2つの手段があります。一つは「画質(Quality)を下げること」、もう一つは「解像度(Scale)を下げること」です。本ツールでは、まず解像度を維持したまま画質をバイナリサーチ的に調整し、それでも目標に届かない場合に初めて解像度を下げるという、二段階の試行アルゴリズムを採用しています。

メリット: 品質だけを下げ続けず、縮小も候補にできます。ただし、細かい文字や顔の輪郭は失われることがあり、容量条件を満たしても提出用の画像として十分とは限りません。

次は実装と同じ品質探索の判断を抜き出した例です。画像を描画済みのCanvasと、正の目標バイト数を渡します。実装はこの探索を縮尺ごとに行います。

function searchQuality(canvas, targetBytes) {
  let low = 0.1, high = 0.95, best = null;
  for (let i = 0; i < 6; i++) {
    const quality = (low + high) / 2;
    const url = canvas.toDataURL('image/jpeg', quality);
    const estimatedBytes = Math.round(url.length * 3 / 4);
    if (estimatedBytes <= targetBytes) {
      best = url;
      low = quality;
    } else {
      high = quality;
    }
  }
  return best;
}

2. 動的インポートによる「ブラウザ完結型」HEIC変換

HEICの直接読込に対応しないブラウザもあるため、本ツールでは、heic2any ライブラリを必要な時だけ import() する仕組みを導入し、クライアントサイドのみでJPEGへの変換を実現しています。

メリット: 選択した画像の変換は端末内で行います。変換ライブラリを初めて読み込む通信や、画像のデコードにかかる時間は別に必要です。画像を送信しない設計と、サイト全体が通信しないことは同じではありません。

3. メモリ解放を意識した Blob オブジェクト管理

高解像度の画像を扱う際、ブラウザのメモリ(RAM)消費が問題になります。特に URL.createObjectURL で生成したURLは、明示的に解放しない限りメモリに残り続け、スマホでのブラウザ落ちの原因になります。本ツールでは、画像の読込に使ったBlob URLを読み込み後に解放しています。編集中の画像や完成画像まで同時に破棄する処理とは区別が必要です。

メリット: 不要なBlob URLへの参照を残さないための処理です。対応できる画像サイズや連続処理の安定性を、すべての端末で保証するものではありません。

出力の仕様と確認する点

現行実装では、各縮尺で品質0.1〜0.95の範囲を6回探索し、目標以下の候補がなければ縦横を0.8倍にします。縮尺の試行は最大8回です。Base64の長さから容量を概算しており、厳密なファイルサイズ測定ではありません。

候補が得られない場合は最後のCanvasを品質0.1で書き出すため、指定KB以下を必ず満たすとは限りません。保存したファイルの容量と、提出先の縦横サイズ条件を確認してください。

出力はJPEGです。Canvasを白く塗ってから画像を描くので透明部分は白になり、元画像のメタデータをそのまま保持する変換でもありません。