Apache静的サイト高速化! WebPで画像を圧縮する
2019-07-01 Web

前回の記事では Apache mod_rewrite を使って事前圧縮した HTML/CSS/JS を返す方法を紹介しました。本稿では JPEG/PNG/GIF 画像を事前圧縮しておいた WebP ウェッピー に差し替えて返す手順を説明します。

WebP とは

WebP ウェッピー JPEG/PNG/GIF それぞれの優れた特徴を持つ画像フォーマットです。いままでは画像の特徴や用途に合わせて JPEG/PNG/GIF を使い分ける必要がありましたが WebP ならすべての用途に対応できます。

JPEG
非可逆圧縮。写真のようなフルカラー画像に適しています。
PNG
可逆圧縮。イラストなどの画像に適しています。フルカラー/パレットカラーの両方に対応。アルファチャネル 半透過 にも対応。多くの点で GIF より優れていますが アニメーションに対応していないという弱点があります。
GIF
可逆圧縮。フルカラーに対応できない。アルファチャネル 半透過 に対応できない。単色の完全透過には対応しています 今となってはアニメーションくらいしか使い道がありません。

WebP はこれらの特徴をすべてサポートしています。

  •  フルカラー写真を非可逆圧縮するのも得意 JPEG より圧縮率が高い
  •  イラストを可逆圧縮するのも得意 PNG より圧縮率が高い
  •  アニメーションもできる GIF より圧縮率が高い

完璧な画像フォーマットである WebP にも弱点があります。それは WebP に対応していないブラウザーが存在していることです。Internet Explorer Safari など一部のブラウザーは WebP 画像を表示することができません。

これらの WebP 未対応のブラウザーをサポートするために まだ JPEG/PNG/GIF を完全に捨て去ることはできないのです。

そこで WebP 対応ブラウザーには WebP を返し WebP 未対応ブラウザーには JPEG/PNG/GIF を返す というブラウザーに合わせたレスポンスを返すことが重要になります。

圧縮ツール

WebP の圧縮ツールは下記のサイトからダウンロードできます。

https://developers.google.com/speed/webp/download

Windows の場合は下部の Download for Windows というリンクをクリックします。ダウンロードしたファイルを展開すると bin フォルダーの中にいくつかのツールが入っています。cwebp.exe が圧縮に使うツールです。

cwebp.exe -h でヘルプが表示されます。

コマンドプロンプト
C:¥>cwebp.exe -h Usage: cwebp [options] -q quality input.png -o output.webp where quality is between 0 (poor) to 100 (very good). Typical value is around 80. Try -longhelp for an exhaustive list of advanced options.

cwebp.exe -longhelp で詳細なヘルプが表示されます。

