2

それらのマトリックスはどのように機能しますか?ピクセルごとに複数にする必要がありますか?周囲のピクセルがない左上、右上、左下、左下のピクセルはどうですか?そして、マトリックスは最初に左から右へ、上から下へ、または上から下へ、次に左から右へと機能しますか?

このカーネル(エッジエンハンス)がなぜ このイメージに変わるのか:http: //i.stack.imgur.com/d755G.png :http: //i.stack.imgur.com/NRdkK.jpg

4

3 に答える 3

1

Convolution フィルタは、すべての単一ピクセルに適用されます。

端では、できることがいくつかあります (すべて、一種の境界線を残すか、画像を縮小します)。

  1. エッジをスキップし、画像のエッジから 1 ピクセルを切り取ります
  2. 画像の範囲外のピクセルを 0 または 255 に置き換えます
  3. 0 (または 255) と画像のエッジ ピクセルの値の間で 3 次スプライン (または他の補間法) を使用して、代用を考え出します。

畳み込みを適用する順序は関係ありません (右上から左下が最も一般的です)。順序に関係なく同じ結果が得られます。

ただし、畳み込み行列を適用するときによくある間違いは、調べている現在のピクセルを新しい値で上書きすることです。これは、現在のピクセルの隣にあるピクセルの値に影響します。より良い方法は、計算された値を保持するためのバッファーを作成することです。これにより、畳み込みフィルターの以前のアプリケーションが行列の現在のアプリケーションに影響を与えないようにします。

サンプル画像から、適用されたフィルターが元の画像を見ずに白黒バージョンを作成する理由を判断するのは困難です。

于 2013-04-29T19:08:43.643 に答える
0

以下は、畳み込みカーネルを画像に適用する段階的な例です (簡単にするために 1D)。

ステップごとの畳み込みの例

投稿のエッジ拡張カーネルについては、-1 の隣に +1 があることに注意してください。それが何をするかを考えてください。領域が一定の場合、+/-1 の下の 2 つのピクセルが加算されてゼロ (黒) になります。2 つのピクセルが異なる場合、ゼロ以外の値になります。つまり、隣り合った異なるピクセルが強調表示され、同じピクセルが黒に設定されていることがわかります。差が大きいほど、フィルタリングされた画像のピクセルは明るく (より白く) なります。

于 2013-04-30T01:29:25.950 に答える
0

はい、すべてのピクセルにその行列を掛けます。従来の方法では、畳み込み対象のピクセルに関連する関連ピクセルを見つけ、係数を掛けて平均化します。したがって、次の 3x3 ぼかし:

1, 1, 1,
1, 1, 1,
1, 1, 1

このマトリックスは、さまざまなコンポーネントの関連する値を取得して乗算することを意味します。次に要素数で割ります。したがって、3 x 3 のボックスを取得し、すべての赤の値を合計してから 9 で割ります。3 x 3 のボックスを取得し、すべての緑の値を加算してから 9 で割ります。3 x 3 を取得します。ボックスで、すべての青色の値を合計してから 9 で割ります。

http://intellabs.github.io/RiverTrail/tutorial/畳み込み画像。

これはいくつかのことを意味します。まず、この操作を実行するには、2 つ目の巨大なメモリ チャンクが必要です。そして、できるすべてのピクセルを実行します。

ただし、これは従来の方法の場合に限られ、従来の方法は実際には不必要に複雑です (わかりますか?)。コーナーで結果を返せば。実際に追加のメモリを必要とすることはなく、開始時のメモリ フットプリント内で常に操作全体を実行します。

public static void convolve(int[] pixels, int offset, int stride, int x, int y, int width, int height, int[][] matrix, int parts) {
    int index = offset + x + (y*stride);
    for (int j = 0; j < height; j++, index += stride) {
        for (int k = 0; k < width; k++) {
            int pos = index + k;
            pixels[pos] = convolve(pixels,stride,pos, matrix, parts);
        }
    }
}

private static int crimp(int color) {
    return (color >= 0xFF) ? 0xFF : (color < 0) ? 0 : color;
}

