[英]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 数组,其中HEIGHT
和WIDTH
对应于存储在标准 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
(我天真的方法中的N
、 E
、 W
和S
)并计算每个像素所需的一切,而无需实际上迭代它们中的每一个。
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
scales=1
scales=1
不同图像尺寸的比较scales=3
scales=3
不同图像尺寸的卷积方法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.