コマンドプロンプト
C:¥cwebp.exe -longhelp Usage: cwebp [-preset <...>] [options] in_file [-o out_file] If input size (-s) for an image is not specified, it is assumed to be a PNG, JPEG, TIFF or WebP file. Windows builds can take as input any of the files handled by WIC. Options: -h / -help ............. short help -H / -longhelp ......... long help -q <float> ............. quality factor (0:small..100:big), default=75 -alpha_q <int> ......... transparency-compression quality (0..100), default=100 -preset <string> ....... preset setting, one of: default, photo, picture, drawing, icon, text -preset must come first, as it overwrites other parameters -z <int> ............... activates lossless preset with given level in [0:fast, ..., 9:slowest] -m <int> ............... compression method (0=fast, 6=slowest), default=4 -segments <int> ........ number of segments to use (1..4), default=4 -size <int> ............ target size (in bytes) -psnr <float> .......... target PSNR (in dB. typically: 42) -s <int> <int> ......... input size (width x height) for YUV -sns <int> ............. spatial noise shaping (0:off, 100:max), default=50 -f <int> ............... filter strength (0=off..100), default=60 -sharpness <int> ....... filter sharpness (0:most .. 7:least sharp), default=0 -strong ................ use strong filter instead of simple (default) -nostrong .............. use simple filter instead of strong -sharp_yuv ............. use sharper (and slower) RGB->YUV conversion -partition_limit <int> . limit quality to fit the 512k limit on the first partition (0=no degradation ... 100=full) -pass <int> ............ analysis pass number (1..10) -crop <x> <y> <w> <h> .. crop picture with the given rectangle -resize <w> <h> ........ resize picture (after any cropping) -mt .................... use multi-threading if available -low_memory ............ reduce memory usage (slower encoding) -map <int> ............. print map of extra info -print_psnr ............ prints averaged PSNR distortion -print_ssim ............ prints averaged SSIM distortion -print_lsim ............ prints local-similarity distortion -d <file.pgm> .......... dump the compressed output (PGM file) -alpha_method <int> .... transparency-compression method (0..1), default=1 -alpha_filter <string> . predictive filtering for alpha plane, one of: none, fast (default) or best -exact ................. preserve RGB values in transparent area, default=off -blend_alpha <hex> ..... blend colors against background color expressed as RGB values written in hexadecimal, e.g. 0xc0e0d0 for red=0xc0 green=0xe0 and blue=0xd0 -noalpha ............... discard any transparency information -lossless .............. encode image losslessly, default=off -near_lossless <int> ... use near-lossless image preprocessing (0..100=off), default=100 -hint <string> ......... specify image characteristics hint, one of: photo, picture or graph -metadata <string> ..... comma separated list of metadata to copy from the input to the output if present. Valid values: all, none (default), exif, icc, xmp -short ................. condense printed message -quiet ................. don't print anything -version ............... print version number and exit -noasm ................. disable all assembly optimizations -v ..................... verbose, e.g. print encoding/decoding times -progress .............. report encoding progress Experimental Options: -jpeg_like ............. roughly match expected JPEG size -af .................... auto-adjust filter strength -pre <int> ............. pre-processing filter

PNG を圧縮するとき

PNG 画像を可逆圧縮する場合はオプション -z 9 を指定するのが良いと思います。-z が指定されると -lossless が指定されたのと同様に可逆圧縮になります。

コマンドプロンプト
C:¥>cwebp.exe -z 9 sample.png -o sample.png.webp

JPEG を圧縮するとき

JPEG 画像を不可逆圧縮する場合はオプション -m 6 を指定するのが良いと思います。このパラメータはエンコード速度と圧縮ファイルのサイズおよび品質との間のトレードオフを制御します。0 を指定するとエンコード時間が短くなり 6 を指定するとエンコード時間が長くなります。事前圧縮ではエンコード時間を気にする必要はないので最大の 6 を指定します。

-q で品質を指定できます。既定値は 75 です。90 以上を指定するとファイルサイズが極端に大きくなる傾向があるので 品質を優先する場合でも 85 までに抑えるのが良いとされています。

コマンドプロンプト
C:¥>cwebp.exe -q 75 -m 6 sample.jpg -o sample.jpg.webp

アニメーション GIF を圧縮するとき

アニメーション GIF を圧縮する場合は cwebp.exe ではなく gif2webp.exe を使用します。ダウンロードした WebP ツール一式の bin フォルダーに入っています

-min_size を指定すると可能な限り出力サイズが小さくなります。

コマンドプロンプト
C:¥>gif2webp.exe -min_size sample.gif -o sample.gif.webp

.htaccess

JPEG/PNG/GIF へのリクエストに対して 事前圧縮した WebP を返すように構成した .htaccess は以下の通りです。

.htaccess
<IfModule rewrite_module> <IfModule headers_module> RewriteEngine on # # WebP # RewriteCond %{HTTP:Accept} image/webp RewriteCond %{REQUEST_URI} ¥.(jpg|png|gif)$ RewriteCond %{REQUEST_FILENAME}¥.webp -s RewriteRule .* %{REQUEST_URI}.webp [L] <Files *.jpg.webp> ForceType image/webp </Files> <Files *.png.webp> ForceType image/webp </Files> <Files *.gif.webp> ForceType image/webp </Files> # # Vary # <FilesMatch "¥.(jpg|png|gif)(¥.webp)?$"> Header append Vary Accept </FilesMatch> </IfModule> </IfModule>
必要なモジュールのロードや .htaccess を有効化する手順については前回の記事を参照してください。

