Python 无SciPy的NumPy阵列的三维插值

Python 无SciPy的NumPy阵列的三维插值,python,numpy,scipy,Python,Numpy,Scipy,我正在为一个应用程序编写一个插件,该应用程序在二进制发行版中包含NumPy,但不包含SciPy。我的插件需要将数据从一个常规三维网格插值到另一个常规三维网格。从源代码运行时,可以使用scipy.ndimage非常有效地完成此操作,或者,如果用户没有安装scipy,则使用我编写的.pyd生成的weave。不幸的是,如果用户正在运行二进制文件,这两个选项都不可用 我已经用python编写了一个简单的例程,可以给出正确的结果,但是对于我使用的数组大小,这需要很长的时间(~5分钟)。我想知道是否有办法只

我正在为一个应用程序编写一个插件,该应用程序在二进制发行版中包含NumPy,但不包含SciPy。我的插件需要将数据从一个常规三维网格插值到另一个常规三维网格。从源代码运行时,可以使用
scipy.ndimage
非常有效地完成此操作,或者,如果用户没有安装scipy,则使用我编写的
.pyd
生成的weave。不幸的是,如果用户正在运行二进制文件,这两个选项都不可用

我已经用python编写了一个简单的例程,可以给出正确的结果,但是对于我使用的数组大小,这需要很长的时间(~5分钟)。我想知道是否有办法只使用NumPy中的功能来加速它。与scipy.ndimage.map_坐标一样,它需要一个3D输入数组和一个数组,其中包含要插值的每个点的x、y和z坐标

def trilinear_interp(input_array, indices):
    """Evaluate the input_array data at the indices given"""

    output = np.empty(indices[0].shape)
    x_indices = indices[0]
    y_indices = indices[1]
    z_indices = indices[2]
    for i in np.ndindex(x_indices.shape):
        x0 = np.floor(x_indices[i])
        y0 = np.floor(y_indices[i])
        z0 = np.floor(z_indices[i])
        x1 = x0 + 1
        y1 = y0 + 1
        z1 = z0 + 1
        #Check if xyz1 is beyond array boundary:
        if x1 == input_array.shape[0]:
            x1 = x0
        if y1 == input_array.shape[1]:
            y1 = y0
        if z1 == input_array.shape[2]:
            z1 = z0
        x = x_indices[i] - x0
        y = y_indices[i] - y0
        z = z_indices[i] - z0
        output[i] = (input_array[x0,y0,z0]*(1-x)*(1-y)*(1-z) +
                 input_array[x1,y0,z0]*x*(1-y)*(1-z) +
                 input_array[x0,y1,z0]*(1-x)*y*(1-z) +
                 input_array[x0,y0,z1]*(1-x)*(1-y)*z +
                 input_array[x1,y0,z1]*x*(1-y)*z +
                 input_array[x0,y1,z1]*(1-x)*y*z +
                 input_array[x1,y1,z0]*x*y*(1-z) +
                 input_array[x1,y1,z1]*x*y*z)

    return output

显然,函数速度如此之慢的原因是3D空间中每个点上的
for
循环。有没有办法执行某种切片或矢量化魔术来加速它?谢谢。

事实证明,将其矢量化非常容易

output = np.empty(indices[0].shape)
x_indices = indices[0]
y_indices = indices[1]
z_indices = indices[2]

x0 = x_indices.astype(np.integer)
y0 = y_indices.astype(np.integer)
z0 = z_indices.astype(np.integer)
x1 = x0 + 1
y1 = y0 + 1
z1 = z0 + 1

#Check if xyz1 is beyond array boundary:
x1[np.where(x1==input_array.shape[0])] = x0.max()
y1[np.where(y1==input_array.shape[1])] = y0.max()
z1[np.where(z1==input_array.shape[2])] = z0.max()

