0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

PHP imagick convolveImage の引数の変更

Last updated at Posted at 2022-02-16

PHP imagick の convolveImage は画像の畳み込み処理するメソッドです。
この convolveImage は、imagick 3.3.0 => 3.4.0 を境に受け取る引数の形式が変わりましたが、公式マニュアルが分かりにくいのでメモを作りました。

convolveImage の機能

画像に対して畳み込みの処理ができます。
例えば、以下のような畳み込みカーネルを与えるとブラー効果を得られます。

\frac{1}{16}
\begin{vmatrix}
1 & 2 & 1 \\
2 & 4 & 2 \\
1 & 2 & 1 \\
\end{vmatrix}
元画像 convoveImage 適用後
input.jpg output.jpg
rg-grid.png blur.png

この 3x3 で 9個のカーネル係数を引数に渡す方法が imagick や ImageMagick のバージョンによって異なります。

バージョンによる差異

imagick 3.3.0 以前

カーネル係数を一次元の配列で渡します。個数は N^2 に限定されます。以下の例だと 3^2 = 3x3 で 9 個の要素を持つ配列です。

$kernel = [1/16, 2/16, 1/16,
           2/16, 4/16, 2/16,
           1/16, 2/16, 1/16];
$image->convolveImage($kernel);

convolveImage は渡された配列を正方行列にして処理します。
今回は 9個の配列を渡したので、3x3 行列として処理します。
25個を渡すと 5x5 です。つまり縦長や横長のカーネルは使えません。

imagick 3.4.0 以降

2次元の配列を ImageKernel オブジェクトで渡します。

$matrix = [ [1/16, 2/16, 1/16],
            [2/16, 4/16, 2/16],
            [1/16, 2/16, 1/16] ];
$kernel = \ImagickKernel::fromMatrix($matrix);
$image->convolveImage($kernel);

ImageMagick 6

ImageMagick 6 では imagick 3.4.0 以降だとしても imagick 3.3.0 以前の引数形式のままになります。

$kernel = [1/16, 2/16, 1/16,
           2/16, 4/16, 2/16,
           1/16, 2/16, 1/16];
$image->convolveImage($kernel);

バージョン分岐

新旧のバージョンに対応する場合、以下のように分岐すると良いでしょう。

$imVersion = \Imagick::getVersion();
if (($imVersion["versionNumber"] < 0x700) || (phpversion("imagick") < "3.4.0")) {  // 古い方
    $kernel = [1/16, 2/16, 1/16,
               2/16, 4/16, 2/16,
               1/16, 2/16, 1/16];
    $image->convolveImage($kernel);
} else {  // 新しい方
    $matrix = [ [1/16, 2/16, 1/16],
                [2/16, 4/16, 2/16],
                [1/16, 2/16, 1/16] ];
    $kernel = \ImagickKernel::fromMatrix($matrix);
    $image->convolveImage($kernel);
} 

バージョンを間違えた場合

以下のようなエラーメッセージが出るはずです。

Imagick::convolveImage() expects parameter 1 to be object, array given

Imagick::convolveImage() expects parameter 1 to be array, object given

何故 ImageMagick 6 だと古いままなのか

ImageMagick 6 の convolve API が正方行列しか対応していないためです。

  • ImageMagick 7 用 imagick 実装
imagick-3.6.0/imagick_class.c
        kernel = Z_IMAGICKKERNEL_P(objvar);

        IMAGICK_KERNEL_NOT_NULL_EMPTY(kernel);
        status = MagickConvolveImageChannel(intern->magick_wand, channel, kernel->kernel_info);
  • ImageMagick 6 用 imagick 実装
imagick-3.6.0/imagick_class.c
        order = (unsigned long) sqrt(num_elements);
        status = MagickConvolveImageChannel(intern->magick_wand, channel, order, kernel);
        efree(kernel);

サイズ情報として rank(整数値) しか渡してないので、縦と横を別にできません。

備考

マニュアルの記述

マニュアルが間違えているのは imagick 実装の PHPDoc コメントによるところもあるので、先月(2022/1/10)に修正 PR を送ってあります。なお、今のところ反応はありません。

セパラブルフィルタ

ImageMagick 7, imagick 3.4.0 以降の引数形式だと、縦と横のサイズを任意にできるので、例えば以下のような横だけブラーが最小限の処理で可能です。

$matrix = [ [1/4, 2/4, 1/4] ];  // horizontal blur
$kernel = \ImagickKernel::fromMatrix($matrix);
$image->convolveImage($kernel);
\frac{1}{4}
\begin{vmatrix}
1 & 2 & 1 \\
\end{vmatrix}
元画像 横ブラー
input.jpg th.jpg
rg-grid.png blur-hori.png

横でブラーをかけた後、更に縦ブラーもかければ、当初の 3x3 行列と同等の結果が得られます。

\frac{1}{4}
\begin{vmatrix}
1 \\
2 \\
1 \\
\end{vmatrix}
$matrix1 = [ [1/4, 2/4, 1/4] ];  // horizontal blur
$matrix2 = [ [1/4],
             [2/4],
             [1/4] ];  // vertical blur
$kernel1 = \ImagickKernel::fromMatrix($matrix1);
$kernel2 = \ImagickKernel::fromMatrix($matrix2);
$image->convolveImage($kernel1);  // horizontal blur
$image->convolveImage($kernel2);  // vertical blur
元画像 横ブラー
input.jpg hori.jpg
縦ブラー 横-縦ブラー
vert.jpg horivert.jpg
元画像 横ブラー
rg-grid.png blur-hori.png
縦ブラー 横-縦ブラー
blur-vert.png blur-hori-vert.png

3x3 だと微妙ですが、それより巨大になればなるほど、縦と横で分けた方が高速化できます。セパラブルフィルタと呼ばれます。

1次元配列の N^2 縛り

古い引数の取り方だと、1次元配列で N^2 に個数が限定されると説明しました。
これには少し注意が必要で、imagick 実装を見てみると、

imagick-3.6.0/imagick_class.c
order = (unsigned long) sqrt(num_elements);
status = MagickConvolveImageChannel(intern->magick_wand, channel, order, kernel);

なので、N^2 に合わない場合はエラーにならず、後ろの方の要素を切り捨てるようです。
例えば 10個要素の配列を渡した場合は 9 つだけ使われるように思われます。(未検証)

まとめ

  • imagick 3.3.0 以前、又は ImageMagick 6 だと、1次元配列を引数にとる。正方行列として解釈される。
  • imagick 3.4.0 以前、かつ ImageMagick 7 だと 2次元配列を包んだ ImageKernel を引数に取る。任意の形の行列を渡せるのでセパラブルフィルタに便利。
  • ImageMagick 6 を使っている場合、出来れば 7 にあげましょう。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?