Asial Blog

Recruit! Asialで一緒に働きませんか?

[GD] imageantialiasを使用せず画像の拡大・縮小でアンチエイリアス処理を行う

カテゴリ :
バックエンド(プログラミング)
タグ :
Tech
GD
アンチエイリアス
こんばんは。松田です。
最近はGDで画像処理を書いているので、今回はGDに関する小ネタです。
imageantialiasを使用せず、画像の拡大・縮小を利用してアンチエリアス処理を行ってみます。

GDで作成した画像にアンチエイリアス処理をかけて滑らかにする場合、通常はimageantialias()関数を使用します。
ですが、この関数には欠点があります。
GDで線を描画する際にimagesetthickness()で線の太さを変更しても、imageantialias()をかけると1ピクセルの線に書き換えられてしまう、という問題です。

これを回避するためにいろいろな処理を試した結果、数倍に拡大して描画した画像を元の大きさに縮小して表示する、という方法にたどり着きました。
スーパーサンプリング処理という名前が付いているらしいです。
この方法を使用すれば、線の太さを保ったまま線を滑らかにすることができます。

下が検証に使用したコードです。
  1. <?php
  2.  
  3. /*
  4. * 円と線を描画したimage resourceを返す関数。
  5. * 通常は400*400の画像を作成する
  6. * 引数を指定すると、その倍率分だけ描画する範囲を広げる
  7. *
  8. * draw(3)の場合、400*3=1200ピクセルの画像を描画した後、縮小処理を行う
  9. */
  10. function draw($bai = 1) {
  11.   
  12.   // 通常は400px * 400px の画像を描画
  13.   $width = 400 * $bai;
  14.   $height = 400 * $bai;
  15.   $im = ImageCreateTrueColor($width, $height);
  16.  
  17.   $white = ImageColorAllocate($im, 255, 255, 255);
  18.   $black = ImageColorAllocate($im, 0, 0, 0);
  19.   $blue  = ImageColorAllocate($im, 0, 0, 255);
  20.  
  21.   // 背景を真っ白に
  22.   imagefilltoborder($im, 0, 0, $white, $white);
  23.  
  24.   // 線の太さを5に設定
  25.   imagesetthickness($im, 5*$bai);
  26.  
  27.   // 楕円描画
  28.   imagefilledellipse($im, $width/2, $height/2, 200*$bai, 250*$bai, $blue);
  29.  
  30.   // 線を描画
  31.   imageline($im, 10*$bai, 10*$bai, 300*$bai, 400*$bai, $black);
  32.  
  33.   if ($bai > 1) {
  34.     // 倍率が指定されていたら、400*400に縮小する
  35.     $dst = imagecreatetruecolor(400, 400);
  36.     imagecopyresampled($dst, $im, 0, 0, 0, 0, 400, 400, 400*$bai, 400*$bai);
  37.     imagedestroy($im);
  38.     return $dst;
  39.   }
  40.  
  41.   return $im;
  42. }
  43.  
  44.  
  45. // 5倍まで作成してみる
  46. $max = 5;
  47.  
  48. // このimageに作成したすべての画像をのっけて表示
  49. $im = ImageCreateTrueColor(400, 400 * $max);
  50. for ($i = 1; $i <= $max; $i++) {
  51.   
  52.   // image作成
  53.   $src = draw($i);
  54.   
  55.   imagecopy($im, $src, 0, 400 * ($i - 1), 0, 0, 400, 400);
  56. }
  57.  
  58. header("Content-type: image/png");
  59. imagepng($im);
  60. imagedestroy($im);
  61. exit;
  62.  
  63. ?>

draw()は、線と円を描画するだけの関数ですが、引数を渡すことによって拡大時の画像の大きさを決定しています。
何も渡さなければ400*400の画像を作成しリサイズも行ないません。
draw(3)と指定した場合、1200*1200のキャンバスに拡大した線と円を描画し、その後400*400まで縮小します。

これを試した結果が下の画像です。

上から順に、1倍(未処理)、2倍、…、5倍、の結果となっています。
画像の利サイズ用に、imagecopyresizedとimagecopyresampledという2つの関数が用意されていますが、imagecopyresizedでは画像がガックガクになってしまうため、imagecopyresampledを使います。

画像を見てわかるとおり、下に行くほど線が滑らかになっています。
線の太さもほぼ変わっていません。

ただ、元画像が数千ピクセルにもなってしまうと、画像の作成に体感できる程度の時間がかかってしまいます。
3倍以降からはほぼ見た目が変わっていないため、実際に使用する際は、作成コストを考えながらちょうどいい倍率を探していくのがいいかもしれません。