前回の記事で説明した GZIP/Brotli と異なる部分について説明します。

ブラウザーが GZIP/Brotli に対応しているかどうかは Accept-Encoding ヘッダーで判別することができました。ブラウザーの WebP 対応については Accept ヘッダーに image/webp が含まれているかどうかで判定することができます。

URL 書き換え条件として Accept ヘッダーを確認するようにします。

#
# WebP
#
RewriteCond %{HTTP:Accept} image/webp

Vary ヘッダーに Accept を追加するようにします。

#
# Vary
#
<FilesMatch "¥.(jpg|png|gif)(¥.webp)?$">
	Header append Vary Accept
</FilesMatch>

以上の変更で *.jpg がリクエストされたときに *.jpg.webp が返されるようになります。*.png *.gif についても同様です

名前を付けて画像を保存するとどうなる?

ブラウザーには表示されている画像に名前を付けて保存する機能があります。

以下のように URL ファイル名 の末尾が .png となっている画像を保存したときに 結果がどうなるのか気になりませんか?

<img src="img/sample.png">

URL ファイル名 .png となっていますが 実際にサーバーが返す画像は WebP 形式 Content-Type: image/webp になるはずです。拡張子は .png なのに中身は WebP というおかしなファイルができてしまうかもしれません。確認してみましょう。

Chrome の場合

Chrome バージョン 75 で確認したところ問題ありませんでした。

画像を右クリックして 名前を付けて画像を保存 を選択すると保存ダイアログが表示されます。

ファイル名の既定値として sample.png が表示されています。このまま保存すると WebP 形式のバイナリーが sample.png という名前で保存されてしまいそうですが…。

実際に保存してみるとファイルの中身は PNG 形式になっていました。

なぜこのようなことになるのでしょうか?

Apache サーバーのログを確認して謎が解けました。Chrome は名前を付けて保存するときに もう一度 サーバーにリクエストを出しています。そして このときは Accept ヘッダーを含めていません。その結果 サーバーは WebP 形式ではなく PNG 形式の画像を返していたのでした。

  • レンダリング時は Accpet: image/webp が付加されている → WebP が返される
  • ファイル保存時は Accept ヘッダーが付加されていない → PNG が返される

この仕組みならオリジナルの PNG 形式で保存されるのでファイル拡張子 .png との矛盾は発生しません。ですが 画像を WebP 形式で保存できないのは少し残念ですね。

Firefox の場合

Firefox バージョン 67 で確認したところ少し問題がありました。

Firefox Chrome と同様にレンダリング時とファイル保存時で Accept ヘッダーが変化しました。Firefox の場合は レンダリング時は Accept: image/webp,*/* が付加され ファイル保存時は Accept: */* が付加されていました。

Chrome と異なるのは保存ダイアログに表示される既定のファイル名です。Firefox では既定のファイル名として sample.png.webp が表示されます。

どうやら IMG タグの src 属性だけではなく Content-Type: image/webp も考慮してファイル名に .webp を追加してくれたようです。しかし これは大きなお世話です。実際に保存操作をおこなうと Accept: */* でリクエストが再送信されるため 保存時のレスポンスは Content-Type: image/png に変化するからです。

その結果 Firefox では PNG 形式のファイルが拡張子 .webp で保存されてしまいます。

Windows 10 WebP に対応している?

Windows 10 RS5 バージョン 1809 から WebP に対応したようです。

バージョン 1709 のペイントで ファイル 開く を選択したときに 開くダイアログに表示される拡張子は以下のようになっていました。

バージョン 1809 のペイントでは表示される拡張子に HEIC WebP が追加されています。

エクスプローラーでも WebP 形式の画像ファイルがサムネイル表示されるようになっています。Windows WebP を標準サポートしたことで WebP の普及にも弾みがつきそうですね。

この記事を共有しませんか?