插入具有numpy和/ scipy的3D体积

时间:2014-02-17 17:56:03

标签: python numpy 3d scipy interpolation

我非常沮丧,因为几个小时后我似乎无法在python中做一个看似简单的3D插值。在Matlab中我所要做的就是

Vi = interp3(x,y,z,V,xi,yi,zi)

使用scipy的ndimage.map_coordinate或其他numpy方法的确切等价是什么?

由于

4 个答案:

答案 0 :(得分:19)

在scipy 0.14或更高版本中,有一个与interp3非常相似的新函数scipy.interpolate.RegularGridInterpolator

MATLAB命令Vi = interp3(x,y,z,V,xi,yi,zi)将转换为:

from numpy import array
from scipy.interpolate import RegularGridInterpolator as rgi
my_interpolating_function = rgi((x,y,z), V)
Vi = my_interpolating_function(array([xi,yi,zi]).T)

这是一个完整的例子,展示了两者;它将帮助您了解确切的差异......

MATLAB代码:

x = linspace(1,4,11);
y = linspace(4,7,22);
z = linspace(7,9,33);
V = zeros(22,11,33);
for i=1:11
    for j=1:22
        for k=1:33
            V(j,i,k) = 100*x(i) + 10*y(j) + z(k);
        end
    end
end
xq = [2,3];
yq = [6,5];
zq = [8,7];
Vi = interp3(x,y,z,V,xq,yq,zq);

结果为Vi=[268 357],这确实是这两个点(2,6,8)(3,5,7)的值。

SCIPY CODE:

from scipy.interpolate import RegularGridInterpolator
from numpy import linspace, zeros, array
x = linspace(1,4,11)
y = linspace(4,7,22)
z = linspace(7,9,33)
V = zeros((11,22,33))
for i in range(11):
    for j in range(22):
        for k in range(33):
            V[i,j,k] = 100*x[i] + 10*y[j] + z[k]
fn = RegularGridInterpolator((x,y,z), V)
pts = array([[2,6,8],[3,5,7]])
print(fn(pts))

再次是[268,357]。所以你看到一些细微的差别:Scipy使用x,y,z索引顺序,而MATLAB使用y,x,z(奇怪);在Scipy中,你在一个单独的步骤中定义一个函数,当你调用它时,坐标分组如(x1,y1,z1),(x2,y2,z2),...而matlab使用(x1,x2,.. 。),(Y1,Y2,...),(Z1,Z2,...)。

除此之外,两者相似且同样易于使用。

答案 1 :(得分:4)

完全等同于MATLAB的interp3将使用scipy的interpn进行一次性插值:

import numpy as np
from scipy.interpolate import interpn

Vi = interpn((x,y,z), V, np.array([xi,yi,zi]).T)

MATLAB和scipy的默认方法是线性插值,可以使用method参数进行更改。请注意,interpn仅支持3维及以上的线性和最近邻插值,不像MATLAB支持三次和样条插值。

在同一网格上进行多次插值调用时,最好使用插值对象RegularGridInterpolator,如接受的答案aboveinterpn在内部使用RegularGridInterpolator

答案 2 :(得分:2)

基本上,ndimage.map_coordinates在“索引”坐标(a.k.a。“体素”或“像素”坐标)中工作。它的界面起初看起来有点笨拙,但它确实给你一个灵活的很多

如果要指定类似于matlab的interp3的插值坐标,则需要将输入坐标转换为“索引”坐标。

还有额外的皱纹map_coordinates总是保留输出中输入数组的dtype。如果插入整数数组,您将得到整数输出,这可能是您想要的,也可能不是。对于下面的代码片段,我假设您总是想要浮点输出。 (如果你不这样做,那实际上更简单。)

我将在今晚晚些时候尝试添加更多解释(这是相当密集的代码)。

总而言之,我所拥有的interp3函数比您确切需要的函数更复杂。但是,它应该或多或少地复制interp3的行为,因为我记得它(忽略了interp3(data, zoom_factor)处理的scipy.ndimage.zoom的“缩放”功能。)

import numpy as np
from scipy.ndimage import map_coordinates

def main():
    data = np.arange(5*4*3).reshape(5,4,3)

    x = np.linspace(5, 10, data.shape[0])
    y = np.linspace(10, 20, data.shape[1])
    z = np.linspace(-100, 0, data.shape[2])

    # Interpolate at a single point
    print interp3(x, y, z, data, 7.5, 13.2, -27)

    # Interpolate a region of the x-y plane at z=-25
    xi, yi = np.mgrid[6:8:10j, 13:18:10j]
    print interp3(x, y, z, data, xi, yi, -25 * np.ones_like(xi))

def interp3(x, y, z, v, xi, yi, zi, **kwargs):
    """Sample a 3D array "v" with pixel corner locations at "x","y","z" at the
    points in "xi", "yi", "zi" using linear interpolation. Additional kwargs
    are passed on to ``scipy.ndimage.map_coordinates``."""
    def index_coords(corner_locs, interp_locs):
        index = np.arange(len(corner_locs))
        if np.all(np.diff(corner_locs) < 0):
            corner_locs, index = corner_locs[::-1], index[::-1]
        return np.interp(interp_locs, corner_locs, index)

    orig_shape = np.asarray(xi).shape
    xi, yi, zi = np.atleast_1d(xi, yi, zi)
    for arr in [xi, yi, zi]:
        arr.shape = -1

    output = np.empty(xi.shape, dtype=float)
    coords = [index_coords(*item) for item in zip([x, y, z], [xi, yi, zi])]

    map_coordinates(v, coords, order=1, output=output, **kwargs)

    return output.reshape(orig_shape)

main()

答案 3 :(得分:0)

这个问题很老,但是我认为它需要澄清,因为没有人指出所请求的操作(trilinear interpolation)可以很容易地从头开始实现,并且可以节省一贯的计算时间(快10倍左右)。 scipy.interpolate的{​​{1}}。

代码

RegularGridInterpolator

示例

import numpy as np
from itertools import product

def trilinear_interpolation(x_volume, y_volume, z_volume, volume, x_needed, y_needed, z_needed):
    """
    Trilinear interpolation (from Wikipedia)

    :param x_volume: x points of the volume grid 
    :type crack_type: list or numpy.ndarray
    :param y_volume: y points of the volume grid 
    :type crack_type: list or numpy.ndarray
    :param x_volume: z points of the volume grid 
    :type crack_type: list or numpy.ndarray
    :param volume:   volume
    :type crack_type: list or numpy.ndarray
    :param x_needed: desired x coordinate of volume
    :type crack_type: float
    :param y_needed: desired y coordinate of volume
    :type crack_type: float
    :param z_needed: desired z coordinate of volume
    :type crack_type: float

    :return volume_needed: desired value of the volume, i.e. volume(x_needed, y_needed, z_needed)
    :type volume_needed: float
    """
    # dimensinoal check
    if np.shape(volume) != (len(x_volume), len(y_volume), len(z_volume)):
        raise ValueError(f'dimension mismatch, volume must be a ({len(x_volume)}, {len(y_volume)}, {len(z_volume)}) list or numpy.ndarray')
    # check of the indices needed for the correct control volume definition
    i = searchsorted(x_volume, x_needed)
    j = searchsorted(y_volume, y_needed)
    k = searchsorted(z_volume, z_needed)
    # control volume definition
    control_volume_coordinates = np.array(
        [[x_volume[i - 1], y_volume[j - 1], z_volume[k - 1]], [x_volume[i], y_volume[j], z_volume[k]]])
    xd = (np.array([x_needed, y_needed, z_needed]) - control_volume_coordinates[0]) / (control_volume_coordinates[1] - control_volume_coordinates[0])
    # interpolation along x
    c2 = [[0, 0], [0, 0]]
    for m, n in product([0, 1], [0, 1]):
        c2[m][n] = volume[i - 1][j - 1 + m][k - 1 + n] * (1 - xd[0]) + volume[i][j - 1 + m][k - 1 + n] * xd[0]
    # interpolation along y
    c1 = [0, 0]
    c1[0] = c2[0][0] * (1 - xd[1]) + c2[1][0] * xd[1]
    c1[1] = c2[0][1] * (1 - xd[1]) + c2[1][1] * xd[1]
    # interpolation along z
    volume_needed = c1[0] * (1 - xd[2]) + c1[1] * xd[2]
    return volume_needed

def searchsorted(l, x):
    for i in l:
        if i >= x: break
    return l.index(i)


from scipy.interpolate import RegularGridInterpolator
def trilin_interp_regular_grid(x_volume, y_volume, z_volume, volume, x_needed, y_needed, z_needed):
    # dimensinoal check
    if np.shape(volume) != (len(x_volume), len(y_volume), len(z_volume)):
        raise ValueError(f'dimension mismatch, volume must be a ({len(x_volume)}, {len(y_volume)}, {len(z_volume)}) list or numpy.ndarray')
    # trilinear interpolation on a regular grid
    fn = RegularGridInterpolator((x_volume,y_volume,z_volume), volume)
    volume_needed = fn(np.array([x_needed, y_needed, z_needed]))
    return volume_needed