2013-04-25 25 views
16

Tôi có một số dữ liệu hình ảnh thể tích bao gồm các giá trị được lấy mẫu trên lưới thông thường trong x, y, z, nhưng với hình dạng voxel không phải khối. khoảng cách giữa các điểm liền kề trong z lớn hơn trong x, y). Tôi cuối cùng đã muốn có thể để có thể suy các giá trị trên một số máy bay 2D tùy ý mà đi qua khối lượng, như thế này:Nội suy nhanh của dữ liệu 3D lấy mẫu thường xuyên với các khoảng thời gian khác nhau trong x, y và z

enter image description here

Tôi nhận thức được scipy.ndimage.map_coordinates, nhưng trong trường hợp của tôi sử dụng nó ít đơn giản hơn vì nó ngầm giả định rằng khoảng cách của các phần tử trong mảng đầu vào bằng nhau trên các chiều. Trước tiên tôi có thể resample mảng đầu vào của tôi theo kích thước voxel nhỏ nhất (để tất cả voxel của tôi sau đó sẽ là hình khối), sau đó sử dụng map_coordinates để nội suy trên máy bay của tôi, nhưng nó không có vẻ như một ý tưởng tuyệt vời để nội suy dữ liệu của tôi hai lần .

Tôi cũng biết rằng scipy có các bộ nội suy khác nhau cho dữ liệu ND không đều nhau (LinearNDInterpolator, NearestNDInterpolator v.v.), nhưng chúng rất chậm và bộ nhớ chuyên sâu cho mục đích của tôi. Cách tốt nhất để nội suy dữ liệu của tôi là gì khi tôi biết rằng các giá trị thường xuyên được đặt cách nhau trong mỗi thứ nguyên?

Trả lời

13

Bạn có thể sử dụng map_coordinates với một ít đại số. Giả sử các khoảng trống của lưới của bạn là dx, dydz. Chúng ta cần phải lập bản đồ những thế giới thực tọa độ để chỉ số mảng tọa độ, do đó cho phép xác định ba biến mới:

xx = x/dx 
yy = y/dy 
zz = z/dz 

Các mảng index đầu vào map_coordinates là một mảng của hình dạng (d, ...) nơi d là số kích thước của dữ liệu gốc của bạn. Nếu bạn xác định một mảng như:

scaling = np.array([dx, dy, dz]) 

bạn có thể chuyển thế giới thực của bạn phối để chỉ số mảng tọa độ bằng cách chia cho scaling với một chút truyền kỳ diệu:

idx = coords/scaling[(slice(None),) + (None,)*(coords.ndim-1)] 

Nói một cách tất cả cùng nhau trong một ví dụ:

dx, dy, dz = 1, 1, 2 
scaling = np.array([dx, dy, dz]) 
data = np.random.rand(10, 15, 5) 

Cho phép nói rằng chúng tôi muốn t o các giá trị nội suy dọc theo mặt phẳng 2*y - z = 0. Chúng tôi có hai vectơ vuông góc với mặt phẳng véc tơ bình thường:

u = np.array([1, 0 ,0]) 
v = np.array([0, 1, 2]) 

Và có được tọa độ mà tại đó chúng tôi muốn suy như:

coords = (u[:, None, None] * np.linspace(0, 9, 10)[None, :, None] + 
      v[:, None, None] * np.linspace(0, 2.5, 10)[None, None, :]) 

Chúng tôi chuyển đổi chúng sang chỉ số mảng tọa độ và interpoalte sử dụng map_coordinates :

idx = coords/scaling[(slice(None),) + (None,)*(coords.ndim-1)] 
new_data = ndi.map_coordinates(data, idx) 

Mảng cuối cùng có dạng (10, 10) và có vị trí [u_idx, v_idx] giá trị tương ứng với tọa độ coords[:, u_idx, v_idx].

Bạn có thể xây dựng trên ý tưởng này để xử lý nội suy nơi tọa độ của bạn không bắt đầu bằng 0, bằng cách thêm một khoảng trống trước khi chia tỷ lệ.

+0

Đó chính xác là những gì tôi cần. Chúc mừng, Jaime! –

7

Đây là một lớp đơn giản Intergrid bản đồ/tỷ lệ không thống nhất với lưới đồng nhất, sau đó thực hiện map_coordinates.
Trên 4d test case nó chạy ở khoảng 1 μ giây cho mỗi điểm truy vấn. Tài liệu HTML là here.

