简体   繁体   English

包含 2D NumPy 数组中每个值的邻居的 3D NumPy 数组?

[英]3D NumPy array which contains the neighbors of every value in a 2D NumPy array?

I want to build a NumPy array of shape (HEIGHT, WIDTH, 3) where HEIGHT and WIDTH correspond to the shape of an image stored inside a standard NumPy array, in which every (i, j) position has the inmediate neighbors (in a certain direction) of that position.我想构建一个形状(HEIGHT, WIDTH, 3)的 NumPy 数组,其中HEIGHTWIDTH对应于存储在标准 NumPy 数组中的图像的形状,其中每个(i, j)位置都有中间邻居(在一个某个方向)那个位置。 For example, if A = [[0, 1, 2, 3, 4],[5, 6, 7, 8, 9]] and I want the neighbors of (1, 2) positioned to the north, I would get as a result [1, 2, 3] .例如,如果A = [[0, 1, 2, 3, 4],[5, 6, 7, 8, 9]]并且我希望(1, 2)的邻居位于北方,我会得到结果[1, 2, 3] So, my result matrix should have [1, 2, 3] = A[1:4] at its respective (1, 2) .所以,我的结果矩阵在其各自的(1, 2)处应该有[1, 2, 3] = A[1:4]

For now, I have tried a simple approach in which I do not use such a matrix, instead I iterate over all values in my array and slice it accordingly to get the desired neighbors.现在,我尝试了一种简单的方法,我不使用这样的矩阵,而是遍历数组中的所有值并相应地对其进行切片以获得所需的邻居。 Nonetheless, if I could compute that matrix beforehand, the algorithm I use afterwards could be vectorized (I don't include this part in the question because it's not relevant to my problem), which is almost always faster at the expense of more memory usage.尽管如此,如果我可以事先计算该矩阵,那么我之后使用的算法可以被向量化(我没有将这部分包含在问题中,因为它与我的问题无关),这几乎总是更快,但会消耗更多内存.

    scales = 3
    padded_img = np.pad(img, scales, mode='constant')
    feature_vectors = np.zeros((img.shape[0]*img.shape[1], 4*scales))
    z = 0
    for i in range(scales, padded_img.shape[0] - scales):
        for j in range(scales, padded_img.shape[1] - scales):
            for scale in range(1, scales + 1):
                N = padded_img[i - scale, j - scale: j + scale + 1]
                E = padded_img[i - scale: i + scale + 1, j + scale]
                S = padded_img[i + scale, j - scale: j + scale + 1]
                W = padded_img[i - scale: i + scale + 1, j - scale]

                neighbors = np.vstack((N, E, S, W))
                avgs = np.mean(neighbors, axis=1)
                           
                feature_vectors[z, 4*(scale-1):4*scale] = avgs.flatten()
            z += 1

img is my original NumPy array; img是我原来的 NumPy 数组; I pad it to avoid problems in the corners.我垫它以避免在角落出现问题。 On the other hand, I use scales because I basically need not only the inmediate neighbors, but those distanced 1 to scales from a certain position.另一方面,我使用scales是因为我基本上不仅需要中间邻居,还需要那些距离 1 到某个位置的scales Since I am also interested in all possible directions, I use N, E, S, W as my vectors of neighbors inside the loop.因为我也对所有可能的方向感兴趣,所以我使用N, E, S, W作为我在循环内的邻居向量。 Above all, the idea is to reduce this algorithm's time complexity.最重要的是,这个想法是为了减少这个算法的时间复杂度。 Any ideas?有任何想法吗? Thank you.谢谢你。

EDIT : After getting those 4 vectors every iteration, I compute their average, flatten them and append them to a feature vector, whose rows contain this information of all directions on all 4 scales.编辑:每次迭代获得这 4 个向量后,我计算它们的平均值,将它们展平并将它们附加到一个特征向量,其行包含所有 4 个尺度上所有方向的信息。

Your code doesn't run (there's problems with pad padding the RGB dimension and the shape of avgs not allowing assignment into feature_vectors ), so I can't tell if this exactly what you want, but this should get you started:您的代码不运行(有问题pad填充的RGB尺寸和形状avgs不允许分配到feature_vectors ),因此,如果这正是你想要什么,我说不出来,但是这应该让你开始:

import numpy as np
from scipy.ndimage import convolve

scales = 3

img = np.random.rand(256, 256, 3)

feature_vectors = np.zeros((scales, 4) + img.shape[:-1])

for n in range(scales):
    dim = 2 * n + 3
    orig = np.zeros((dim, dim, 3), dtype = float)
    orig[:, 0, :] = 1 / (dim * 3)
    kernel = np.array([np.rot90(orig, i, (0, 1)) for i in range(4)])
    feature_vectors[n] = convolve( img[None, ...], 
                                   kernel,
                                   mode = 'constant')[..., 1]
    
feature_vectors = feature_vectors.transpose(2, 3, 1, 0).reshape(-1, scales * 4)

feature_vectors.shape
Out[]: (65536, 12)

I am posting this here so that people who may come across this question can have the full picture.我在这里发布此信息,以便可能遇到此问题的人可以全面了解。 I encountered this problem when I was creating a Python implementation for the extraction of multi-scale features explained in the paper Segmentation of structural features in cheese micrographs using pixel statistics by G. Impoco, L. Tuminello, N. Fucà, M. Caccamo and G. Licitra.我遇到过,当我创建一个Python实现多尺度的提取中的结构特征,使用像素统计由G. Impoco,L. Tuminello,N富卡,M.卡卡莫和奶酪显微照片分段功能解释这个问题G. 利西特拉。 You can check it out in ScienceDirect .您可以在ScienceDirect 中查看。

I first created a naïve approach consisting of a not-so-elegant triple for-loop that went like this (posted in my original question):我首先创建了一个简单的方法,由一个不太优雅的三重 for 循环组成,如下所示(发布在我的原始问题中):

import numpy as np

padded_img = np.pad(img, scales, mode="constant")
for i in range(scales, padded_img.shape[0] - scales):
    for j in range(scales, padded_img.shape[1] - scales):
        for scale in range(1, scales + 1):
            N = padded_img[i - scale, j - scale : j + scale + 1]
            E = padded_img[i - scale : i + scale + 1, j + scale]
            S = padded_img[i + scale, j - scale : j + scale + 1]
            W = padded_img[i - scale : i + scale + 1, j - scale]

This approach obtained the desired neighborhood information by using a variable scale that would iterate over the predefined window radius.这种方法通过使用将在预定义的窗口半径上迭代的可变scale来获得所需的邻域信息。 This variable allowed me to access the neighboring pixels of (i, j) .这个变量允许我访问(i, j)的相邻像素。 Nevertheless, as @Daniel F pointed out, this can be convolved and made simpler like so:尽管如此,正如@Daniel F 指出的那样,这可以像这样进行卷积和简化:

import scipy.ndimage

directions = 4
for scale in range(1, scales + 1):
    filter_size = 2 * (scale - 1) + 3
    orig = np.zeros((filter_size, filter_size), dtype=np.float64)
    orig[:, 0] = 1 / filter_size
    for c in range(directions):
        # c = 0 -> North; c = 1 -> East; c = 2 -> South; c = 3 -> West
        correlation_filter = np.rot90(orig, 3 - c, (0, 1))
        convolution_filter = np.flip(correlation_filter)

        directions_img = scipy.ndimage.convolve(img, convolution_filter, cval=0.0, mode="constant")

So, instead of iterating over every pixel and computing what I wanted that many times, this iterates over the wanted directions ( N , E , W and S in my naïve approach) and computes everything I need for every single pixel, without the need of actually iterating over every single one of them.因此,不是迭代每个像素并多次计算我想要的东西,而是迭代所需的directions (我天真的方法中的NEWS )并计算每个像素所需的一切,而无需实际上迭代它们中的每一个。

Naturally, the difference in performance is mind-blowing.自然地,性能上的差异是令人兴奋的。 I tested out both versions with timeit module ( mean ± std. dev. of 7 runs, 1 loop each ) and got the following benchmark with an image of shape (630, 630) and scales=1 :我用timeit模块测试了两个版本( mean ± std. dev. of 7 runs, 1 loop each ),并获得了以下基准测试,图像的形状为(630, 630)scales=1

Naïve approach: 46.4 ± 1.65 s per loop 
Convolution approach: 204 ± 11.7 ms per loop

Comparison over different image sizes with scales=1 scales=1不同图像尺寸的比较

naive 方法和卷积方法的比较

Convolution approach over different image sizes with scales=3 scales=3不同图像尺寸的卷积方法

卷积方法基准

Code代码

You can check out my code for both versions in this GitHub gist .你可以在这个 GitHub gist 中查看我的两个版本的代码。 Keep in mind that this code was specifically written for gray images.请记住,此代码是专门为灰度图像编写的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM