猫好きモバイルアプリケーション開発者記録

Imlib2とImage::Imlib2のJPEGヒント対応版を作成しました

| Comments

久々に作ったものをGithubへ公開しました。
元ネタは、よくImageMagickと比較される Imlib2 です。
これのJPEGヒント対応版が欲しくなったので探したのですが、どこにも見当たらなかったのでいっそのこと自分で作りました。
(対応についてはSmallLightのソースを参考にさせて頂きました)

JPEGヒント(scale denom)とは

まずJPEGヒントとは何かというと、ImageMagickやImlib2で利用しているlibjpeg ver.7以降で利用することの出来る scale_denom というスケーリング手法の俗称です。
これは簡単に言うと予めリサイズ後のサイズがわかっている場合に、元画像ロード時にメモリを効率よく確保しつつリサイズを高速化する手法です。
ImageMagickではこの機能をサポートしており、「-define jpeg:size=640x480」といったオプションをつけることで利用することができます。
よく「ImageMagickは遅い」と言われるのですが、このJPEGヒントを利用することで10倍近くの高速化が測れます。

ところが、Imlib2にはこのオプションが存在せず、APIにもそれをサポートしたものがありません。 APIの設計上、JPEGのみに特化した設計にはなっていないというのも理由の1つかも知れませんが、 JPEGヒントがないImlib2は単純なリサイズですとJPEGヒントを利用したImageMagickと同等か少し遅くなってしまうため、ベースの速度が速いのにこのままでは非常に勿体ないわけです。

JPEGヒントをサポートしたImlib2の利用

そこで今回、Imlib2を拡張してJPEGヒントが利用できるように実装しました。
実際に利用する場合はPerlのライブラリであるImage::Imlib2を経由して利用します。
このImage::Imlib2もJPEGヒントに対応させるために少し拡張しております。
実際の利用イメージとしては以下のような感じです。

1
2
3
4
5
6
7
use Image::Imlib2;

# 元サイズ1920x1200の画像を使用
my $scaledImage = Image::Imlib2->load_scale('sample.jpg', 640, 480);
$scaledImage = $scaledImage->create_scaled_image(640, 480);
$scaledImage->set_quality(100);
$scaledImage->save('sample_resized.jpg');

「load_scale」というメソッドを追加しており、これを利用することでロード時にリサイズ後のサイズを指定します。 あとは普通にリサイズして書き出してみます。 これだけで、このサイズの場合は元のJPEGヒントを利用しない場合に比べて 1.5 倍ほど高速化しております。

チューニング対応したImageMagickとの比較

それでは、ImageMagickと速度比較してみます。
対等に比較を行うために、まずImageMagickの高速化を図ります。
具体的には、以下のオプションを指定して予めコンパイルしておきます。

01. 高速化のためのImageMagickのコンパイルオプション

01. —disable-openmp を指定してOpenMPを無効にする
→これが有効になっていると、マルチスレッド実行時に異常にメモリを消費して重くなる

02. —with-quantum-depth=8 を指定してRPG分解能を 8 bitにする。
→通常はこれで十分に24/32 bitの画像を処理できます。この指定をしなくても、convert 実行時に -depth 8 をオプションとして渡してもOK

特に 01 が重要になります。
画像のサムネイル作成を高速化する場合、最初に出てくる発想としてはマルチスレッドで実行することがあげられますが、 OpenMPはそのマルチスレッドによる実行時に異常な負荷をあげる原因となっているからです。 02は -depth 8 を convert 実行時に指定しても良いと思いますが、コマンド実行時に特に意識させずに高速化したいのであればコンパイル時に指定してしまいましょう。

02. libjpeg-turboの導入

ImageMagickもImlib2も通常はlibjpegを利用しますが、このlibjpegよりも更に高速に処理ができる libjpeg-turbo というライブラリがあります。 これを利用することで更に 1.3 倍ほど高速になります。 導入方法は簡単で、こちらより最新版のRPMをダウンロードしてインストールします。 すると、/opt 配下へインストールされますので、以下のようにして共有ライブラリファイルとして認識させます。

1
2
echo /opt/libjpeg-turbo/lib64 > /etc/ld.so.conf.d/libjpeg-turbo.conf
ldconfig

これでインストール済みのImageMagickとImlib2が libjpeg-turbo の方を利用するようになります。

03. 実効速度の比較

準備が完了したところで、実効速度を比較してみます。
今回の検証では 1920x1200 のJPEG画像を 640x480 へ 1000 回リサイズした場合の速度差を比較しています。
具体的には以下の内容のシェルスクリプトを作成して速度比較してみました。

<ImageMagick>

1
2
3
4
5
6
7
8
#!/bin/bash

time /bin/bash << TIMEEND
for (( i = 0; i < 1000; i++ ))
do
    convert -define jpeg:size=640x480 -quality 100 -scale 640x480 sample.jpg sample_resized.jpg
done
TIMEEND

<Imlib2>
シェルスクリプトから呼び出すPerlスクリプト (resize.pm とします)

1
2
3
4
5
6
7
8
9
#!/usr/bin/env perl

use strict;
use Image::Imlib2;

my $scaledImage = Image::Imlib2->load_scale('sample.jpg', 640, 480);
$scaledImage = $scaledImage->create_scaled_image(640, 480);
$scaledImage->set_quality(100);
$scaledImage->save('sample_resized.jpg');

実行するシェルスクリプト

1
2
3
4
5
6
7
8
#!/bin/bash

time /bin/bash << TIMEEND
for (( i = 0; i < 1000; i++ ))
do
    `dirname $0`/resize.pm
done
TIMEEND

結果

<ImageMagick>

1
2
3
real    1m12.344s
user    0m58.140s
sys     0m8.317s

<Imlib2>

1
2
3
real    0m52.510s
user    0m36.622s
sys     0m9.565s

チューニングしたImageMagickと比較しても、JPEGヒントを利用したImlib2はかなり速いことがわかります。 画質についても素人目には違いが判りません。

まとめ

Imlib2はこのように速度が非常に速いことが利点ですが、 欠点としてはWindowsやMacへ導入するとなると少々面倒くさいので、開発時のデバッグが容易に出来ません。(その点、ImageMagickは簡単に導入できます) また、GIF画像の書き出しもサポートしていないので、GIF画像を扱う必要がある場合はImageMagickと併用するなどして対応する必要があります。 そのため、GIF以外の画像(特にJPEG画像)を多く扱い、速度を追求する必要が有る場合はImlib2の利用をお勧めします。

開発時にWindowsやMacで利用する場合は頑張ってImlib2をインストールするのもありですが、 どちらかといえばImageMagickとImlib2を同じインターフェースで利用出来るクラスを作成して、環境ごとに切り替えする方がいいかもしれません。 これについてはJavaのクラスとして近々作成予定ですので、完成次第展開いたします。

今回作成したJPEGヒント対応版のImlib2とImage::Imlib2については以下よりダウンロードしてください。

Imlib2
Image::Imlib2

Comments