ImageMagickを使って画像をリサイズ

携帯で撮影された画像を投稿できる機能を実装しようと考えた場合、画像のファイルサイズに気をつけなければなりません。
最近の携帯のデジカメの画像サイズは、500KByteくらいのものが多く、そのまま画面表示すると、容量オーバーで画面自体が表示できない危険性があります。
そこで、ImageMagick*1を使ってサイズ調整することを検討します。


※ここでは、ImageMagick(Ver6.6.7)を利用しています。

基本的なリサイズ

方法はとても簡単で、アップロード画像を「横230px、縦180px」に変換したい場合、以下のコマンドを実行するだけです。
結果を見ると、ファイルサイズが大きく削減され、見た目についても極端に劣化していませんでした。

[root]# /usr/bin/convert Sunset.jpg -resize 230x180\> Sunset_230x180.jpg

[root]# ls -l
 -rw-r--r-- 1 root root 589115  1月 11 18:06 Sunset.jpg
 -rw-r--r-- 1 root root  12174  1月 11 18:08 Sunset_230x180.jpg


※「\>」の記述は、「もし指定サイズより画像が小さい場合はリサイズされない」という記述になります。画像が粗くなるのがいやであれば指定するといいかもしれません。*2

処理時間の改善

便利なImageMagickですが、処理は結構重いのが問題です。


上記のコマンド実行例では、(約600KByteの画像変換に)約1秒かかっています。リサイズパターンが数パターンあると、画像1枚で数秒かかってしまうということになり、単位時間あたりに処理可能な画像の枚数がとても少なくなってしまいます。。


下記のページに、とても参考になる情報が書かれていたので、ご紹介します。

いつもの convert に「-define jpeg:size=...」をつけるだけで10倍速くなる
「ある条件下で高速に」と書きましたが、その条件とは、以下の2つです。

条件1: 元ファイルが JPEG 画像であること
条件2: 元ファイルを開くより前に、変換後の縦横pxが分かっていること

本当は速いImageMagick: サムネイル画像生成を10倍速くする方法 - 昼メシ物語


上記の条件についてですが、

  • 元ファイルが JPEG 画像であること
    • デジカメの画像保存形式は、JPEGなので投稿機能の制限としてしまっても問題ないでしょう。
  • 変換後の縦横pxが分かっていること
    • これも画面仕様として決まっていることがほとんどなので問題ないと思います。


ということで、実際にやってみました。

[root]# /usr/bin/convert 500K.jpg -resize 230x180 500K_230x180.jpg
⇒処理時間:約1秒

[root]# /usr/bin/convert -define jpeg:size=230x180 -resize 230x180 500K.jpg 500K_230x180_fast.jpg
⇒処理時間:約0.1秒


確かに、約10倍速い!!!画質も特に問題なさそうです。素晴らしい。


ちなみに、「条件2: 元ファイルを・・・」についてですが、「縦横両方とも事前に分かっていること」が必要だと思っていましたが、「縦か横の片方だけ」の指定でもうまく動作します。

[root]# /usr/bin/convert -debug Coder -log %e -resize 230x 500K.jpg 500K_230x.jpg
Profile: exif, 5664 bytes
Profile: iptc, 14824 bytes
・・・
Geometry: 1509x2100
・・・

[root]# /usr/bin/convert -debug Coder -log %e -define jpeg:size=230x -resize 230x 500K.jpg 500K_230x_fast.jpg
Profile: exif, 5664 bytes
Profile: iptc, 14824 bytes
・・・
Geometry: 378x525
・・・


上記のようにデバック情報を出力してみると、Geometry という項目が、元画像のサイズよりもかなり小さな値で読み込まれているのがわかります。


若干、注意が必要なのは、このオプションは、1/2以下に圧縮する場合のみ効果があるということです。

  • 1200x900 ⇒ 800x600 : 効果なし(※Geometry: 1200x900)
  • 1200x900 ⇒ 600x450 : 効果有り(※Geometry: 600x450)
  • 1200x900 ⇒ 200x150 : 効果有り(※Geometry: 300x225)


その理由は、このオプションの仕組みにあります。

ImageMagick は、 -define jpeg:size={width}x{height} というオプションが与えられると、実際の画像サイズの 1/2、1/4、1/8 のサイズの中から、オプションで指定されたサイズに近いものを選び、そのサイズとして画像を開きます。どうしてそんなことが可能なのかというと、その仕組はJPEGのデータ構造に由来しています。JPEG画像は、8x8(場合によっては16x16)pxの小さな正方形の画像の集合からなっています。詳しい説明は他の資料に任せますが、この構造を活かして、ImageMagickで利用しているJPEGライブラリのlibjpegでは、1/2、1/4、1/8のサイズへの縮小は高速に計算できるのです。

本当は速いImageMagick: サムネイル画像生成を10倍速くする方法 - 昼メシ物語


つまり、1/2、1/4、1/8のどれかに高速に変換後*3、指定された任意の値に変換するという動作をするため、1/2以下でなければ、このオプションは動作できないということです。

補足

補足1
但し、下記にもある通り、画像処理ソフトは脆弱性がある可能性の高いソフトウェアなので、念のため注意が必要です。

画像処理プログラムには過去、さまざまな脆弱性が発見されてきました。ImageMagickもご多分に漏れず、CVEを検索するだけでも過去に37件の脆弱性が発見されていることが分かります。

画像処理ソフトウェア「ImageMagick」の脆弱性 (1/2):CodeZine(コードジン)


補足2
リサイズすることを前提とするなら、HTMLでの画像表示の際は、「widthの指定」はなくてもよいと思います。昔のブラウザでは「width,height属性は指定すべき*4」という話もありましたが、今は逆に指定しないほうがよいかもしれません。

それは「画像の差し替え時にHTMLファイルの修正が不要となる」ことです。
サイト上のプロモーション、ユーザーへのアプローチを変えるためにキャッチとなる画像を変更したりということが多々あるのですが、見せ方によって画像のサイズが変わってしまう場合もあります。
レイアウトはそのままで画像のサイズだけが少し変わるというケースがあったとして、その画像(img要素)に対してwidth,height属性を指定してる場合には、画像ファイルの差し替えだけでなくHTMLファイルの方も修正しないといけません。

http://designblog.ecstudio.jp/guideline/img-width-height.html

*1:参考:コマンドラインで画像を縮小する:ImageMagick convert mogrify

*2:ImageMagickの詳しいオプションはコチラを参照⇒ImageMagickオプション(日本語)

*3:面積としては、1/2 X 1/2 = 1/4 , 1/8 X 1/8 = 1/64 のように圧倒的に小さくなり、CPUがメモリを確保する仕事量が大きく削減されることになり、これほどの効果があると考えられます。

*4:レンダリングエラーになるとか、指定しているとレンダリングが速いとかいう話ですね。でも最近のブラウザを考えるとこのあたりはもう考えなくてもよいと思います。