使用Python在二维数组(图像)中查找像素邻居

19
我有一个这样的numpy数组:
x = np.array([[1,2,3],[4,5,6],[7,8,9]])

我需要创建一个名为"neighbors"的函数,它具有以下输入参数:

  • x:一个numpy 2d数组
  • (i,j):2d数组中一个元素的索引
  • d:邻域半径

作为输出,我想要获取给定距离的单元格的邻居。因此,如果我运行

neighbors(im, i, j, d=1) with i = 1 and j = 1 (element value = 5) 

我应该得到以下值的索引:[1,2,3,4,6,7,8,9]。我希望我的意思清楚明白。是否有像scipy一样处理这个问题的库?

我已经做了一些工作,但它只是一个粗略的解决方案。

def pixel_neighbours(self, p):

    rows, cols = self.im.shape

    i, j = p[0], p[1]

    rmin = i - 1 if i - 1 >= 0 else 0
    rmax = i + 1 if i + 1 < rows else i

    cmin = j - 1 if j - 1 >= 0 else 0
    cmax = j + 1 if j + 1 < cols else j

    neighbours = []

    for x in xrange(rmin, rmax + 1):
        for y in xrange(cmin, cmax + 1):
            neighbours.append([x, y])
    neighbours.remove([p[0], p[1]])

    return neighbours

我该如何改进这个?

8个回答

29

请查看scipy.ndimage.generic_filter.

举个例子:

import numpy as np
import scipy.ndimage as ndimage

def test_func(values):
    print(values)
    return values.sum()


x = np.array([[1,2,3],[4,5,6],[7,8,9]])

footprint = np.array([[1,1,1],
                      [1,0,1],
                      [1,1,1]])

results = ndimage.generic_filter(x, test_func, footprint=footprint)

默认情况下,它会在边界处“反射”值。您可以使用mode关键字参数来控制此行为。

但是,如果您想要做类似于此的操作,则有很大的可能性可以将问题表示为某种卷积。如果是这样,将其分解为卷积步骤并使用更优化的函数(例如大多数scipy.ndimage)将会更快。


这种方法比我使用for循环编写的自定义方法要慢。 - Angel

10

编辑:啊糟糕,我的答案只是以难以理解的方式编写了im [i-d:i+d+1,j-d:j+d+1] .flatten() :)


这里可能会有一个老掉牙的滑动窗口技巧:

import numpy as np
from numpy.lib.stride_tricks import as_strided

def sliding_window(arr, window_size):
    """ Construct a sliding window view of the array"""
    arr = np.asarray(arr)
    window_size = int(window_size)
    if arr.ndim != 2:
        raise ValueError("need 2-D input")
    if not (window_size > 0):
        raise ValueError("need a positive window size")
    shape = (arr.shape[0] - window_size + 1,
             arr.shape[1] - window_size + 1,
             window_size, window_size)
    if shape[0] <= 0:
        shape = (1, shape[1], arr.shape[0], shape[3])
    if shape[1] <= 0:
        shape = (shape[0], 1, shape[2], arr.shape[1])
    strides = (arr.shape[1]*arr.itemsize, arr.itemsize,
               arr.shape[1]*arr.itemsize, arr.itemsize)
    return as_strided(arr, shape=shape, strides=strides)

def cell_neighbors(arr, i, j, d):
    """Return d-th neighbors of cell (i, j)"""
    w = sliding_window(arr, 2*d+1)

    ix = np.clip(i - d, 0, w.shape[0]-1)
    jx = np.clip(j - d, 0, w.shape[1]-1)

    i0 = max(0, i - d - ix)
    j0 = max(0, j - d - jx)
    i1 = w.shape[2] - max(0, d - i + ix)
    j1 = w.shape[3] - max(0, d - j + jx)

    return w[ix, jx][i0:i1,j0:j1].ravel()

x = np.arange(8*8).reshape(8, 8)
print x

for d in [1, 2]:
    for p in [(0,0), (0,1), (6,6), (8,8)]:
        print "-- d=%d, %r" % (d, p)
        print cell_neighbors(x, p[0], p[1], d=d)

我没有在这里进行任何时间测试,但是这个版本可能具有合理的性能。

如需更多信息,请使用“rolling window numpy”或“sliding window numpy”短语在网络上搜索。


4

