unoh.github.com

ImageMagickのテキスト描画を画像にしてコストダウン

Thu May 14 03:28:39 -0700 2009

こんにちは。中村です。

本日、まちつく!正式リリースになりました。よろしければ是非携帯でアクセスして遊んでみてください。以前公開しました位置情報ライブラリも利用されています。

さて、PHPでImageMagickを使って画像生成を行うときに、どうもテキストを描画すると無視できないコストがかかることに最近気が付きました。具体的には次のようにImagickDraw::drawImageメソッドによる描画コストです。

<?php
$draw = new ImagickDraw();
$draw->setFontSize(20);
$draw->setFillColor('#FFFFFF');
$draw->annotation(20, 28, 'Hello World');

for ($i = 0; $i < 1000; $i++) {
    $img = new Imagick();
    $img->newImage(160, 40, new ImagickPixel('#550000'));
    $img->drawImage($draw);
    $img->setImageFormat('jpeg');
    $img->writeImage('test.jpg');
}

生成される画像は次のようになります。

helloworld
helloworld posted by (C)フォト蔵

これを実行すると手元の環境では次のようになりました。

% /usr/bin/time -p php test.php
real 12.81
user 12.07
sys 0.71

フリーな文字列を生成する場合には使えないのですが、出現する文字やフォントサイズなどが限られている場合には、これを画像に置き換えることで大分と速度が上がります。次の例では「Hello」と「World」の2つの画像を描画しています。

<?php
$hello = new Imagick('hello.jpg');
$world = new Imagick('world.jpg');
for ($i = 0; $i < 1000; $i++) {
    $img = new Imagick();
    $img->newImage(160, 40, new ImagickPixel('#550000'));
    $img->compositeImage($hello, Imagick::COMPOSITE_OVER, 20, 15);
    $img->compositeImage($world, Imagick::COMPOSITE_OVER, 65, 15);
    $img->setImageFormat('jpeg');
    $img->writeImage('test.jpg');
}
% /usr/bin/time -p php test.php
real 2.08
user 1.94
sys 0.13

約13秒から約2秒にまでなりました。ただし、10文字の描画に2枚の画像では不公平な感じもしますので、それぞれ文字単位に画像にして描画してみました。

<?php
$h = new Imagick('h.jpg');
$e = new Imagick('e.jpg');
$l = new Imagick('l.jpg');
$o = new Imagick('o.jpg');
$w = new Imagick('w.jpg');
$r = new Imagick('r.jpg');
$d = new Imagick('d.jpg');

for ($i = 0; $i < 1000; $i++) {
    $img = new Imagick();
    $img->newImage(160, 40, new ImagickPixel('#550000'));
    $img->compositeImage($h, Imagick::COMPOSITE_OVER, 20, 15);
    $img->compositeImage($e, Imagick::COMPOSITE_OVER, 30, 15);
    $img->compositeImage($l, Imagick::COMPOSITE_OVER, 40, 15);
    $img->compositeImage($l, Imagick::COMPOSITE_OVER, 50, 15);
    $img->compositeImage($o, Imagick::COMPOSITE_OVER, 60, 15);
    $img->compositeImage($w, Imagick::COMPOSITE_OVER, 80, 15);
    $img->compositeImage($o, Imagick::COMPOSITE_OVER, 90, 15);
    $img->compositeImage($r, Imagick::COMPOSITE_OVER, 100, 15);
    $img->compositeImage($l, Imagick::COMPOSITE_OVER, 110, 15);
    $img->compositeImage($d, Imagick::COMPOSITE_OVER, 120, 15);
    $img->setImageFormat('jpeg');
    $img->writeImage('test.jpg');
}
/usr/bin/time -p php test.php
real 2.31
user 2.12
sys 0.15

小さな画像が増えてもそれほどコストが上がることはないようです。

大量に画像を生成するようなケースで、かつ文字のパターンが少ない場合に限られますが、速度が必要な場合にはテキスト描画を画像に置き換えた方が有利のようです。

いずれにしても、自分の利用する環境でのベンチが大切だと思いますので、参考程度に見てもらえればと思います。