課題提出システムには「1 ファイル 2 MB まで」のような上限がよく付いています。自分はずっと、ビューアで開いてサイズを半分にし、JPG で保存し直すというやり方で通していました。容量は確実に下がるので疑っていなかったのですが、貼ったターミナルの出力が読めないと言われて拡大してみたら、赤い文字の画線がつぶれて、細い見出しの縁もぼやけていました。最初はツールが悪いのだと思って別のものに替えましたが、結果は同じでした。原因は圧縮する前の「縮小」の方にありました。
手元で測り直すために、自分でレンダリングした架空の「課題提出システム」画面をサンプルにしました。内容はすべて作り物で実在のサービスではありません。1280×800、元の PNG が 94.0 KB です。
これまでのやり方、つまり 50% に縮めてから JPG 品質 80 で保存すると 20.1 KB、元の 21% になります。ただし元画像と 1 ピクセルずつ比べた PSNR は 27.0 でした。PSNR は元画像との差の大きさを表す指標で、数字が大きいほど元に近く、40 を超えると見分けがつきにくい、という程度の目安です。一方、サイズには一切触れず減色だけを行うと 37.6 KB、60% 削れて PSNR は 60.7、拡大しても文字はほぼ元のままでした。
差が出る理由は、独立した二つの劣化が重なっているからです。まず縮小は隣り合う画素をまとめ直す処理で、UI のスクリーンショットでは画線の幅が 1〜2 画素しかないことも多く、エンコードに入る前の時点で情報が消えます。次に JPEG は小さなブロック単位で量子化するため、濃い文字と明るい背景が接する場所に残差が残り、それが文字の周りのもやとして見えます。写真は連続階調なのでどちらの影響も目立ちませんが、スクリーンショットは情報のほとんどが輪郭にあるので、そのまま効いてしまいます。
減色がスクリーンショットに効くのは逆の理由です。UI で使われる色は、白い背景、灰色の罫線、数種類の文字色、ボタン色くらいで、もともと種類が少ない。減色は限られたパレットを作って各画素をその番号として持つ処理なので、画素の位置は動かず、文字の形はそのまま残ります。imging のオンライン画像圧縮に既定設定で通したところ、208 色のインデックス PNG で 37.6 KB、PSNR 60.7、SSIM 1.000 でした。ローカルの Pillow で 256 色に減色した場合も 37.3 KB とほぼ同じで、効くかどうかは実装よりも画像の性質で決まると言えます。もっと小さくしたいなら WebP 品質 80 で 31.6 KB ですが、これは非可逆の側に戻ることになります。
画像の種類ごとの使い分けは、今のところこう整理しています。
| 画像 | 使う手 | 手元の実測 |
|---|---|---|
| スクショ・単色アイコン・線画 | サイズを保って減色(インデックス PNG) | 94.0 KB → 37.6 KB、60% 減、PSNR 60.7 |
| 写真 | WebP か JPEG、目標容量に合わせて品質を決める | 約 150 KB 時:JPEG q23 が PSNR 33.7、WebP q65 が 37.6、AVIF q59 が 38.9 |
| 透過のある素材 | WebP か AVIF で書き出す | 35.2 KB のバッジ → WebP 20.2 KB、α は 255 段階のまま。AVIF は 2.4 KB |
写真のサンプルは Wikimedia Commons の CC0 画像(iPhone 6 で撮影されたもので、自分が撮った写真ではありません)を 2048×1536 に縮めたものです。透過素材は影付きのバッジを自分で作りました。このバッジを JPG で保存すると、JPEG にはアルファチャンネルがないので透明部分が塗りつぶされ、明るい背景に重ねて比べると PSNR 1.2 まで落ちます。AVIF が古い端末やブラウザでどう表示されるかは試していないので、人に渡すファイルは WebP にしています。
ツール側の条件は、登録不要、インストール不要、出力形式を自分で選べること。imging はブラウザのタブで動き、画像圧縮と形式変換については元画像をアップロードせずローカルで処理します(形式によっては初回にデコーダを取得しますが、これはダウンロードであってアップロードではありません)。境界も二つあります。グラデーション画像を渡したときは圧縮後の方が大きくなり、フォールバックして元ファイルがそのまま返ってきました(画面には元画像が最小である旨が表示されます)。写真は既定のままだと 638.7 KB が 578.0 KB、10% 程度しか減らないので、品質は自分で下げる必要があります。
提出前の確認は 300% に拡大して一番小さい行を見るだけです。文字がはっきりしていて、寸法に触れていなければそのまま出して大丈夫です。ブラウザで試すなら https://imging.ai/ です。
