見る人が見ればすぐに分かるのだけど、このブログはwordpressというブログシステム(CMS?)を使っていて、さほどカスタマイズはしていないものの、単純に写真をアップロードすれば、それをリサイズしたりコピーライトメッセージを追加したりEXIF情報を削除したりファイル名を変更したりしてくれる、「プラグイン」と呼ばれるプログラムを拵えて仕込んだりしてきた。手間を減らすためである。きょうは、新しいプラグインを作った話。
サムネイルのぼんやり感
ブログのページに載せる写真をアップロードすると、そのときにはたらく自作プラグインによって、オリジナル画像を1200×900のリサイズして保存する。そして、wordpressの機能によってそこから更に縮小が行われ、全部で5種類のサムネイル画像が生成される。ブログ画面に貼り付けることが多いのは、そのうちの474×356(アスペクト比が4:3のとき)という大きさの「サイズ大」という画像ファイルである(画像サイズはwordpress管理画面で自分で設定する)。
ブログの画像をクリックすれば、1200×900の画像を表示するようにしているとはいえ、一番目に付くのはこの474×356の画像なので、自分で見直してみても、どうにもぼやけていてパッとしない(もともとパッとしないのは置いておいて)。これは縮小によって被写体の微細な輪郭線が埋没したりまじりあってしまうので、文字通りエッジが効かないぼんやり画像になってしまうのである。これを解消するには、リサイズ後に輪郭を強調しシャープネスを上げてやるしかない。
ブログにアップロードする前に画像処理ソフトでせっせときれいにしてやるのでは、手間がかかってしょうがないから、単純にドラッグ&ドロップでアップロードさえすれば、サーバー側のプログラムが勝手に所定の措置をとってくれるのが望ましい。そこで、wordpressのフィルター機能を使って、縮小画像のシャープネスを上げる機能をプラグインに追加することにした。
imagefilter()でシャープネスを上げる
画像のシャープネスを上げる処理を実現するには、コンボリューション(畳み込み)と呼ばれる処理を使うのが定番で、phpにもimageconvolution() という関数が用意されている。この関数は、GD (libgd)というライブラリを利用しているので、linux以外のphpの実装で動くのかどうかは分からない。imageconvolution()の実装を見ていくと、どうやらimagefilter() という関数でも同じようなことがやれそうで、パラメータも少ないからこちらを使うことにした。
「大」サイズにimagefilter()を適用した結果が上の画像で、花粉や花びらを見るとwordpressが作ってくれたものよりもシャープさが増している。なお、クリックして表示されるオリジナルサイズ(1200×900)にも、若干の輪郭強調をかけてある。ただ、通常の撮影時には、シャープネスとコントラストを共に-1に落としているくらいなので、これを見るとちょっとキツメの気がする。
150×150のサムネイル画像に適用すると以下のような感じになる。上がwordpressの縮小で下がimagefilter()をかけたもの。ちょっとドギツイ感じ、なんというか、黎明期のガビガビなコンデジ風。
300×225の「中」画像に適用するとこんな具合。サムネイル的な画像としてはこんなもんか。
輪郭を強調する部分は以下のような具合。
1 2 3 4 5 6 |
$src = 'input.jpg'; $dst = 'output.jpg'; $img_src = imagecreatefromjpeg($src); imagefilter($img_src, IMG_FILTER_SMOOTH, -14); imagejpeg($img_src, $dst); |
imagefilter()の最初のパラメータとして IMG_FILTER_SMOOTH を指定し第二パラメータとして-9より小さな負の値を指定することで、エッジが強調された結果を得ることができる。詳しくは、PHPのソースの…/ext/gd/libgd/gd_filter.cを参照。
第二パラメータの値が小さいほど効きが緩くなるので、画像の縮小率との兼ね合いから適切な値を見つければよい。今のところ、小には-14、中には-15、大には-16としてみた。オリジナル画像(1200×900)も、もともとの画像からは大きく縮小されているから適用した方が良さそうな気もするが、趣味の問題でやらないことにした。
画像のアップロード時に自動で処理する
imagefilter() による処理をいつ行うかと言えば、やはりアップロード時だろう。では、アップロード時にいつ縮小画像が作成されるのかを探してみると、どうやら wp-admin/includes/image.php あたりのようである。縮小画像の作成が終わって諸情報のdbへの格納が終わると、
1 |
apply_filters( 'wp_generate_attachment_metadata', $metadata, $attachment_id ); |
が実行される。つまり、’wp_generate_attachment_metadata’ をプラグインで add_filter() してやり、$metadataに入っている諸情報に基づいて、wordpressが作ってくれた縮小画像を一つ一つ作り直していけばよい。
プラグインはだいたいこんな具合になっている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?php add_filter('wp_generate_attachment_metadata', 'my_generate_attachment_metadata', 10, 2); function emphasize_sharpness($src, $max_size) { // サイズに応じて輪郭強調する。 } function my_generate_attachment_metadata($metadata, $attachment_id) { $upload_dir = wp_upload_dir(); $dir = $upload_dir['basedir'] . '/' . pathinfo($metadata['file'], PATHINFO_DIRNAME) . '/'; foreach($metadata['sizes'] as $a) { $src = $dir . $a['file']; $max_size = max($a['width'], $a['height']); emphasize_sharpness($src, $max_size); } return $metadata; } |
早速きょうから使うことにしよう。これ以降の縮小画像は、このプラグインがはたらいた結果になります。過去のものについては、当面は何もしない。また、画像をクリックしたときに表示される1200×900の画像については、imagefilter()を適用しないことにした。
その他
wordpressは5種類の縮小画像を作っている、と書いたが、そのうちの3つ(サムネイル、中、大)は設定画面で大きさを指定することができる。残りの2つは、wordpressがもつ「テーマ」という機能によって追加されているようで、うちの場合は672×372と1038×576という変わった比率の画像ができている。どうやら、アイキャッチ画像として使うためのものらしい。使わないなら、プラグインで自動的に削除してしまうのもいいかもしれないし、ちゃんとやるなら別のフィルタでもって、そのサイズの作成を妨げてやればよいだろう。
追記
その後ちょっと使ってみて、いくつかの問題点や見落としがあったので補足しておく。
img srcset問題
いつの間にか、wordpressが生成するページのhtmlに含まれるimgタグには、表示側に応じて適切な画像ファイルを選択可能とする、srcset という属性が追加されていた。
うちのブラウザで見ると、最初にページが表示されたときはシャープネスを高めた縮小画像が表示されているが、一度その画像をクリックしてオリジナルサイズの画像を表示すると、それを閉じた後は、ブラウザがwidth/heightに応じて縮小したオリジナルサイズ画像がページ内に表示された。ブラウザがシャープネスを高めてくれるわけではないので、イマイチなぼんやり縮小画像に戻ってしまう。
ページを構成するimgタグにsrcsetが書いてあっても、widthとheightもしっかり指定されているので、表示サイズ指定と表示する画像サイズとの間に大きな開きがあれば、こういう結果になってしまう。解決策として、プラグインに以下を追加してsrcsetが生成されないようにした。
1 2 3 4 5 |
add_filter('wp_calculate_image_srcset_meta', 'my_calculate_image_srcset_meta', 10, 4); function my_calculate_image_srcset_meta($image_meta, $size_array, $image_src, $attachment_id) { return null; } |
jpeg以外は処理しない
当たり前なので逆に忘れていたのだけど、
1 |
$metadata['sizes']['mime-type'] |
が image/jpeg 以外では輪郭強調処理は行わない。
縮小画像「大」の大きさ
wordpressの設定/メディアにおいて、画像サイズ「大」の大きさは決められるし、指定のサイズに応じた縮小画像が生成される。ところが、「大」の幅を広くしてから画像ファイルを登録し、いざページに挿入しようとすると、imgタグ内には常に width=”474″が指定されているので、こりゃいったいなんなの?と思っていた。
wordpressの実装を見ていくと、コンテンツの最大幅をもつ(グローバルな)変数 $content_width と、挿入しようとする画像の幅の比較が行われていた。画像の幅が$content_widthより大きい場合には、imgタグのwidthには$content_widthの値が与えられるようになっていた。うちのwordpressの表示テーマでは、$content_widthに474が代入されていたから、画像サイズを大きくしても474だったわけである。
$content_widthの値は、テーマ/functions.phpを直して自分で書き直すものらしく、適当な大きい値をセットすることで解決。今後はページの上の画像サイズが若干大きくなる。