web-dev-qa-db-ja.com

組み込みのガウス関数を使用せずに画像をガウスぼかしするにはどうすればよいですか?

ネイティブのガウスぼかし式を使用して画像をぼかしたい。 this と読みましたが、これを実装する方法がわかりません。

数式を使用して重みを決定するにはどうすればよいですか?

MATLABのような組み込み関数を使用したくない

53
Moeb

単純なガウスぼかしを書くのは、実際には非常に簡単です。他の畳み込みフィルターとまったく同じ方法で行われます。ボックスとガウスフィルターの唯一の違いは、使用するマトリックスです。

次のように定義されたイメージがあるとします。

 0  1  2  3  4  5  6  7  8  9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32 33 34 35 36 37 38 39
40 41 42 43 44 45 46 47 48 49
50 51 52 53 54 55 56 57 58 59
60 61 62 63 64 65 66 67 68 69
70 71 72 73 74 75 76 77 78 79
80 81 82 83 84 85 86 87 88 89
90 91 92 93 94 95 96 97 98 99

3x3ボックスフィルターマトリックスは次のように定義されます。

0.111 0.111 0.111
0.111 0.111 0.111
0.111 0.111 0.111

ガウスぼかしを適用するには、次の手順を実行します。

ピクセル11の場合、ピクセル0、1、2、10、11、12、20、21、22を読み込む必要があります。

次に、ピクセル0に3x3ぼかしフィルターの左上部分を掛けます。ピクセル1を中央上部、ピクセル2、ピクセル3を右上、ピクセル10を中央左などとします。

次に、それらをすべて追加し、ピクセル11に結果を書き込みます。ご覧のとおり、Pixel 11は、それ自体と周囲のピクセルの平均になりました。

エッジケースはもう少し複雑になります。テクスチャのエッジの値に使用する値は何ですか? 1つの方法は、反対側にラップすることです。これは、後で並べて表示される画像に適しています。別の方法は、ピクセルを周囲の場所にプッシュすることです。

そのため、左上には次のようにサンプルを配置できます。

 0  0  1
 0  0  1
10 10 11

これがどのように簡単に大きなフィルターカーネル(5x5や9x9など)に拡張できるかをご覧ください。

ガウスフィルターとボックスフィルターの違いは、行列に入る数値です。ガウスフィルターは、行と列にわたるガウス分布を使用します。

例:任意に定義されたフィルターの場合(つまり、これはガウスではありませんが、おそらくそれほど遠くない)

0.1 0.8 0.1

最初の列は同じですが、上の行の最初の項目に乗算されます。

0.01 0.8 0.1
0.08 
0.01 

2番目の列は同じですが、値は上の行の0.8で乗算されます(以下同様)。

0.01 0.08 0.01
0.08 0.64 0.08
0.01 0.08 0.01

上記のすべてを一緒に追加した結果は1になります。上記のフィルターと元のボックスフィルターの違いは、書き込まれた最終ピクセルが中心ピクセル(つまり、その位置にあるピクセル)に対してより重い重みを持つことです既に)。ブラーは、周囲のピクセルがそれほど大きくはないが、そのピクセルにブラーするために発生します。この種類のフィルターを使用すると、ぼかしが得られますが、高周波数(つまり、ピクセルからピクセルへの色の急速な変化)の情報をほとんど破壊しません。

これらの種類のフィルターは、多くの興味深いことができます。現在のピクセルから周囲のピクセルを減算することにより、この種のフィルターを使用してエッジ検出を行うことができます。これにより、色の非常に大きな変化(高周波)のみが残されます。

編集:5x5フィルターカーネルは上記とまったく同じように定義されます。

たとえば、行が0.1 0.2 0.4 0.2 0.1である場合、それらの各値に最初のアイテムを乗算して列を形成し、次にそれぞれに2番目のアイテムを乗算して2番目の列を形成すると、フィルターになりますの

0.01 0.02 0.04 0.02 0.01
0.02 0.04 0.08 0.04 0.02
0.04 0.08 0.16 0.08 0.04
0.02 0.04 0.08 0.04 0.02
0.01 0.02 0.04 0.02 0.01

任意の位置を取ると、位置0、0は単純な0.1 * 0.1であることがわかります。位置0、2は0.1 * 0.4、位置2、2は0.4 * 0.4、位置1、2は0.2 * 0.4です。

これで十分な説明ができればと思います。

129
Goz

カーネルを計算するためにC#で使用したコードの擬似コードを次に示します。ただし、終了条件を正しく処理するとは言いません。

double[] kernel = new double[radius * 2 + 1];
double twoRadiusSquaredRecip = 1.0 / (2.0 * radius * radius);
double sqrtTwoPiTimesRadiusRecip = 1.0 / (sqrt(2.0 * Math.PI) * radius);
double radiusModifier = 1.0;

int r = -radius;
for (int i = 0; i < kernel.Length; i++)
{
    double x = r * radiusModifier;
    x *= x;
    kernel[i] =
    sqrtTwoPiTimesRadiusRecip * Exp(-x * sqrtTwoPiTimesRadiusRecip);
    r++;
}

double div = Sum(kernel);
for (int i = 0; i < kernel.Length; i++)
{
    kernel[i] /= div;
}

これが役立つことを願っています。

11

Wikipediaの記事で説明されているフィルターカーネルを使用するには、(離散) convolution を実装する必要があります。アイデアは、値の小さなマトリックス(カーネル)を持ち、このカーネルを画像内のピクセルからピクセルに移動し(つまり、マトリックスの中心がピクセル上にあるように)、マトリックス要素をオーバーラップした画像で乗算することです。要素で、結果のすべての値を合計し、古いピクセル値をこの合計で置き換えます。

ガウスぼかしは、2D畳み込みの代わりに2つの1D畳み込み(1つは垂直方向、1つは水平方向)に分割できます。

9
Markus Johnsson

これを制限するかどうかは明確ではありませんto特定の技術ですが、そうでない場合は SVG(ScalableVectorGraphics) の実装がありますガウスぼかし。ピクセルを含むすべてのプリミティブに適用されると思います。 SVGには、オープン標準であり、広く実装されているという利点があります。

3

さて、ガウスカーネルは分離可能なカーネルです。
したがって、必要なのは、- ImageConvolutionSeparableKernel() のような分離可能な2D畳み込みをサポートする関数だけです。

必要なのは、1D Gaussian Kernelを生成し、それを ImageConvolutionGaussianKernel() のように関数に送信するラッパーだけです。

このコードは、SIMD(SSE)およびMulti Threading(OpenMP)によって加速される2D Image Convolutionの単純なC実装です。

プロジェクト全体は- Image Convolution-GitHub で与えられます。

0
Royi