如何有效扫描2d numpy阵列?

时间:2013-03-20 16:11:35

标签: python opencv numpy

我需要扫描图像,看看每个像素的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数组?

实际上我正试图将这个thinning code从C ++移植到Python。

4 个答案:

答案 0 :(得分:3)

我最终使用scipy.weave来编写用于迭代Numpy数组的内联C ++代码。它使代码运行得非常快。以前天真的方法需要134秒才能完成处理300x150图像。虽然这种方法只需要75毫秒。

如果您感兴趣,以下是Python中的完整细化代码:

# 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()

示例源图像和细化结果:

enter image description here enter image description here

有用的教程:Python Numpy Performance

答案 1 :(得分:1)

你可以用三个回合来做到这一点。创建三个模板/掩码数组

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) & ...

答案 2 :(得分: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]为中心的窗口的布尔值,并且在每个方向上比原始图像短两个项目。您可以为整个图像获取一个布尔数组:

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)数组,其中包含左上角3x3图片i, j窗口的内容。

如果您所使用的模式是形状为pattern的数组(3, 3),则可以执行以下操作:

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

获取两个数组的元组,其中窗口左上角的行和列与模式匹配。

这里唯一的问题是,当你执行win_img == pattern时,会创建一个9倍于图像大小的数组,如果图像非常大,这可能会很麻烦。如果您有内存问题,请将模式检查分成几个波段并循环遍历它们。超过10个波段的for循环仍将比当前两个嵌套循环在图像的整个宽度和高度上快得多。

答案 3 :(得分:0)

您可以尝试以下方法:

  1. 直接从图像中读取测试值,而不是创建临时变量;
  2. 首先执行较便宜的测试,因为布尔测试是短路的。
  3. 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