画像を公開できる状態に整える

ページに載せる前に画像をどうサイズ調整し、切り抜き、リサンプルし、圧縮するかを、ピクセル比、品質設定、切り抜きのハロー、EXIF の回転、メタデータの除去まで含めて解説します。

画像は実際に表示されるサイズにリサイズし、高精細ディスプレイ向けでもその 2 倍までにとどめます。ブラウザーに押し潰させるのではなく、レイアウトが必要とする形に切り抜きます。アーティファクトが見え始めるところまで圧縮し、そこから 1 段戻します。メタデータの除去は最後です。順序には意味があります。最初の工程より後の工程は、はるかに少ない画素を相手にすることになるからです。

まず寸法から

画素数は幅の 2 乗で増えるため、寸法のわずかな違いがデータ量の巨大な違いになります。幅 4000 ピクセルで保存された画像を 800 ピクセルの段に表示すると、必要なデータのおよそ 25 倍を運んでいることになり、どんな品質設定でもこれは取り返せません。

保存サイズ画素数800px を 1 としたデータ量
4000 x 26671070 万25 倍
1600 x 1067170 万4 倍
800 x 53343 万1 倍

目標の幅を決めるには、画面の最大幅ではなく、レイアウトの中でその画像が描画される最大の幅を測ってください。3 列のグリッドに置かれるカードなら、幅は常に 380 ピクセルまでかもしれません。1 つのファイルを複数の場所で使うなら、そのうち最大のものに合わせます。

デバイスピクセル、CSS ピクセル、そして DPI という誤解

CSS ピクセルはレイアウトの単位であって、物理的な点ではありません。デバイスピクセル比は、画面が CSS ピクセル 1 つあたり何個のデバイスピクセルを描画するかを表します。古いモニターでは 1、現代のスマートフォンやノートパソコンの多くは 2、一部のスマートフォンは 3 です。比が 2 の環境では、800 CSS ピクセルの段は 1600 デバイスピクセルで描画されます。CSS 上の幅に合わせたサイズの画像がぼやけて見えるのは、このためです。

実務上の目安は 2 倍です。表示幅の 2 倍で書き出し、縮小はブラウザーに任せます。3 倍にすると、データ量がさらに 125% 増える一方で、通常の視距離ではその差に気づく人はほとんどいません。

ファイルに保存された DPI や PPI は、Web には無関係です。これはそのファイルをどの物理サイズで印刷すべきかを示すヘッダー上の 1 つの数値にすぎず、ブラウザーは完全に無視します。

printed width in inches = pixel width / PPI

3000 px / 300 ppi = 10.0 in
3000 px /  72 ppi = 41.7 in    same file, same 3000 pixels

「300 DPI」で書き出しても、画素は増えませんし、画像データも変わりません。

リサンプルが行っていること

縮小とは、多数の元画素が 1 つになることです。新しい格子より細かいディテールは表現できません。そしてリサンプラーが範囲全体を平均せずに元画素をわずかしか参照しないと、そのディテールは偽のパターンとして折り返されます。斜め線のギザギザや、ストライプのシャツや 1 ピクセル罫線の表のような細かい繰り返し模様に現れるモアレがそれです。良い縮小処理は寄与するすべての画素を平均し、その後たいてい少しシャープネスを加える必要があります。

拡大はディテールを生みません。補間はすでに存在する画素どうしの間の値を推測するだけなので、結果はぼやけるかブロック状になります。機械学習による拡大は、もっともらしいディテールを創作しますが、それは復元とは別のことです。

フィルター仕組み結果
最近傍法最も近い元画素をコピーする混色なし。ドット絵や整数倍には適するが、それ以外ではギザギザになる
バイリニア周囲 4 画素を平均する高速で柔らかい。大きく縮小するとディテールを失う
Lanczosより広い近傍に窓関数付きの sinc を適用する最も鮮明な縮小。極端なコントラストの輪郭ではハローが出る

縦横比、切り抜きかレターボックスか引き伸ばしか

切り抜きは被写体を適切な大きさのまま保ち、端を捨てます。既定としてはこれが正解です。レターボックスは画面全体を保ちますが、帯が加わり、その帯の色を選ぶ必要が生じます。作品や図版のように、何も切ってはならない場合に価値を発揮します。引き伸ばしは形を歪めますし、顔、円、文字では目が瞬時にそれを見抜きます。

.tile img {
  width: 100%;
  height: 100%;
  object-fit: cover;   /* crop; contain = letterbox, fill = stretch */
}