我不知道是否有对此有所相关的库函数,但是你可以很容易地使用 numpy 的强大切片功能编写类似的代码:

import numpy as np
def neighbors(im, i, j, d=1):
    n = im[i-d:i+d+1, j-d:j+d+1].flatten()
    # remove the element (i,j)
    n = np.hstack((n[:len(n)//2], n[len(n)//2+1:] ))
    return n

当然,为避免越界访问,您应该进行一些范围检查。

2
这里的 b 是指什么? - James
b未定义,正如James所说 - Tommaso Guerrini

4

通过使用 maxmin,您可以处理处于上下限的像素:

im[max(i-1,0):min(i+2,i_end), max(j-1,0):min(j+2,j_end)].flatten()

2
我同意Joe Kington的回答,只是想补充一下。
import numpy as np
from scipy.ndimage import generate_binary_structure
from scipy.ndimage import iterate_structure
foot = np.array(generate_binary_structure(2, 1),dtype=int)

或者为更大/不同的印迹。
np.array(iterate_structure(foot , 2),dtype=int)

0
from skimage.util import view_as_windows
window_shape = (3, 3, 3)
neighborhoods = view_as_windows(img[200:220], window_shape) 

如果你需要,这里有一个D:

>>> A = np.arange(10)
>>> A
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> window_shape = (3,)
>>> B = view_as_windows(A, window_shape)
>>> B.shape
(8, 3)
>>> B
array([[0, 1, 2],
       [1, 2, 3],
       [2, 3, 4],
       [3, 4, 5],
       [4, 5, 6],
       [5, 6, 7],
       [6, 7, 8],
       [7, 8, 9]]

参考链接: https://scikit-image.org/docs/stable/api/skimage.util.html#skimage.util.view_as_windows

-1

2
就算是为了一点点价值,对于规则网格数据来说,四叉树并不是最理想的选择。索引数据所在的网格可以更快地查找邻居。 - Joe Kington

-1
我们首先使用numpy初始化我们感兴趣的矩阵。
import numpy as np

x = np.array([[1,2,3],[4,5,6],[7,8,9]])

print(x)

[[1 2 3]
 [4 5 6]
 [7 8 9]]

我们的邻居是距离函数,例如我们可能对距离为2的邻居感兴趣,这告诉我们应该如何填充矩阵x。我们选择用零填充,但你可以用任何你喜欢的方式填充,比如行/列的平均值、众数、中位数等。

d = 2

x_padded = np.pad(x,d,mode='constant')

print(x_padded)

[[0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0]
 [0 0 1 2 3 0 0]
 [0 0 4 5 6 0 0]
 [0 0 7 8 9 0 0]
 [0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0]]

我们使用 `x_padded` 矩阵来获得矩阵 `x` 中任意值的邻居。 假设 `(i,j)` 和 `(s,t)` 是 `x` 和 `x_padded` 的索引。现在我们需要将 `(i,j)` 转化为 `(s,t)`,以获取 `(i,j)` 的邻居。
i,j = 2,1
s,t = 2*d+i+1, 2*d+j+1

window = x_padded[i:s, j:t]

print(window)

[[0 1 2 3 0]
 [0 4 5 6 0]
 [0 7 8 9 0]
 [0 0 0 0 0]
 [0 0 0 0 0]]

请注意!!!索引 (i,j) 指向您希望在矩阵 x 中获取其邻居的任何值。
有时候,我们可能希望迭代矩阵 x 中的每个点,获取其邻居并进行计算。例如,在图像处理中,卷积核与图像进行卷积时,我们可能需要使用邻居像素的值进行计算。为了获取图像 x 中每个像素的邻居,我们可以采用以下方法:
for i in range(x.shape[0]):
    for j in range(x.shape[1]):
        i,j = 2,1
        s,t = 2*d+i+1, 2*d+j+1
        window = x_padded[i:s, j:t]

2
请修改您的回答,以便解释它如何回答 OP 的问题。在 Stack Overflow 上,代码转储不被视为有用的答案。 - Cris Luengo
请在您的代码之前添加注释,以便澄清它如何解决问题,将代码与其输出分开,并在结果后添加注释以分析结果中具体有用的内容。 - Michael Baudin
我尝试解释我的思考过程,如果有任何问题或困惑,我很乐意解答。 - Omphemetse

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接