omohayui blog

おも‐はゆ・い【面映ゆい】[形][文]おもはゆ・し[ク]《顔を合わせるとまばゆく感じられる意》きまりが悪い。てれくさい。

リサイズした画像がぼやける件

Image Resizing Algorithm

経緯

Retina対応等が進む中、大きい画像をWeb上で縮小表示し高解像度ディスプレイに対応するケースが多々あります。
がしかし、ブラウザ固有のリサイズのアルゴリズムや表示画像サイズによっては、レンダリングされた画像がぼやけることも。
そこで、リサイズのアルゴリズムを正しく理解した上で、適切な対応方法を検討したい、という話なのです。

縮小表示の例

表示サイズ width: 280px
(原寸)
width: 140px
(4px → 1px)
width: 145px
(3.86...px → 1px)
width: 145px
(3.86...px → 1px)
image-rendering auto auto auto pixelated
画像

(サンプル画像が古いことへの突っ込みは受け付けません。久しく絵と呼べるものを描いていないのです。)

Chromeで見た場合

  • width: 140px は実画像サイズの1/2なので、4ピクセルが1ピクセルに縮小される計算
    • ⇒ ボケない
  • width: 145px は実画像サイズから表示サイズに縮小する際にピクセル数が割り切れない少数になってしまう
    • ⇒ エッジがボケた感じになる
  • width: 145px に style属性で image-rendering: pixelated; を指定すると、縮小アルゴリズムが「最近傍 (Nearest Neighbor)」に指定される
    • ⇒ エッジがギザる

IEで見た場合

ChromeのリサイズアルゴリズムがBilinearやBicubicといったエッジを滑らかにするアルゴリズムであるのに対して、 IEは元々Nearest Neighborのような単純なアルゴリズムを使っているようで、width: 145px をIEで見てもボケるというよりギザる感じでした。 (※個人的な主観です)

拡大表示の例

表示サイズ width: 100px (原寸) width: 300px (3倍) width: 300px (3倍)
image-rendering auto auto pixelated
画像

拡大表示の方がアルゴリズムの違いがわかりやすいです。

  • 原寸の画像はあえてドットが分かりやすいようにしている 100x100pxの画像
  • そのままブラウザで3倍のサイズに指定して表示すると、ドットのギザギザはブラウザのレンダリングの仕様によってぼかされて表示される
  • style属性で image-rendering: pixelated; を指定すると、原寸のピクセルをそのまま拡大され、ドットがはっきり表示される

image-renderingの指定について

ブラウザ上のリサイズアルゴリズムを指定するのに使っていた image-rendering ですが、 まだ実験段階、開発途上といったところみたいです。

developer.mozilla.org

説明
auto
  • デフォルト値
  • バイリニア (Bilinear) リサンプリングなどの「滑らかな」色が許容されるアルゴリズムで拡大/縮小
  • 高品質
crisp-edges
  • 画像内のコントラストとエッジを保つアルゴリズムにより拡大/縮小
  • 画像の処理過程で滑らかな色やぼかし効果が現れない
  • ピクセルアートなどの画像を想定
pixelated
  • 画像拡大時に「最近傍 (Nearest Neighbor)」などのアルゴリズムが使用され、画像が大きなピクセルで構成されたように表示される
  • 縮小する時は、'auto' と同じになる
    • みたいな記述があるが実際に試してみると縮小表示でも「最近傍 (nearest neighbor)」が使われている模様

各ブラウザに対応させたい場合

.pixelated {
  -ms-interpolation-mode: nearest-neighbor;   /* IE8+ */
  image-rendering: -webkit-optimize-contrast; /* Safari (WebKit) */
  image-rendering: -moz-crisp-edges;          /* Firefox (Gecko) */
  image-rendering: -o-crisp-edges;            /* Opera 12.x */
  image-rendering: pixelated;                 /* Chrome 41+, Opera 29+ (CSS4) */
}

参照:http://memo.sdn-project.net/post/113148316679/pixelated

よく使われるリサイズアルゴリズム

滑らか度
名称
説明
NearestNeighbor
(最近傍補間)
  • 最も単純な拡大アルゴリズム
  • 参照する位置に最も近い位置にある画素の輝度値を参照する
  • 拡大縮小後の画素の実数を整数とみなしたときの誤差をそのまま表示すため、エッジにはジャギー(ギザギザ)が発生する
  • 処理速度が非常に高速
Bilinear
(バイリニア補間)
  • 単純な線形補間による拡大アルゴリズム
  • 補間対象となるピクセル周辺の2×2画素(4画素)を使って、輝度値を直線的に補間して輝度値を求める
Bicubic
(双三次補間)
高+Lanczos3
  • 動画や写真の縮小処理などで高品質で有名
  • 補間対象となるピクセル周辺の6×6画素(36画素)のピクセルを参照し、Bicubicとは異なる計算式で算出
  • 処理速度は最も遅い
  • 線画の縮小時にちらつきが見受けられたりする(経験談

参照:http://imagingsolution.blog107.fc2.com/blog-entry-142.html, http://www.geocities.jp/numada777/ip05.html, http://www.amedama.com/blog/20110220392.html

対応案とまとめ

縮小表示

  • 理想は表示サイズの2倍3倍といった整数値でピクセル表示ができる元画像サイズを用意する(とにかくこれ)
  • ユーザーに入稿してもらったものを表示する際や1つの画像を同一画面で使いまわす際、縮小時にエッジがボヤける(特に線画)と言った場合は、 CSSで image-rendering を指定するといった手法もあり(※あまりおすすめできない)

拡大表示

  • 通常の画像の場合は表示画像より大きいもの(Retina対応するのであれば4倍)を用意する
  • ドット絵等あえてジャギー(ギザギザ)を見せたいものは、CSSで image-rendering を指定する方法もあり

Goのresizeパッケージ、giftパッケージを使って画像ファイル自体のリサイズ処理も色々と試したので、その話も書いておきたい気持ちある。