1 つのファイルを複数の形で使うときは、セーフエリア、つまりどの切り抜きでも残る領域で考えてください。16:9 のバナーは上下を切り、1:1 のタイルと 4:5 のカードは左右を切るので、すべてに共通して残るのは中央を通る帯です。被写体と文字はその中に収めてください。そうしないと、横長の写真の端にある顔は、正方形の切り抜きで首から上を落とされます。

品質設定を見つける

品質の数値は何かのパーセンテージではありませんし、その尺度はエンコーダー間で比較できません。JPEG の 80、WebP の 80、AVIF の 80 は、互いに無関係な 3 つの設定です。

やり方はどれでも同じです。いくつかの設定で書き出し、表示されるサイズで結果を眺め、アーティファクトが最初に現れる場所を確認します。空のような滑らかなグラデーションには縞やまだらが出ますし、硬い輪郭や文字にはリンギングが出ます。劣化が目に見えるようになる地点を見つけ、そこから 1 段戻して終わりにします。

1 つの固定値がどの画像にも当てはまることはありません。どれだけ捨てられるかを決めるのは中身だからです。ノイズや質感の多い写真は生き延びるためにより多くのビットを必要とし、平坦なイラストは、情報量の多い写真なら平気な設定でも崩れてしまいます。

内容出発点
表示サイズの写真JPEG は 78 ~ 85、WebP は 75 ~ 82、AVIF は 50 ~ 62
2 倍サイズの写真5 ~ 10 ポイント低く
スクリーンショット、平坦なグラフィック、文字可逆、または非可逆なら 90 以上

切り抜き、柔らかい縁、ハロー

背景の除去が正しいのは、画像を複数の背景色の上に置く必要がある場合や、他の要素と重ねる場合です。商品写真、ロゴ、レイアウトに合成する人物などがそれにあたります。境界が本当に細かい場合、たとえば髪、葉、ガラスなどでは不適切です。出来の悪いマットは、正直な長方形より見苦しいからです。

硬い縁は二値であり、各画素は完全に不透明か完全に透明かのどちらかです。平坦なグラフィックには向いていますが、写真的なものでは階段状になります。柔らかいマットは小数の不透明度を許すので、ある画素を 40% だけ被写体にできます。髪が正しく見えるのは、この方法しかありません。

ハローは境界の画素から生まれます。レンズのボケとアンチエイリアスによって、被写体の色は元の背景の色とすでに混ざっています。つまり各画素はこうなっています。

observed = alpha * foreground + (1 - alpha) * background

背景除去が計算するのはアルファだけです。それらの画素に残る色は依然として汚染されているので、白いスタジオ写真から切り抜いた被写体は淡いふちを帯びます。白いページの上では見えず、暗いページの上でははっきり見えます。きちんとしたマット処理は前景の色についても解を求めます。これはフリンジ除去や色の除染といった名前で提供されています。それができない場合は、マットを 1 画素に満たない量だけ内側へ縮め、改めてぼかしてください。

回転とメタデータ

あるプログラムでは正しい向きなのに別のプログラムでは横倒しになる画像は、ほぼ必ず EXIF の向き情報が原因です。カメラはセンサーの画像を回転させずに保存し、どう回すべきかを示すタグを付け加えます。このタグを読むソフトウェアは正しく表示し、無視するソフトウェア、たとえば多くの画像ライブラリやサムネイル生成器は、生の画素を横倒しのまま表示します。

向きの値意味
1正しい向きで保存されている
3180 度回転する
6時計回りに 90 度回転する
8反時計回りに 90 度回転する
2、4、5、7鏡像を伴う変種

対処法は、回転を画素そのものに焼き込み、タグを 1 に設定するか取り除くことです。90 度単位の回転は、画素をリサンプルせずに並べ替えるだけです。二重回転には注意してください。画素はすでに回されているのにタグが残ったままだと、仕様に従うビューアーはすべて、それをもう一度回してしまいます。

メタデータの除去は片付けではなく、プライバシー上の対策です。EXIF、IPTC、XMP のブロックには、数メートルの精度の GPS 座標、撮影時刻、カメラのメーカー、機種、シリアル番号、所有者名が日常的に入っていますし、切り抜く前の画像を映したままの埋め込みサムネイルが入っていることもあります。自宅で撮った写真をそのまま投稿することは、自宅の住所を公開することです。

除去は最後に行いますが、例外が 1 つあります。ICC のカラープロファイルはこれらのメタデータと同じ場所にあり、広色域の画像からこれを取り除くと、残った数値が sRGB として読まれ、色が平板になったり過飽和になったりします。先に sRGB に変換し、その上で他のすべてを取り除いてください。