4

画像をスキャンして、各ピクセルの 3x3 ウィンドウの値が特定のパターンと一致するかどうかを確認する必要があります。次のコードを使用します

import numpy as np
import cv2

im = cv2.imread("image.png")
h, w = im.shape[:2]

for i in range(1, h-1):
    for j in range(1, w-1):
        p2 = im[i-1, j]
        p3 = im[i-1, j+1]
        p4 = im[i, j+1]
        p5 = im[i+1, j+1]
        p6 = im[i+1, j]
        p7 = im[i+1, j-1]
        p8 = im[i, j-1]
        p9 = im[i-1, j-1]
        # code for checking the pattern looks something like this:
        if (p2 + p3 + p9) == 1 and p4 == 0 and p5 == 1:
            val = True

しかし、上記のコードは完了するまでに時間がかかります。私はPythonとnumpyが初めてです.2d numpy配列を効果的にスキャンするにはどうすればよいですか?

実際、私はこの間引きコードを C++ から Python に移植しようとしています。

4

4 に答える 4

3

I ended up with scipy.weave to write inline C++ code for iterating the Numpy array. It makes the code runs very fast. The previously naive approach took 134 seconds to finish processing 300x150 image. While this approach takes only 75ms.

Here is the complete thinning code in Python if you interested:

# Code for thinning a binary image using Zhang-Suen algorithm
from scipy import weave
import numpy as np
import cv2
import sys

def _thinningIteration(im, iter):
    I, M = im, np.zeros(im.shape, np.uint8)
    expr = """
    for (int i = 1; i < NI[0]-1; i++) {
        for (int j = 1; j < NI[1]-1; j++) {
            int p2 = I2(i-1, j);
            int p3 = I2(i-1, j+1);
            int p4 = I2(i, j+1);
            int p5 = I2(i+1, j+1);
            int p6 = I2(i+1, j);
            int p7 = I2(i+1, j-1);
            int p8 = I2(i, j-1);
            int p9 = I2(i-1, j-1);

            int A  = (p2 == 0 && p3 == 1) + (p3 == 0 && p4 == 1) +
                     (p4 == 0 && p5 == 1) + (p5 == 0 && p6 == 1) +
                     (p6 == 0 && p7 == 1) + (p7 == 0 && p8 == 1) +
                     (p8 == 0 && p9 == 1) + (p9 == 0 && p2 == 1);
            int B  = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;
            int m1 = iter == 0 ? (p2 * p4 * p6) : (p2 * p4 * p8);
            int m2 = iter == 0 ? (p4 * p6 * p8) : (p2 * p6 * p8);

            if (A == 1 && B >= 2 && B <= 6 && m1 == 0 && m2 == 0) {
                M2(i,j) = 1;
            }
        }
    } 
    """
    weave.inline(expr, ["I", "iter", "M"])
    return (I & ~M)


def thinning(src):
    dst = src.copy() / 255
    prev = np.zeros(src.shape[:2], np.uint8)
    diff = None

    while True:
        dst = _thinningIteration(dst, 0)
        dst = _thinningIteration(dst, 1)
        diff = np.absolute(dst - prev)
        prev = dst.copy()
        if np.sum(diff) == 0:
            break

    return dst * 255

if __name__ == "__main__":
    src = cv2.imread("image.png")
    if src == None:
        sys.exit()
    bw = cv2.cvtColor(src, cv2.cv.CV_BGR2GRAY)
    _, bw2 = cv2.threshold(bw, 10, 255, cv2.THRESH_BINARY)
    bw2 = thinning(bw2)
    cv2.imshow("src", bw)
    cv2.imshow("thinning", bw2)
    cv2.waitKey()

Sample source image and the thinning result:

enter image description here enter image description here

A useful tutorial: Python Numpy Performance

于 2013-03-22T06:41:23.780 に答える
1

3 つの畳み込みでこれを行うことができます。3 つのテンプレート/マスク配列を作成する

1/3 0 0
1/3 0 0
1/3 0 0

0 0 0
0 0 1
0 0 0

0 0 0
0 0 0
0 0 1

各配列で畳み込みを実行します。次に、結果は次のようになります。

output = (convolved_with_first == 1) & (convolved_with_second == 0) & ...
于 2013-03-20T16:38:45.273 に答える
0

編集実際のパターン検索を考えると、次のようなものになります。

from numpy.lib.stride_tricks import as strided
win_img = as_strided(im, shape=(h, w - 3 + 1, 3),
                     strides=im.strides + im.strides[-1:])
cond_1 = np.sum(win_img, axis=-1) == 1
cond_2 = im == 0
cond_3 = im == 1

cond = cond_1[:-2, :] & cond_2[1:-1, 2:] & cond_3[2:, 2:]

cond[i, j]を中心とするウィンドウのブール値になりim[i+1, j+1]、元の画像より各方向に 2 アイテム短くなります。次のように、画像全体のブール配列を取得できます。

cond_im = np.zeros_like(im, dtype=bool)
cond_im[1:-1, 1:-1] = cond

アレイをウィンドウ ビューで表示します。

from numpy.lib.stride_tricks import as strided
win_img = as_strided(im, shape=(h - 3 + 1, w - 3+ 1 , 3, 3),
                     strides=im.strides * 2)

これで、画像のウィンドウの内容が左上隅にwin_img[i, j]ある(3, 3)配列になります。3x3i, j

求めているパターンがpatternshape の配列である場合は、次の(3, 3)ように簡単に実行できます。

np.where(np.all(np.all(win_img == pattern, axis=-1), axis=-1))

パターンが一致するウィンドウの左上隅の行と列を使用して、2 つの配列のタプルを取得します。

ここでの唯一の問題は、 を実行するwin_img == patternと、画像のサイズの 9 倍の配列が作成されることです。これは、画像が非常に大きい場合に問題になる可能性があります。メモリに問題がある場合は、パターン チェックをいくつかのバンドに分割し、それらをループします。10 バンドにわたる for ループは、画像の幅と高さ全体にわたる現在の 2 つのネストされたループよりもはるかに高速です。

于 2013-03-20T16:26:47.180 に答える
0

次のアプローチを試すことができます。

  1. 一時変数を作成する代わりに、イメージから直接テスト値を読み取ります。
  2. ブール値のテストは短絡的であるため、コストの低いテストを最初に実行します。

.

result_array = numpy.zeros((h-2, w-2)).astype(bool)

for i in xrange(1, h-1):
    for j in xrange(1, w-1):
        if (im[i, j+1] == 0 and
            im[i+1, j+1] == 1 and
            im[i-1,j] + im[i-1,j+1] + im[i-1, j-1]):
                result_array[i,j] = True 
于 2013-03-20T17:47:05.533 に答える