private static int convolve(int[] pixels, int stride, int index, int[][] matrix, int parts) {
    int redSum = 0;
    int greenSum = 0;
    int blueSum = 0;

    int pixel, factor;

    for (int j = 0, m = matrix.length; j < m; j++, index+=stride) {
        for (int k = 0, n = matrix[j].length; k < n; k++) {
            pixel = pixels[index + k];
            factor = matrix[j][k];

            redSum += factor * ((pixel >> 16) & 0xFF);
            greenSum += factor * ((pixel >> 8) & 0xFF);
            blueSum += factor * ((pixel) & 0xFF);
        }
    }
    return 0xFF000000 | ((crimp(redSum / parts) << 16) | (crimp(greenSum / parts) << 8) | (crimp(blueSum / parts)));
}

カーネルでは、伝統的に値を中央の最もピクセルに返します。これにより、画像のエッジの周りがぼやけますが、元の位置に多かれ少なかれ残ります。これは良いアイデアのように思えましたが、実際には問題があります。これを行う正しい方法は、結果のピクセルを左上隅に配置することです。次に、余分なメモリを使用せずに、イメージ全体をスキャンラインで単純に反復し、一度に 1 ピクセルずつ移動して値を返すことができます。エラーは発生しません。色の重みの大部分は、1 ピクセル分上および左にシフトされます。しかし、これは 1 ピクセルであり、結果のピクセルが右下にある状態で逆方向に反復すると、下方向と左方向に戻すことができます。これはキャッシュヒットの問題かもしれませんが。

ただし、最新のアーキテクチャの多くには GPU が搭載されているため、イメージ全体を同時に処理できます。それを一種の論点にする。しかし、グラフィックスで最も重要なアルゴリズムの 1 つがこれを要求するのは奇妙です。これは、操作を実行する最も簡単な方法を不可能にし、メモリを浪費するためです。

そのため、この質問についてマットのような人は、「ただし、畳み込み行列を適用するときによくある間違いは、調べている現在のピクセルを新しい値で上書きすることです。」-- 本当にこれは正しい方法です。エラーは結果ピクセルを左上隅ではなく中央に書き込んでいます。左上隅とは異なり、中央のピクセルが再び必要になるためです。左上隅が再び必要になることはありません (左から右へ、上から下へと反復していると仮定すると) ため、そこに値を保存しても安全です。

「これは、現在のピクセルの隣にあるピクセルの値に影響します。」-- スキャンとして処理したときに左上隅に書き込んでしまうと、必要のないデータを上書きしてしまいます。追加のメモリを大量に使用することは、より良い解決策ではありません。

そのため、おそらくこれまでに見た中で最速の Java ブラーです。

private static void applyBlur(int[] pixels, int stride) {
    int v0, v1, v2, r, g, b;
    int pos;
    pos = 0;
    try {
        while (true) {
            v0 = pixels[pos];
            v1 = pixels[pos+1];
            v2 = pixels[pos+2];

            r = ((v0 >> 16) & 0xFF) + ((v1 >> 16) & 0xFF) + ((v2 >> 16) & 0xFF);
            g = ((v0 >> 8 ) & 0xFF) + ((v1 >>  8) & 0xFF) + ((v2 >>  8) & 0xFF);
            b = ((v0      ) & 0xFF) + ((v1      ) & 0xFF) + ((v2      ) & 0xFF);
            r/=3;
            g/=3;
            b/=3;
            pixels[pos++] = r << 16 | g << 8 | b;
        }
    }
    catch (ArrayIndexOutOfBoundsException e) { }
    pos = 0;
    try {
    while (true) {
            v0 = pixels[pos];
            v1 = pixels[pos+stride];
            v2 = pixels[pos+stride+stride];

            r = ((v0 >> 16) & 0xFF) + ((v1 >> 16) & 0xFF) + ((v2 >> 16) & 0xFF);
            g = ((v0 >> 8 ) & 0xFF) + ((v1 >>  8) & 0xFF) + ((v2 >>  8) & 0xFF);
            b = ((v0      ) & 0xFF) + ((v1      ) & 0xFF) + ((v2      ) & 0xFF);
            r/=3;
            g/=3;
            b/=3;
            pixels[pos++] = r << 16 | g << 8 | b;
        }
    }
    catch (ArrayIndexOutOfBoundsException e) { }
}
于 2016-07-12T12:23:58.553 に答える