""" interpolate data given on an Nd rectangular grid, uniform or non-uniform. 

Purpose: extend the fast N-dimensional interpolator 
`scipy.ndimage.map_coordinates` to non-uniform grids, using `np.interp`. 

Background: please look at 
http://en.wikipedia.org/wiki/Bilinear_interpolation 
https://stackoverflow.com/questions/6238250/multivariate-spline-interpolation-in-python-scipy 
http://docs.scipy.org/doc/scipy-dev/reference/generated/scipy.ndimage.interpolation.map_coordinates.html 

Example 
------- 
Say we have rainfall on a 4 x 5 grid of rectangles, lat 52 .. 55 x lon -10 .. -6, 
and want to interpolate (estimate) rainfall at 1000 query points 
in between the grid points. 

     # define the grid -- 
    griddata = np.loadtxt(...) # griddata.shape == (4, 5) 
    lo = np.array([ 52, -10 ]) # lowest lat, lowest lon 
    hi = np.array([ 55, -6 ]) # highest lat, highest lon 

     # set up an interpolator function "interfunc()" with class Intergrid -- 
    interfunc = Intergrid(griddata, lo=lo, hi=hi) 

     # generate 1000 random query points, lo <= [lat, lon] <= hi -- 
    query_points = lo + np.random.uniform(size=(1000, 2)) * (hi - lo) 

     # get rainfall at the 1000 query points -- 
    query_values = interfunc(query_points) # -> 1000 values 

What this does: 
    for each [lat, lon] in query_points: 
     1) find the square of griddata it's in, 
      e.g. [52.5, -8.1] -> [0, 3] [0, 4] [1, 4] [1, 3] 
     2) do bilinear (multilinear) interpolation in that square, 
      using `scipy.ndimage.map_coordinates` . 
Check: 
    interfunc(lo) -> griddata[0, 0], 
    interfunc(hi) -> griddata[-1, -1] i.e. griddata[3, 4] 

Parameters 
---------- 
    griddata: numpy array_like, 2d 3d 4d ... 
    lo, hi: user coordinates of the corners of griddata, 1d array-like, lo < hi 
    maps: a list of `dim` descriptors of piecewise-linear or nonlinear maps, 
     e.g. [[50, 52, 62, 63], None] # uniformize lat, linear lon 
    copy: make a copy of query_points, default True; 
     copy=False overwrites query_points, runs in less memory 
    verbose: default 1: print a 1-line summary for each call, with run time 
    order=1: see `map_coordinates` 
    prefilter: 0 or False, the default: smoothing B-spline 
       1 or True: exact-fit interpolating spline (IIR, not C-R) 
       1/3: Mitchell-Netravali spline, 1/3 B + 2/3 fit 
     (prefilter is only for order > 1, since order = 1 interpolates) 

Non-uniform rectangular grids 
----------------------------- 
What if our griddata above is at non-uniformly-spaced latitudes, 
say [50, 52, 62, 63] ? `Intergrid` can "uniformize" these 
before interpolation, like this: 

    lo = np.array([ 50, -10 ]) 
    hi = np.array([ 63, -6 ]) 
    maps = [[50, 52, 62, 63], None] # uniformize lat, linear lon 
    interfunc = Intergrid(griddata, lo=lo, hi=hi, maps=maps) 

This will map (transform, stretch, warp) the lats in query_points column 0 
to array coordinates in the range 0 .. 3, using `np.interp` to do 
piecewise-linear (PWL) mapping: 
    50 51 52 53 54 55 56 57 58 59 60 61 62 63 # lo[0] .. hi[0] 
    0 .5 1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2 3 

`maps[1] None` says to map the lons in query_points column 1 linearly: 
    -10 -9 -8 -7 -6 # lo[1] .. hi[1] 
    0 1 2 3 4 

More doc: https://denis-bz.github.com/docs/intergrid.html 

""" 
# split class Gridmap ? 

from __future__ import division 
from time import time 
# warnings 
import numpy as np 
from scipy.ndimage import map_coordinates, spline_filter 

__version__ = "2014-01-15 jan denis" # 15jan: fix bug in linear scaling 
__author_email__ = "[email protected]" # comments welcome, testcases most welcome 

