Merge branch 'fix-interpolate' into develop

This commit is contained in:
Luke Campagnola 2015-02-16 11:39:01 -05:00
commit baefdd0880
4 changed files with 60 additions and 33 deletions

View File

@ -2,6 +2,7 @@ pyqtgraph-0.9.11 [unreleased]
Bugfixes: Bugfixes:
- Fixed git version string generation on python3 - Fixed git version string generation on python3
- Fixed setting default values for out-of-bound points in pg.interpolateArray
pyqtgraph-0.9.10 pyqtgraph-0.9.10

View File

@ -447,11 +447,9 @@ def affineSlice(data, shape, origin, vectors, axes, order=1, returnCoords=False,
## Build array of sample locations. ## Build array of sample locations.
grid = np.mgrid[tuple([slice(0,x) for x in shape])] ## mesh grid of indexes grid = np.mgrid[tuple([slice(0,x) for x in shape])] ## mesh grid of indexes
#print shape, grid.shape
x = (grid[np.newaxis,...] * vectors.transpose()[(Ellipsis,) + (np.newaxis,)*len(shape)]).sum(axis=1) ## magic x = (grid[np.newaxis,...] * vectors.transpose()[(Ellipsis,) + (np.newaxis,)*len(shape)]).sum(axis=1) ## magic
x += origin x += origin
#print "X values:"
#print x
## iterate manually over unused axes since map_coordinates won't do it for us ## iterate manually over unused axes since map_coordinates won't do it for us
if have_scipy: if have_scipy:
extraShape = data.shape[len(axes):] extraShape = data.shape[len(axes):]
@ -465,7 +463,6 @@ def affineSlice(data, shape, origin, vectors, axes, order=1, returnCoords=False,
tr = tuple(range(1,x.ndim)) + (0,) tr = tuple(range(1,x.ndim)) + (0,)
output = interpolateArray(data, x.transpose(tr)) output = interpolateArray(data, x.transpose(tr))
tr = list(range(output.ndim)) tr = list(range(output.ndim))
trb = [] trb = []
for i in range(min(axes)): for i in range(min(axes)):
@ -483,7 +480,7 @@ def affineSlice(data, shape, origin, vectors, axes, order=1, returnCoords=False,
def interpolateArray(data, x, default=0.0): def interpolateArray(data, x, default=0.0):
""" """
N-dimensional interpolation similar scipy.ndimage.map_coordinates. N-dimensional interpolation similar to scipy.ndimage.map_coordinates.
This function returns linearly-interpolated values sampled from a regular This function returns linearly-interpolated values sampled from a regular
grid of data. grid of data.
@ -492,7 +489,7 @@ def interpolateArray(data, x, default=0.0):
*x* is an array with (shape[-1] <= data.ndim) containing the locations *x* is an array with (shape[-1] <= data.ndim) containing the locations
within *data* to interpolate. within *data* to interpolate.
Returns array of shape (x.shape[:-1] + data.shape) Returns array of shape (x.shape[:-1] + data.shape[x.shape[-1]:])
For example, assume we have the following 2D image data:: For example, assume we have the following 2D image data::
@ -535,11 +532,12 @@ def interpolateArray(data, x, default=0.0):
This is useful for interpolating from arrays of colors, vertexes, etc. This is useful for interpolating from arrays of colors, vertexes, etc.
""" """
prof = debug.Profiler() prof = debug.Profiler()
nd = data.ndim nd = data.ndim
md = x.shape[-1] md = x.shape[-1]
if md > nd:
raise TypeError("x.shape[-1] must be less than or equal to data.ndim")
# First we generate arrays of indexes that are needed to # First we generate arrays of indexes that are needed to
# extract the data surrounding each point # extract the data surrounding each point
@ -559,14 +557,12 @@ def interpolateArray(data, x, default=0.0):
# of bounds, but the interpolation will work anyway) # of bounds, but the interpolation will work anyway)
mask &= (xmax[...,ax] < data.shape[ax]) mask &= (xmax[...,ax] < data.shape[ax])
axisIndex = indexes[...,ax][fields[ax]] axisIndex = indexes[...,ax][fields[ax]]
#axisMask = mask.astype(np.ubyte).reshape((1,)*(fields.ndim-1) + mask.shape)
axisIndex[axisIndex < 0] = 0 axisIndex[axisIndex < 0] = 0
axisIndex[axisIndex >= data.shape[ax]] = 0 axisIndex[axisIndex >= data.shape[ax]] = 0
fieldInds.append(axisIndex) fieldInds.append(axisIndex)
prof() prof()
# Get data values surrounding each requested point # Get data values surrounding each requested point
# fieldData[..., i] contains all 2**nd values needed to interpolate x[i]
fieldData = data[tuple(fieldInds)] fieldData = data[tuple(fieldInds)]
prof() prof()
@ -585,8 +581,13 @@ def interpolateArray(data, x, default=0.0):
result = result.sum(axis=0) result = result.sum(axis=0)
prof() prof()
totalMask.shape = totalMask.shape + (1,) * (nd - md)
result[~totalMask] = default if totalMask.ndim > 0:
result[~totalMask] = default
else:
if totalMask is False:
result[:] = default
prof() prof()
return result return result

View File

@ -1061,8 +1061,8 @@ class ROI(GraphicsObject):
=================== ==================================================== =================== ====================================================
This method uses :func:`affineSlice <pyqtgraph.affineSlice>` to generate This method uses :func:`affineSlice <pyqtgraph.affineSlice>` to generate
the slice from *data* and uses :func:`getAffineSliceParams <pyqtgraph.ROI.getAffineSliceParams>` to determine the parameters to the slice from *data* and uses :func:`getAffineSliceParams <pyqtgraph.ROI.getAffineSliceParams>`
pass to :func:`affineSlice <pyqtgraph.affineSlice>`. to determine the parameters to pass to :func:`affineSlice <pyqtgraph.affineSlice>`.
If *returnMappedCoords* is True, then the method returns a tuple (result, coords) If *returnMappedCoords* is True, then the method returns a tuple (result, coords)
such that coords is the set of coordinates used to interpolate values from the original such that coords is the set of coordinates used to interpolate values from the original
@ -1079,24 +1079,16 @@ class ROI(GraphicsObject):
else: else:
kwds['returnCoords'] = True kwds['returnCoords'] = True
result, coords = fn.affineSlice(data, shape=shape, vectors=vectors, origin=origin, axes=axes, **kwds) result, coords = fn.affineSlice(data, shape=shape, vectors=vectors, origin=origin, axes=axes, **kwds)
#tr = fn.transformToArray(img.transform())[:2] ## remove perspective transform values
### separate translation from scale/rotate
#translate = tr[:,2]
#tr = tr[:,:2]
#tr = tr.reshape((2,2) + (1,)*(coords.ndim-1))
#coords = coords[np.newaxis, ...]
### map coordinates and return ### map coordinates and return
#mapped = (tr*coords).sum(axis=0) ## apply scale/rotate
#mapped += translate.reshape((2,1,1))
mapped = fn.transformCoordinates(img.transform(), coords) mapped = fn.transformCoordinates(img.transform(), coords)
return result, mapped return result, mapped
def getAffineSliceParams(self, data, img, axes=(0,1)): def getAffineSliceParams(self, data, img, axes=(0,1)):
""" """
Returns the parameters needed to use :func:`affineSlice <pyqtgraph.affineSlice>` to Returns the parameters needed to use :func:`affineSlice <pyqtgraph.affineSlice>`
extract a subset of *data* using this ROI and *img* to specify the subset. (shape, vectors, origin) to extract a subset of *data* using this ROI
and *img* to specify the subset.
See :func:`getArrayRegion <pyqtgraph.ROI.getArrayRegion>` for more information. See :func:`getArrayRegion <pyqtgraph.ROI.getArrayRegion>` for more information.
""" """
@ -1138,8 +1130,6 @@ class ROI(GraphicsObject):
relativeTo['scale'] = relativeTo['size'] relativeTo['scale'] = relativeTo['size']
st['scale'] = st['size'] st['scale'] = st['size']
t1 = SRTTransform(relativeTo) t1 = SRTTransform(relativeTo)
t2 = SRTTransform(st) t2 = SRTTransform(st)
return t2/t1 return t2/t1

View File

@ -1,6 +1,7 @@
import pyqtgraph as pg import pyqtgraph as pg
import numpy as np import numpy as np
from numpy.testing import assert_array_almost_equal, assert_almost_equal from numpy.testing import assert_array_almost_equal, assert_almost_equal
import pytest
np.random.seed(12345) np.random.seed(12345)
@ -22,18 +23,39 @@ def testSolve3D():
def test_interpolateArray(): def test_interpolateArray():
def interpolateArray(data, x):
result = pg.interpolateArray(data, x)
assert result.shape == x.shape[:-1] + data.shape[x.shape[-1]:]
return result
data = np.array([[ 1., 2., 4. ], data = np.array([[ 1., 2., 4. ],
[ 10., 20., 40. ], [ 10., 20., 40. ],
[ 100., 200., 400.]]) [ 100., 200., 400.]])
# test various x shapes
interpolateArray(data, np.ones((1,)))
interpolateArray(data, np.ones((2,)))
interpolateArray(data, np.ones((1, 1)))
interpolateArray(data, np.ones((1, 2)))
interpolateArray(data, np.ones((5, 1)))
interpolateArray(data, np.ones((5, 2)))
interpolateArray(data, np.ones((5, 5, 1)))
interpolateArray(data, np.ones((5, 5, 2)))
with pytest.raises(TypeError):
interpolateArray(data, np.ones((3,)))
with pytest.raises(TypeError):
interpolateArray(data, np.ones((1, 3,)))
with pytest.raises(TypeError):
interpolateArray(data, np.ones((5, 5, 3,)))
x = np.array([[ 0.3, 0.6], x = np.array([[ 0.3, 0.6],
[ 1. , 1. ], [ 1. , 1. ],
[ 0.5, 1. ], [ 0.5, 1. ],
[ 0.5, 2.5], [ 0.5, 2.5],
[ 10. , 10. ]]) [ 10. , 10. ]])
result = pg.interpolateArray(data, x) result = interpolateArray(data, x)
#import scipy.ndimage #import scipy.ndimage
#spresult = scipy.ndimage.map_coordinates(data, x.T, order=1) #spresult = scipy.ndimage.map_coordinates(data, x.T, order=1)
spresult = np.array([ 5.92, 20. , 11. , 0. , 0. ]) # generated with the above line spresult = np.array([ 5.92, 20. , 11. , 0. , 0. ]) # generated with the above line
@ -44,9 +66,10 @@ def test_interpolateArray():
x = np.array([[ 0.3, 0], x = np.array([[ 0.3, 0],
[ 0.3, 1], [ 0.3, 1],
[ 0.3, 2]]) [ 0.3, 2]])
r1 = interpolateArray(data, x)
x = np.array([0.3]) # should broadcast across axis 1
r2 = interpolateArray(data, x)
r1 = pg.interpolateArray(data, x)
r2 = pg.interpolateArray(data, x[0,:1])
assert_array_almost_equal(r1, r2) assert_array_almost_equal(r1, r2)
@ -54,13 +77,25 @@ def test_interpolateArray():
x = np.array([[[0.5, 0.5], [0.5, 1.0], [0.5, 1.5]], x = np.array([[[0.5, 0.5], [0.5, 1.0], [0.5, 1.5]],
[[1.5, 0.5], [1.5, 1.0], [1.5, 1.5]]]) [[1.5, 0.5], [1.5, 1.0], [1.5, 1.5]]])
r1 = pg.interpolateArray(data, x) r1 = interpolateArray(data, x)
#r2 = scipy.ndimage.map_coordinates(data, x.transpose(2,0,1), order=1) #r2 = scipy.ndimage.map_coordinates(data, x.transpose(2,0,1), order=1)
r2 = np.array([[ 8.25, 11. , 16.5 ], # generated with the above line r2 = np.array([[ 8.25, 11. , 16.5 ], # generated with the above line
[ 82.5 , 110. , 165. ]]) [ 82.5 , 110. , 165. ]])
assert_array_almost_equal(r1, r2) assert_array_almost_equal(r1, r2)
# test interpolate where data.ndim > x.shape[1]
data = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]) # 2x2x3
x = np.array([[1, 1], [0, 0.5], [5, 5]])
r1 = interpolateArray(data, x)
assert np.all(r1[0] == data[1, 1])
assert np.all(r1[1] == 0.5 * (data[0, 0] + data[0, 1]))
assert np.all(r1[2] == 0)
def test_subArray(): def test_subArray():
a = np.array([0, 0, 111, 112, 113, 0, 121, 122, 123, 0, 0, 0, 211, 212, 213, 0, 221, 222, 223, 0, 0, 0, 0]) a = np.array([0, 0, 111, 112, 113, 0, 121, 122, 123, 0, 0, 0, 211, 212, 213, 0, 221, 222, 223, 0, 0, 0, 0])
b = pg.subArray(a, offset=2, shape=(2,2,3), stride=(10,4,1)) b = pg.subArray(a, offset=2, shape=(2,2,3), stride=(10,4,1))