x = x_indices - x0
y = y_indices - y0
z = z_indices - z0
output = (input_array[x0,y0,z0]*(1-x)*(1-y)*(1-z) +
             input_array[x1,y0,z0]*x*(1-y)*(1-z) +
             input_array[x0,y1,z0]*(1-x)*y*(1-z) +
             input_array[x0,y0,z1]*(1-x)*(1-y)*z +
             input_array[x1,y0,z1]*x*(1-y)*z +
             input_array[x0,y1,z1]*(1-x)*y*z +
             input_array[x1,y1,z0]*x*y*(1-z) +
             input_array[x1,y1,z1]*x*y*z)

return output

非常感谢你的这篇文章,也感谢你的关注。我已经自由地基于你的矢量化,以给它另一个速度提升(至少与我的数据工作)

我正在处理图像相关性,因此我需要在同一个输入数组中插入多组不同的坐标

不幸的是,我把它弄得更复杂了一点,但如果我能解释我所做的事情,那么额外的复杂性应该是a)证明它是正确的,b)变得清楚了。您的最后一行(输出=)仍然需要在输入数组中的非顺序位置进行大量查找,这使得查找速度相对较慢

假设我的3D数据是NxMxP长的。我决定做以下事情:如果我能得到一个点及其近邻的(8x(NxMxP))预先计算的灰度值矩阵,我还可以计算一个((NxMxP)x8)系数矩阵(上面例子中的第一个系数是(x-1)(y-1)(z-1)),那么我就可以将它们相乘,就没有家了

对我来说,一个很好的好处是我可以预先计算灰色矩阵,并回收它

下面是一段示例代码(粘贴自两个不同的函数,因此可能无法开箱即用,但应该是一个很好的灵感来源):