#............................................................................... 
class Intergrid: 
    __doc__ = globals()["__doc__"] 

    def __init__(self, griddata, lo, hi, maps=[], copy=True, verbose=1, 
      order=1, prefilter=False): 
     griddata = np.asanyarray(griddata) 
     dim = griddata.ndim # - (griddata.shape[-1] == 1) # ?? 
     assert dim >= 2, griddata.shape 
     self.dim = dim 
     if np.isscalar(lo): 
      lo *= np.ones(dim) 
     if np.isscalar(hi): 
      hi *= np.ones(dim) 
     self.loclip = lo = np.asarray_chkfinite(lo).copy() 
     self.hiclip = hi = np.asarray_chkfinite(hi).copy() 
     assert lo.shape == (dim,), lo.shape 
     assert hi.shape == (dim,), hi.shape 
     self.copy = copy 
     self.verbose = verbose 
     self.order = order 
     if order > 1 and 0 < prefilter < 1: # 1/3: Mitchell-Netravali = 1/3 B + 2/3 fit 
      exactfit = spline_filter(griddata) # see Unser 
      griddata += prefilter * (exactfit - griddata) 
      prefilter = False 
     self.griddata = griddata 
     self.prefilter = (prefilter == True) 

     self.maps = maps 
     self.nmap = 0 
     if len(maps) > 0: 
      assert len(maps) == dim, "maps must have len %d, not %d" % (
        dim, len(maps)) 
      # linear maps (map None): Xcol -= lo *= scale -> [0, n-1] 
      # nonlinear: np.interp e.g. [50 52 62 63] -> [0 1 2 3] 
      self._lo = np.zeros(dim) 
      self._scale = np.ones(dim) 

      for j, (map, n, l, h) in enumerate(zip(maps, griddata.shape, lo, hi)): 
       ## print "test: j map n l h:", j, map, n, l, h 
       if map is None or callable(map): 
        self._lo[j] = l 
        if h > l: 
         self._scale[j] = (n - 1)/(h - l) # _map lo -> 0, hi -> n - 1 
        else: 
         self._scale[j] = 0 # h <= l: X[:,j] -> 0 
        continue 
       self.maps[j] = map = np.asanyarray(map) 
       self.nmap += 1 
       assert len(map) == n, "maps[%d] must have len %d, not %d" % (
        j, n, len(map)) 
       mlo, mhi = map.min(), map.max() 
       if not (l <= mlo <= mhi <= h): 
        print "Warning: Intergrid maps[%d] min %.3g max %.3g " \ 
         "are outside lo %.3g hi %.3g" % (
         j, mlo, mhi, l, h) 

#............................................................................... 
    def _map_to_uniform_grid(self, X): 
     """ clip, map X linear/nonlinear inplace """ 
     np.clip(X, self.loclip, self.hiclip, out=X) 
      # X nonlinear maps inplace -- 
     for j, map in enumerate(self.maps): 
      if map is None: 
       continue 
      if callable(map): 
       X[:,j] = map(X[:,j]) # clip again ? 
      else: 
        # PWL e.g. [50 52 62 63] -> [0 1 2 3] -- 
       X[:,j] = np.interp(X[:,j], map, np.arange(len(map))) 

      # linear map the rest, inplace (nonlinear _lo 0, _scale 1: noop) 
     if self.nmap < self.dim: 
      X -= self._lo 
      X *= self._scale # (griddata.shape - 1)/(hi - lo) 
     ## print "test: _map_to_uniform_grid", X.T 

#............................................................................... 
    def __call__(self, X, out=None): 
     """ query_values = Intergrid(...) (query_points npt x dim) 
     """ 
     X = np.asanyarray(X) 
     assert X.shape[-1] == self.dim, ("the query array must have %d columns, " 
       "but its shape is %s" % (self.dim, X.shape)) 
     Xdim = X.ndim 
     if Xdim == 1: 
      X = np.asarray([X]) # in a single point -> out scalar 
     if self.copy: 
      X = X.copy() 
     assert X.ndim == 2, X.shape 
     npt = X.shape[0] 
     if out is None: 
      out = np.empty(npt, dtype=self.griddata.dtype) 
     t0 = time() 
     self._map_to_uniform_grid(X) # X inplace 
#............................................................................... 
     map_coordinates(self.griddata, X.T, 
      order=self.order, prefilter=self.prefilter, 
      mode="nearest", # outside -> edge 
       # test: mode="constant", cval=np.NaN, 
      output=out) 
     if self.verbose: 
      print "Intergrid: %.3g msec %d points in a %s grid %d maps order %d" % (
       (time() - t0) * 1000, npt, self.griddata.shape, self.nmap, self.order) 
     return out if Xdim == 2 else out[0] 

    at = __call__ 

# end intergrid.py 
Các vấn đề liên quan