有效地将函数应用于numpy数组中的球形邻域
Efficiently apply function to spheric neighbourhood in numpy array
我在 Python 中有一个浮点值的 3D numpy 数组。
我需要从开始检索半径为 r 的球体中的所有元素
中心点 P(x, y, z)。然后,我想对球体点应用一个函数
更新它们的值并需要到中心点的距离来执行此操作。我做了很多次这些步骤,为了
大半径值,所以我想有一个同样有效的解决方案
尽可能。
我目前的解决方案只检查球体边界框中的点,
如此处所示:Using a QuadTree to get all points within a bounding circle。
代码草图如下所示:
# P(x, y, z): center of the sphere
for k1 in range(x - r, x + r + 1):
for k2 in range(y - r, y + r + 1):
for k3 in range(z - r, z + r + 1):
# Sphere center - current point distance
dist = np.sum((np.array([k1, k2, k3]) - np.array([x, y, z])) ** 2)
if (dist <= r * r):
# computeUpdatedValue(distance, radius): function that computes the new value of the matrix in the current point
newValue = computeUpdatedValue(dist, r)
# Update the matrix
mat[k1, k2, k3] = newValue
但是,我认为应用掩码来检索点,然后,
以矢量化方式基于距离更新它们更有效。
我已经看到如何应用循环内核
(How to apply a disc shaped mask to a numpy array?),
但我不知道如何在每个掩码元素上有效地应用该函数(取决于索引)。
编辑:如果您的阵列与您正在更新的区域相比非常大,则下面的解决方案将占用比必要更多的内存。您可以应用相同的想法,但仅限于球体可能掉落的区域:
def updateSphereBetter(mat, center, radius):
# Find beginning and end of region of interest
center = np.asarray(center)
start = np.minimum(np.maximum(center - radius, 0), mat.shape)
end = np.minimum(np.maximum(center + radius + 1, 0), mat.shape)
# Slice region of interest
mat_sub = mat[tuple(slice(s, e) for s, e in zip(start, end))]
# Center coordinates relative to the region of interest
center_rel = center - start
# Same as before but with mat_sub and center_rel
ind = np.indices(mat_sub.shape)
ind = np.moveaxis(ind, 0, -1)
dist_squared = np.sum(np.square(ind - center_rel), axis=-1)
mask = dist_squared <= radius * radius
mat_sub[mask] = computeUpdatedValue(dist_squared[mask], radius)
请注意,由于 mat_sub
是 mat
的一个视图,更新它会更新原始数组,所以这会产生与以前相同的结果,但使用的资源更少。
这里有一点概念证明。我定义了 computeUpdatedValue
以便它显示距中心的距离,然后绘制了几个 "sections" 的示例:
import numpy as np
import matplotlib.pyplot as plt
def updateSphere(mat, center, radius):
# Make array of all index coordinates
ind = np.indices(mat.shape)
# Compute the squared distances to each point
ind = np.moveaxis(ind, 0, -1)
dist_squared = np.sum(np.square(ind - center), axis=-1)
# Make a mask for squared distances within squared radius
mask = dist_squared <= radius * radius
# Update masked values
mat[mask] = computeUpdatedValue(dist_squared[mask], radius)
def computeUpdatedValue(dist_squared, radius):
# 1 at the center of the sphere and 0 at the surface
return np.clip(1 - np.sqrt(dist_squared) / radius, 0, 1)
mat = np.zeros((100, 60, 80))
updateSphere(mat, [50, 20, 40], 20)
plt.subplot(131)
plt.imshow(mat[:, :, 30], vmin=0, vmax=1)
plt.subplot(132)
plt.imshow(mat[:, :, 40], vmin=0, vmax=1)
plt.subplot(133)
plt.imshow(mat[:, :, 55], vmin=0, vmax=1)
输出:
我在 Python 中有一个浮点值的 3D numpy 数组。 我需要从开始检索半径为 r 的球体中的所有元素 中心点 P(x, y, z)。然后,我想对球体点应用一个函数 更新它们的值并需要到中心点的距离来执行此操作。我做了很多次这些步骤,为了 大半径值,所以我想有一个同样有效的解决方案 尽可能。
我目前的解决方案只检查球体边界框中的点, 如此处所示:Using a QuadTree to get all points within a bounding circle。 代码草图如下所示:
# P(x, y, z): center of the sphere
for k1 in range(x - r, x + r + 1):
for k2 in range(y - r, y + r + 1):
for k3 in range(z - r, z + r + 1):
# Sphere center - current point distance
dist = np.sum((np.array([k1, k2, k3]) - np.array([x, y, z])) ** 2)
if (dist <= r * r):
# computeUpdatedValue(distance, radius): function that computes the new value of the matrix in the current point
newValue = computeUpdatedValue(dist, r)
# Update the matrix
mat[k1, k2, k3] = newValue
但是,我认为应用掩码来检索点,然后, 以矢量化方式基于距离更新它们更有效。 我已经看到如何应用循环内核 (How to apply a disc shaped mask to a numpy array?), 但我不知道如何在每个掩码元素上有效地应用该函数(取决于索引)。
编辑:如果您的阵列与您正在更新的区域相比非常大,则下面的解决方案将占用比必要更多的内存。您可以应用相同的想法,但仅限于球体可能掉落的区域:
def updateSphereBetter(mat, center, radius):
# Find beginning and end of region of interest
center = np.asarray(center)
start = np.minimum(np.maximum(center - radius, 0), mat.shape)
end = np.minimum(np.maximum(center + radius + 1, 0), mat.shape)
# Slice region of interest
mat_sub = mat[tuple(slice(s, e) for s, e in zip(start, end))]
# Center coordinates relative to the region of interest
center_rel = center - start
# Same as before but with mat_sub and center_rel
ind = np.indices(mat_sub.shape)
ind = np.moveaxis(ind, 0, -1)
dist_squared = np.sum(np.square(ind - center_rel), axis=-1)
mask = dist_squared <= radius * radius
mat_sub[mask] = computeUpdatedValue(dist_squared[mask], radius)
请注意,由于 mat_sub
是 mat
的一个视图,更新它会更新原始数组,所以这会产生与以前相同的结果,但使用的资源更少。
这里有一点概念证明。我定义了 computeUpdatedValue
以便它显示距中心的距离,然后绘制了几个 "sections" 的示例:
import numpy as np
import matplotlib.pyplot as plt
def updateSphere(mat, center, radius):
# Make array of all index coordinates
ind = np.indices(mat.shape)
# Compute the squared distances to each point
ind = np.moveaxis(ind, 0, -1)
dist_squared = np.sum(np.square(ind - center), axis=-1)
# Make a mask for squared distances within squared radius
mask = dist_squared <= radius * radius
# Update masked values
mat[mask] = computeUpdatedValue(dist_squared[mask], radius)
def computeUpdatedValue(dist_squared, radius):
# 1 at the center of the sphere and 0 at the surface
return np.clip(1 - np.sqrt(dist_squared) / radius, 0, 1)
mat = np.zeros((100, 60, 80))
updateSphere(mat, [50, 20, 40], 20)
plt.subplot(131)
plt.imshow(mat[:, :, 30], vmin=0, vmax=1)
plt.subplot(132)
plt.imshow(mat[:, :, 40], vmin=0, vmax=1)
plt.subplot(133)
plt.imshow(mat[:, :, 55], vmin=0, vmax=1)
输出: