一般的にImageMagick のサムネイル画像生成は遅いとされており、パフォーマンスが求められるシーンではImlib2 などのより高速な画像処理ライブラリが使われることが多いです。
Imlib2 の高速さについては、以前「Imlib2でImageMagickより3倍高速かつ美しいサムネイル画像の生成 - 床のトルストイ、ゲイとするとのこと」という記事で紹介しました。この記事のベンチマークにおいて、Imlib2 によるサムネイル画像の生成は、ImageMagick の3倍程高速でした。
しかし、ImageMagick は Imlib2 より画質がよく、高機能で使いやすく、今も頻繁にメンテナンスされており、とてもよく出来ています。その点 Imlib2 は、2004年からメンテナンスされておらず、セキュリティホールが見つかっても、各Linuxディストリビューションがそれぞれパッチを当てているような状況です。ImageMagick がもし十分に速ければ、 Imlib2 から脱却することができます。
そんな中、ImageMagick について調べているうちに、ある条件下で Imlib2 よりも高速にサムネイル生成する方法を見つけたので、紹介します。
「ある条件下で高速に」と書きましたが、その条件とは、以下の2つです。
たとえば、前回の記事 の例と同じ、4288x2848のJPG画像(4.8MB)から180x120pxのサムネイル画像を作成することを考えます。
普段なら、ImageMagick の convert コマンドを使って
convert -resize 180x120 src.jpg dst.jpg
などとするとおもうんですが、ここで、高速化のためのオプションを付けます。
convert-define jpeg:size=180x120 -resize 180x120 src.jpg dst.jpg速くする方法というのは、実は、これだけです。*1
4288x2848のJPG画像(4.8MB)から180x120pxのサムネイル画像を以下の条件でそれぞれ10回ずつ生成し、変換にかかる時間を比較しました。
ベンチマークのコードはこちら(gist: 488401)。
結果は以下のとおりです。*2
| 条件 | 1枚あたりの平均変換時間(sec) | 変換結果(quality=90) |
|---|---|---|
| 1.ImageMagick(jpeg:size なし) | 2.735397 | ![]() |
| 2.ImageMagick(jpeg:size あり) | 0.275900 | ![]() |
| 3. Imlib2 | 0.832415 | ![]() |
以上のように、ImageMagick のサムネイル生成では、 jpeg:size オプションを指定することで、指定しない場合に比べておよそ10倍高速になりました。Imlib2 と比べても約3倍ほど高速になっています。
画質については、jpeg:size オプションをつけたことによる変化はほとんどありませんでした。
画質はほぼそのままで高速なら、このオプションを付けない理由はないですね。
なぜ jpeg:size で画像の大きさを指定しただけで、これほどまでに JPEG の縮小が速くなったのかを説明します。
そもそもこのオプションはなんなのかというと、公式ドキュメントに以下のような記述があります。
(以下、Common Formats -- IM v6 Examplesより意訳)
-define jpeg:size={width}x{height}
JPEGライブラリに、JPEGファイルの読み込みのヒントを与えます。このとき、作成される画像は、少なくともこの width x height より大きな画像になります。
もし入力画像が巨大なら、このヒントを与えることで、ImageMagickは小さな画像として開くため、画像読み込み時のメモリを大幅に節約でき、演算を劇的に高速化できるでしょう。
このオプションはあくまでヒントでしかなく、実際にその大きさの画像を得ることができるわけではないという点を忘れないでください。このヒントで指定したサイズに近い、かつそれ以上の大きさの画像を得ることになります。
この公式の説明ではよくわかりませんが、とにかく速くなりそうなことは伝わってきます。このオプションの挙動について、詳しく調べてみました。
先程の例、4288x2848 のJPEG画像を 180x120 にリサイズする例で説明します。使ったコマンドは、以下のとおりでした。
convert-define jpeg:size=180x120 -resize 180x120 src.jpg dst.jpgこの時の挙動は次のようになります。
図にすると次のようになります。
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は画像をすべてメモリ上に展開するという特性があるので、元画像が巨大であるほどメモリ消費は激しくなり、そのためのメモリ確保に時間がかかります。それがImageMagick が遅いと言われる原因のひとつです。リサイズ処理自体は、高速なアルゴリズム(デフォルトではLanczos)で行われるので、実は Imlib2 などの他のライブラリに比べても遅いわけではないのです。
この仕組みを簡単に理解するために、convert コマンドに、デバッグ出力をする「-debug」オプションを加えてみます。
まずは、 -define jpeg:size=180x120 をつけない場合です。
$ convert-debug Coder -log %e -resize 180x120 src.jpg dst.jpgProfile:exif, 45952 bytesProfile:xmp, 5079 bytesProfile:iptc, 76 bytesInterlace: nonprogressiveData precision: 8Geometry: 4288x2848 :
Geometry という項目が、 4288x2848 と出力されています。これは、 src.jpg が本来の 4288x2848 として開かれたことを示しています。
次に、 -define jpeg:size=180x120 を付けてみます。
$ convert -debug Coder -log %e-define jpeg:size=180x120 -resize 180x120 src.jpg dst.jpgProfile:exif, 45952 bytesProfile:xmp, 5079 bytesProfile:iptc, 76 bytesScale factor: 23.733333333333334281Interlace: nonprogressiveData precision: 8Geometry: 536x356 :
このとき、 Geometry は 536x356 となっており、この src.jpg は一旦、元画像の 1/8 である 536x356 として開かれたことがわかります。
今回は、ImageMagick で、JPEG画像の縮小を通常の10倍速くする方法を紹介しました。使えるシーンではどんどん -define jpeg:size をつけてみましょう。
この -define jpeg:size オプションは、ImageMagick-6.5.x 以降から使うことができます。それより前のバージョンでは、-sizeオプションを使うことで同様の効果が得られます。
convert コマンドのオプションは -define jpeg:size=... ですが、同様の効果を MagickWand から得るためには、 MagickReadImage等 の前に
MagickSetOption(wand,"jpeg:size","180x120");
を呼べばOKです。
ImageMagick の話をすると、必ず「ImageMagick から派生したGraphicsMagick のほうが速いよ!」と教えてくれる人が出てくるんですが、リサイズに関しては公式のベンチマークでも別にそんな速くないし、手元でもセットアップが大変なわりに有意な速度差が出ませんでした。あとブランチ元のImageMagick のバージョンが古すぎて非常に使いづらいです。
*1:ImageMagick-6.4.9 かそれ以前のバージョンでは、 -define jpeg:size=... でなく -size=... とします
*2:前回の記事よりCPUが貧弱なMacBook Air 11inch で計測しているのでやや遅めです
引用をストックしました
引用するにはまずログインしてください
引用をストックできませんでした。再度お試しください
限定公開記事のため引用できません。