def三线性插值器加速比(输入数组,坐标):
input_array_precut_2x2x2=numpy.zeros((input_array.shape[0]-1,input_array.shape[1]-1,input_array.shape[2]-1,8),dtype=DATA_dtype)
input_array_precut_2x2x2[:,:,:,0]=input_array[0:新_维度-1,0:新_维度-1,0:新_维度-1]
input_array_precut_2x2x2[:,:,:,1]=input_array[1:新_维度,0:新_维度-1,0:新_维度-1]
input_array_precut_2x2x2[:,:,:,2]=input_array[0:新_维度-1,1:新_维度,0:新_维度-1]
input_array_precut_2x2x2[:,:,:,3]=input_array[0:新_维度-1,0:新_维度-1,1:新_维度]
input_array_precut_2x2x2[:,:,:,4]=input_array[1:新_维度,0:新_维度-1,1:新_维度]
input_array_precut_2x2x2[:,:,:,5]=input_array[0:新_维度-1,1:新_维度,1:新_维度]
input_array_precut_2x2x2[:,:,:,6]=input_array[1:新_维度,1:新_维度,0:新_维度-1]
input_array_precut_2x2x2[:,:,:,7]=input_array[1:新_维度,1:新_维度,1:新_维度]
#改编自http://stackoverflow.com/questions/6427276/3d-interpolation-of-numpy-arrays-without-scipy
#2012.03.02-大量修改,使最终计算矢量化。。。现在它是超高速的。
#-为了更快,检查现在被删除。。。
#重要提示:输入阵列是一个预拆分的8xNxMxO阵列。
#输入坐标可以包含非整数值的索引(这是一种想法),而坐标0和坐标1是整数值。
如果coords.max()>min(input_array.shape[0:3])-1或coords.min()<0:
#做一些检查以恢复极限
#可以分别检查x y和z中的每个参数,但我知道我得到的是立方数据。。。
coords[numpy.where(coords>min(input_array.shape[0:3])-1)]=min(input_array.shape[0:3])-1
coords[numpy.where(coords
def trilinear_interpolator_speedup( input_array, coords ):
  input_array_precut_2x2x2 = numpy.zeros( (input_array.shape[0]-1, input_array.shape[1]-1, input_array.shape[2]-1, 8 ), dtype=DATA_DTYPE )
  input_array_precut_2x2x2[ :, :, :, 0 ] = input_array[ 0:new_dimension-1, 0:new_dimension-1, 0:new_dimension-1 ]
  input_array_precut_2x2x2[ :, :, :, 1 ] = input_array[ 1:new_dimension  , 0:new_dimension-1, 0:new_dimension-1 ]
  input_array_precut_2x2x2[ :, :, :, 2 ] = input_array[ 0:new_dimension-1, 1:new_dimension  , 0:new_dimension-1 ]
  input_array_precut_2x2x2[ :, :, :, 3 ] = input_array[ 0:new_dimension-1, 0:new_dimension-1, 1:new_dimension   ]
  input_array_precut_2x2x2[ :, :, :, 4 ] = input_array[ 1:new_dimension  , 0:new_dimension-1, 1:new_dimension   ]
  input_array_precut_2x2x2[ :, :, :, 5 ] = input_array[ 0:new_dimension-1, 1:new_dimension  , 1:new_dimension   ]
  input_array_precut_2x2x2[ :, :, :, 6 ] = input_array[ 1:new_dimension  , 1:new_dimension  , 0:new_dimension-1 ]
  input_array_precut_2x2x2[ :, :, :, 7 ] = input_array[ 1:new_dimension  , 1:new_dimension  , 1:new_dimension   ] 
  # adapted from from http://stackoverflow.com/questions/6427276/3d-interpolation-of-numpy-arrays-without-scipy
  # 2012.03.02 - heavy modifications, to vectorise the final calculation... it is now superfast.
  #  - the checks are now removed in order to go faster...

  # IMPORTANT: Input array is a pre-split, 8xNxMxO array.

  # input coords could contain indexes at non-integer values (it's kind of the idea), whereas the coords_0 and coords_1 are integer values.
  if coords.max() > min(input_array.shape[0:3])-1  or coords.min() < 0:
    # do some checks to bring back the extremeties
    # Could check each parameter in x y and z separately, but I know I get cubic data...
    coords[numpy.where(coords>min(input_array.shape[0:3])-1)] = min(input_array.shape[0:3])-1
    coords[numpy.where(coords<0                      )] = 0              

  # for NxNxN data, coords[0].shape = N^3
  output_array = numpy.zeros( coords[0].shape, dtype=DATA_DTYPE )

  # a big array to hold all the coefficients for the trilinear interpolation
  all_coeffs = numpy.zeros( (8,coords.shape[1]), dtype=DATA_DTYPE )

  # the "floored" coordinates x, y, z
  coords_0 = coords.astype(numpy.integer)                  

  # all the above + 1 - these define the top left and bottom right (highest and lowest coordinates)
  coords_1 = coords_0 + 1

  # make the input coordinates "local"
  coords = coords - coords_0

  # Calculate one minus these values, in order to be able to do a one-shot calculation
  #   of the coefficients.
  one_minus_coords = 1 - coords

  # calculate those coefficients.
  all_coeffs[0] = (one_minus_coords[0])*(one_minus_coords[1])*(one_minus_coords[2])
  all_coeffs[1] =      (coords[0])     *(one_minus_coords[1])*(one_minus_coords[2])
  all_coeffs[2] = (one_minus_coords[0])*    (coords[1])      *(one_minus_coords[2])
  all_coeffs[3] = (one_minus_coords[0])*(one_minus_coords[1])*     (coords[2])
  all_coeffs[4] =      (coords[0])     *(one_minus_coords[1])*     (coords[2])      
  all_coeffs[5] = (one_minus_coords[0])*     (coords[1])     *     (coords[2])
  all_coeffs[6] =      (coords[0])     *     (coords[1])     *(one_minus_coords[2])
  all_coeffs[7] =      (coords[0])     *     (coords[1])     *     (coords[2])

  # multiply 8 greyscale values * 8 coefficients, and sum them across the "8 coefficients" direction
  output_array = (  input_array[ coords_0[0], coords_0[1], coords_0[2] ].T * all_coeffs ).sum( axis=0 )

  # and return it...
  return output_array