Merge pull request #415 from campagnola/fix-getarrayregion
Fix getarrayregion
This commit is contained in:
commit
7b20b33a06
@ -51,7 +51,7 @@ install:
|
|||||||
- conda update conda --yes
|
- conda update conda --yes
|
||||||
- conda create -n test_env python=${PYTHON} --yes
|
- conda create -n test_env python=${PYTHON} --yes
|
||||||
- source activate test_env
|
- source activate test_env
|
||||||
- conda install numpy pyopengl pytest flake8 six coverage --yes
|
- conda install numpy scipy pyopengl pytest flake8 six coverage --yes
|
||||||
- echo ${QT}
|
- echo ${QT}
|
||||||
- echo ${TEST}
|
- echo ${TEST}
|
||||||
- echo ${PYTHON}
|
- echo ${PYTHON}
|
||||||
|
@ -410,11 +410,44 @@ def eq(a, b):
|
|||||||
raise Exception("== operator returned type %s" % str(type(e)))
|
raise Exception("== operator returned type %s" % str(type(e)))
|
||||||
|
|
||||||
|
|
||||||
|
def affineSliceCoords(shape, origin, vectors, axes):
|
||||||
|
"""Return the array of coordinates used to sample data arrays in affineSlice().
|
||||||
|
"""
|
||||||
|
# sanity check
|
||||||
|
if len(shape) != len(vectors):
|
||||||
|
raise Exception("shape and vectors must have same length.")
|
||||||
|
if len(origin) != len(axes):
|
||||||
|
raise Exception("origin and axes must have same length.")
|
||||||
|
for v in vectors:
|
||||||
|
if len(v) != len(axes):
|
||||||
|
raise Exception("each vector must be same length as axes.")
|
||||||
|
|
||||||
|
shape = list(map(np.ceil, shape))
|
||||||
|
|
||||||
|
## make sure vectors are arrays
|
||||||
|
if not isinstance(vectors, np.ndarray):
|
||||||
|
vectors = np.array(vectors)
|
||||||
|
if not isinstance(origin, np.ndarray):
|
||||||
|
origin = np.array(origin)
|
||||||
|
origin.shape = (len(axes),) + (1,)*len(shape)
|
||||||
|
|
||||||
|
## Build array of sample locations.
|
||||||
|
grid = np.mgrid[tuple([slice(0,x) for x in shape])] ## mesh grid of indexes
|
||||||
|
x = (grid[np.newaxis,...] * vectors.transpose()[(Ellipsis,) + (np.newaxis,)*len(shape)]).sum(axis=1) ## magic
|
||||||
|
x += origin
|
||||||
|
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
def affineSlice(data, shape, origin, vectors, axes, order=1, returnCoords=False, **kargs):
|
def affineSlice(data, shape, origin, vectors, axes, order=1, returnCoords=False, **kargs):
|
||||||
"""
|
"""
|
||||||
Take a slice of any orientation through an array. This is useful for extracting sections of multi-dimensional arrays such as MRI images for viewing as 1D or 2D data.
|
Take a slice of any orientation through an array. This is useful for extracting sections of multi-dimensional arrays
|
||||||
|
such as MRI images for viewing as 1D or 2D data.
|
||||||
|
|
||||||
The slicing axes are aribtrary; they do not need to be orthogonal to the original data or even to each other. It is possible to use this function to extract arbitrary linear, rectangular, or parallelepiped shapes from within larger datasets. The original data is interpolated onto a new array of coordinates using scipy.ndimage.map_coordinates if it is available (see the scipy documentation for more information about this). If scipy is not available, then a slower implementation of map_coordinates is used.
|
The slicing axes are aribtrary; they do not need to be orthogonal to the original data or even to each other. It is
|
||||||
|
possible to use this function to extract arbitrary linear, rectangular, or parallelepiped shapes from within larger
|
||||||
|
datasets. The original data is interpolated onto a new array of coordinates using either interpolateArray if order<2
|
||||||
|
or scipy.ndimage.map_coordinates otherwise.
|
||||||
|
|
||||||
For a graphical interface to this function, see :func:`ROI.getArrayRegion <pyqtgraph.ROI.getArrayRegion>`
|
For a graphical interface to this function, see :func:`ROI.getArrayRegion <pyqtgraph.ROI.getArrayRegion>`
|
||||||
|
|
||||||
@ -453,47 +486,24 @@ def affineSlice(data, shape, origin, vectors, axes, order=1, returnCoords=False,
|
|||||||
affineSlice(data, shape=(20,20), origin=(40,0,0), vectors=((-1, 1, 0), (-1, 0, 1)), axes=(1,2,3))
|
affineSlice(data, shape=(20,20), origin=(40,0,0), vectors=((-1, 1, 0), (-1, 0, 1)), axes=(1,2,3))
|
||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
x = affineSliceCoords(shape, origin, vectors, axes)
|
||||||
import scipy.ndimage
|
|
||||||
have_scipy = True
|
|
||||||
except ImportError:
|
|
||||||
have_scipy = False
|
|
||||||
have_scipy = False
|
|
||||||
|
|
||||||
# sanity check
|
|
||||||
if len(shape) != len(vectors):
|
|
||||||
raise Exception("shape and vectors must have same length.")
|
|
||||||
if len(origin) != len(axes):
|
|
||||||
raise Exception("origin and axes must have same length.")
|
|
||||||
for v in vectors:
|
|
||||||
if len(v) != len(axes):
|
|
||||||
raise Exception("each vector must be same length as axes.")
|
|
||||||
|
|
||||||
shape = list(map(np.ceil, shape))
|
|
||||||
|
|
||||||
## transpose data so slice axes come first
|
## transpose data so slice axes come first
|
||||||
trAx = list(range(data.ndim))
|
trAx = list(range(data.ndim))
|
||||||
for x in axes:
|
for ax in axes:
|
||||||
trAx.remove(x)
|
trAx.remove(ax)
|
||||||
tr1 = tuple(axes) + tuple(trAx)
|
tr1 = tuple(axes) + tuple(trAx)
|
||||||
data = data.transpose(tr1)
|
data = data.transpose(tr1)
|
||||||
#print "tr1:", tr1
|
#print "tr1:", tr1
|
||||||
## dims are now [(slice axes), (other axes)]
|
## dims are now [(slice axes), (other axes)]
|
||||||
|
|
||||||
## make sure vectors are arrays
|
if order > 1:
|
||||||
if not isinstance(vectors, np.ndarray):
|
try:
|
||||||
vectors = np.array(vectors)
|
import scipy.ndimage
|
||||||
if not isinstance(origin, np.ndarray):
|
except ImportError:
|
||||||
origin = np.array(origin)
|
raise ImportError("Interpolating with order > 1 requires the scipy.ndimage module, but it could not be imported.")
|
||||||
origin.shape = (len(axes),) + (1,)*len(shape)
|
|
||||||
|
|
||||||
## Build array of sample locations.
|
# iterate manually over unused axes since map_coordinates won't do it for us
|
||||||
grid = np.mgrid[tuple([slice(0,x) for x in shape])] ## mesh grid of indexes
|
|
||||||
x = (grid[np.newaxis,...] * vectors.transpose()[(Ellipsis,) + (np.newaxis,)*len(shape)]).sum(axis=1) ## magic
|
|
||||||
x += origin
|
|
||||||
|
|
||||||
## iterate manually over unused axes since map_coordinates won't do it for us
|
|
||||||
if have_scipy:
|
|
||||||
extraShape = data.shape[len(axes):]
|
extraShape = data.shape[len(axes):]
|
||||||
output = np.empty(tuple(shape) + extraShape, dtype=data.dtype)
|
output = np.empty(tuple(shape) + extraShape, dtype=data.dtype)
|
||||||
for inds in np.ndindex(*extraShape):
|
for inds in np.ndindex(*extraShape):
|
||||||
@ -502,8 +512,8 @@ def affineSlice(data, shape, origin, vectors, axes, order=1, returnCoords=False,
|
|||||||
else:
|
else:
|
||||||
# map_coordinates expects the indexes as the first axis, whereas
|
# map_coordinates expects the indexes as the first axis, whereas
|
||||||
# interpolateArray expects indexes at the last axis.
|
# interpolateArray expects indexes at the last axis.
|
||||||
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), order=order)
|
||||||
|
|
||||||
tr = list(range(output.ndim))
|
tr = list(range(output.ndim))
|
||||||
trb = []
|
trb = []
|
||||||
@ -520,16 +530,24 @@ def affineSlice(data, shape, origin, vectors, axes, order=1, returnCoords=False,
|
|||||||
else:
|
else:
|
||||||
return output
|
return output
|
||||||
|
|
||||||
def interpolateArray(data, x, default=0.0):
|
|
||||||
|
def interpolateArray(data, x, default=0.0, order=1):
|
||||||
"""
|
"""
|
||||||
N-dimensional interpolation similar to 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. It differs from `ndimage.map_coordinates` by allowing broadcasting
|
||||||
|
within the input array.
|
||||||
|
|
||||||
*data* is an array of any shape containing the values to be interpolated.
|
============== ===========================================================================================
|
||||||
*x* is an array with (shape[-1] <= data.ndim) containing the locations
|
**Arguments:**
|
||||||
within *data* to interpolate.
|
*data* Array of any shape containing the values to be interpolated.
|
||||||
|
*x* Array with (shape[-1] <= data.ndim) containing the locations within *data* to interpolate.
|
||||||
|
(note: the axes for this argument are transposed relative to the same argument for
|
||||||
|
`ndimage.map_coordinates`).
|
||||||
|
*default* Value to return for locations in *x* that are outside the bounds of *data*.
|
||||||
|
*order* Order of interpolation: 0=nearest, 1=linear.
|
||||||
|
============== ===========================================================================================
|
||||||
|
|
||||||
Returns array of shape (x.shape[:-1] + data.shape[x.shape[-1]:])
|
Returns array of shape (x.shape[:-1] + data.shape[x.shape[-1]:])
|
||||||
|
|
||||||
@ -574,6 +592,9 @@ 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.
|
||||||
"""
|
"""
|
||||||
|
if order not in (0, 1):
|
||||||
|
raise ValueError("interpolateArray requires order=0 or 1 (got %s)" % order)
|
||||||
|
|
||||||
prof = debug.Profiler()
|
prof = debug.Profiler()
|
||||||
|
|
||||||
nd = data.ndim
|
nd = data.ndim
|
||||||
@ -581,46 +602,56 @@ def interpolateArray(data, x, default=0.0):
|
|||||||
if md > nd:
|
if md > nd:
|
||||||
raise TypeError("x.shape[-1] must be less than or equal to data.ndim")
|
raise TypeError("x.shape[-1] must be less than or equal to data.ndim")
|
||||||
|
|
||||||
# First we generate arrays of indexes that are needed to
|
|
||||||
# extract the data surrounding each point
|
|
||||||
fields = np.mgrid[(slice(0,2),) * md]
|
|
||||||
xmin = np.floor(x).astype(int)
|
|
||||||
xmax = xmin + 1
|
|
||||||
indexes = np.concatenate([xmin[np.newaxis, ...], xmax[np.newaxis, ...]])
|
|
||||||
fieldInds = []
|
|
||||||
totalMask = np.ones(x.shape[:-1], dtype=bool) # keep track of out-of-bound indexes
|
totalMask = np.ones(x.shape[:-1], dtype=bool) # keep track of out-of-bound indexes
|
||||||
for ax in range(md):
|
if order == 0:
|
||||||
mask = (xmin[...,ax] >= 0) & (x[...,ax] <= data.shape[ax]-1)
|
xinds = np.round(x).astype(int) # NOTE: for 0.5 this rounds to the nearest *even* number
|
||||||
# keep track of points that need to be set to default
|
for ax in range(md):
|
||||||
totalMask &= mask
|
mask = (xinds[...,ax] >= 0) & (xinds[...,ax] <= data.shape[ax]-1)
|
||||||
|
xinds[...,ax][~mask] = 0
|
||||||
|
# keep track of points that need to be set to default
|
||||||
|
totalMask &= mask
|
||||||
|
result = data[tuple([xinds[...,i] for i in range(xinds.shape[-1])])]
|
||||||
|
|
||||||
# ..and keep track of indexes that are out of bounds
|
elif order == 1:
|
||||||
# (note that when x[...,ax] == data.shape[ax], then xmax[...,ax] will be out
|
# First we generate arrays of indexes that are needed to
|
||||||
# of bounds, but the interpolation will work anyway)
|
# extract the data surrounding each point
|
||||||
mask &= (xmax[...,ax] < data.shape[ax])
|
fields = np.mgrid[(slice(0,order+1),) * md]
|
||||||
axisIndex = indexes[...,ax][fields[ax]]
|
xmin = np.floor(x).astype(int)
|
||||||
axisIndex[axisIndex < 0] = 0
|
xmax = xmin + 1
|
||||||
axisIndex[axisIndex >= data.shape[ax]] = 0
|
indexes = np.concatenate([xmin[np.newaxis, ...], xmax[np.newaxis, ...]])
|
||||||
fieldInds.append(axisIndex)
|
fieldInds = []
|
||||||
prof()
|
for ax in range(md):
|
||||||
|
mask = (xmin[...,ax] >= 0) & (x[...,ax] <= data.shape[ax]-1)
|
||||||
|
# keep track of points that need to be set to default
|
||||||
|
totalMask &= mask
|
||||||
|
|
||||||
# Get data values surrounding each requested point
|
# ..and keep track of indexes that are out of bounds
|
||||||
fieldData = data[tuple(fieldInds)]
|
# (note that when x[...,ax] == data.shape[ax], then xmax[...,ax] will be out
|
||||||
prof()
|
# of bounds, but the interpolation will work anyway)
|
||||||
|
mask &= (xmax[...,ax] < data.shape[ax])
|
||||||
|
axisIndex = indexes[...,ax][fields[ax]]
|
||||||
|
axisIndex[axisIndex < 0] = 0
|
||||||
|
axisIndex[axisIndex >= data.shape[ax]] = 0
|
||||||
|
fieldInds.append(axisIndex)
|
||||||
|
prof()
|
||||||
|
|
||||||
## Interpolate
|
# Get data values surrounding each requested point
|
||||||
s = np.empty((md,) + fieldData.shape, dtype=float)
|
fieldData = data[tuple(fieldInds)]
|
||||||
dx = x - xmin
|
prof()
|
||||||
# reshape fields for arithmetic against dx
|
|
||||||
for ax in range(md):
|
## Interpolate
|
||||||
f1 = fields[ax].reshape(fields[ax].shape + (1,)*(dx.ndim-1))
|
s = np.empty((md,) + fieldData.shape, dtype=float)
|
||||||
sax = f1 * dx[...,ax] + (1-f1) * (1-dx[...,ax])
|
dx = x - xmin
|
||||||
sax = sax.reshape(sax.shape + (1,) * (s.ndim-1-sax.ndim))
|
# reshape fields for arithmetic against dx
|
||||||
s[ax] = sax
|
for ax in range(md):
|
||||||
s = np.product(s, axis=0)
|
f1 = fields[ax].reshape(fields[ax].shape + (1,)*(dx.ndim-1))
|
||||||
result = fieldData * s
|
sax = f1 * dx[...,ax] + (1-f1) * (1-dx[...,ax])
|
||||||
for i in range(md):
|
sax = sax.reshape(sax.shape + (1,) * (s.ndim-1-sax.ndim))
|
||||||
result = result.sum(axis=0)
|
s[ax] = sax
|
||||||
|
s = np.product(s, axis=0)
|
||||||
|
result = fieldData * s
|
||||||
|
for i in range(md):
|
||||||
|
result = result.sum(axis=0)
|
||||||
|
|
||||||
prof()
|
prof()
|
||||||
|
|
||||||
|
@ -2070,9 +2070,9 @@ class LineSegmentROI(ROI):
|
|||||||
if len(positions) > 2:
|
if len(positions) > 2:
|
||||||
raise Exception("LineSegmentROI must be defined by exactly 2 positions. For more points, use PolyLineROI.")
|
raise Exception("LineSegmentROI must be defined by exactly 2 positions. For more points, use PolyLineROI.")
|
||||||
|
|
||||||
|
self.endpoints = []
|
||||||
for i, p in enumerate(positions):
|
for i, p in enumerate(positions):
|
||||||
self.addFreeHandle(p, item=handles[i])
|
self.endpoints.append(self.addFreeHandle(p, item=handles[i]))
|
||||||
|
|
||||||
|
|
||||||
def listPoints(self):
|
def listPoints(self):
|
||||||
return [p['item'].pos() for p in self.handles]
|
return [p['item'].pos() for p in self.handles]
|
||||||
@ -2080,8 +2080,8 @@ class LineSegmentROI(ROI):
|
|||||||
def paint(self, p, *args):
|
def paint(self, p, *args):
|
||||||
p.setRenderHint(QtGui.QPainter.Antialiasing)
|
p.setRenderHint(QtGui.QPainter.Antialiasing)
|
||||||
p.setPen(self.currentPen)
|
p.setPen(self.currentPen)
|
||||||
h1 = self.handles[0]['item'].pos()
|
h1 = self.endpoints[0].pos()
|
||||||
h2 = self.handles[1]['item'].pos()
|
h2 = self.endpoints[1].pos()
|
||||||
p.drawLine(h1, h2)
|
p.drawLine(h1, h2)
|
||||||
|
|
||||||
def boundingRect(self):
|
def boundingRect(self):
|
||||||
@ -2090,8 +2090,8 @@ class LineSegmentROI(ROI):
|
|||||||
def shape(self):
|
def shape(self):
|
||||||
p = QtGui.QPainterPath()
|
p = QtGui.QPainterPath()
|
||||||
|
|
||||||
h1 = self.handles[0]['item'].pos()
|
h1 = self.endpoints[0].pos()
|
||||||
h2 = self.handles[1]['item'].pos()
|
h2 = self.endpoints[1].pos()
|
||||||
dh = h2-h1
|
dh = h2-h1
|
||||||
if dh.length() == 0:
|
if dh.length() == 0:
|
||||||
return p
|
return p
|
||||||
@ -2109,7 +2109,7 @@ class LineSegmentROI(ROI):
|
|||||||
|
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def getArrayRegion(self, data, img, axes=(0,1), order=1, **kwds):
|
def getArrayRegion(self, data, img, axes=(0,1), order=1, returnMappedCoords=False, **kwds):
|
||||||
"""
|
"""
|
||||||
Use the position of this ROI relative to an imageItem to pull a slice
|
Use the position of this ROI relative to an imageItem to pull a slice
|
||||||
from an array.
|
from an array.
|
||||||
@ -2120,15 +2120,15 @@ class LineSegmentROI(ROI):
|
|||||||
See ROI.getArrayRegion() for a description of the arguments.
|
See ROI.getArrayRegion() for a description of the arguments.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
imgPts = [self.mapToItem(img, h['item'].pos()) for h in self.handles]
|
imgPts = [self.mapToItem(img, h.pos()) for h in self.endpoints]
|
||||||
rgns = []
|
rgns = []
|
||||||
for i in range(len(imgPts)-1):
|
coords = []
|
||||||
d = Point(imgPts[i+1] - imgPts[i])
|
|
||||||
o = Point(imgPts[i])
|
|
||||||
r = fn.affineSlice(data, shape=(int(d.length()),), vectors=[Point(d.norm())], origin=o, axes=axes, order=order, **kwds)
|
|
||||||
rgns.append(r)
|
|
||||||
|
|
||||||
return np.concatenate(rgns, axis=axes[0])
|
d = Point(imgPts[1] - imgPts[0])
|
||||||
|
o = Point(imgPts[0])
|
||||||
|
rgn = fn.affineSlice(data, shape=(int(d.length()),), vectors=[Point(d.norm())], origin=o, axes=axes, order=order, returnCoords=returnMappedCoords, **kwds)
|
||||||
|
|
||||||
|
return rgn
|
||||||
|
|
||||||
|
|
||||||
class _PolyLineSegment(LineSegmentROI):
|
class _PolyLineSegment(LineSegmentROI):
|
||||||
|
@ -2,7 +2,7 @@ import numpy as np
|
|||||||
import pytest
|
import pytest
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
from pyqtgraph.Qt import QtCore, QtTest
|
from pyqtgraph.Qt import QtCore, QtTest
|
||||||
from pyqtgraph.tests import assertImageApproved, mouseMove, mouseDrag, mouseClick, TransposedImageItem
|
from pyqtgraph.tests import assertImageApproved, mouseMove, mouseDrag, mouseClick, TransposedImageItem, resizeWindow
|
||||||
|
|
||||||
|
|
||||||
app = pg.mkQApp()
|
app = pg.mkQApp()
|
||||||
@ -43,8 +43,7 @@ def check_getArrayRegion(roi, name, testResize=True, transpose=False):
|
|||||||
#win = pg.GraphicsLayoutWidget()
|
#win = pg.GraphicsLayoutWidget()
|
||||||
win = pg.GraphicsView()
|
win = pg.GraphicsView()
|
||||||
win.show()
|
win.show()
|
||||||
win.resize(200, 400)
|
resizeWindow(win, 200, 400)
|
||||||
|
|
||||||
# Don't use Qt's layouts for testing--these generate unpredictable results.
|
# Don't use Qt's layouts for testing--these generate unpredictable results.
|
||||||
#vb1 = win.addViewBox()
|
#vb1 = win.addViewBox()
|
||||||
#win.nextRow()
|
#win.nextRow()
|
||||||
@ -97,7 +96,6 @@ def check_getArrayRegion(roi, name, testResize=True, transpose=False):
|
|||||||
vb2.enableAutoRange(True, True)
|
vb2.enableAutoRange(True, True)
|
||||||
|
|
||||||
app.processEvents()
|
app.processEvents()
|
||||||
|
|
||||||
assertImageApproved(win, name+'/roi_getarrayregion', 'Simple ROI region selection.')
|
assertImageApproved(win, name+'/roi_getarrayregion', 'Simple ROI region selection.')
|
||||||
|
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
@ -159,7 +157,7 @@ def test_PolyLineROI():
|
|||||||
#plt = pg.plot()
|
#plt = pg.plot()
|
||||||
plt = pg.GraphicsView()
|
plt = pg.GraphicsView()
|
||||||
plt.show()
|
plt.show()
|
||||||
plt.resize(200, 200)
|
resizeWindow(plt, 200, 200)
|
||||||
vb = pg.ViewBox()
|
vb = pg.ViewBox()
|
||||||
plt.scene().addItem(vb)
|
plt.scene().addItem(vb)
|
||||||
vb.resize(200, 200)
|
vb.resize(200, 200)
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
from .image_testing import assertImageApproved, TransposedImageItem
|
from .image_testing import assertImageApproved, TransposedImageItem
|
||||||
from .ui_testing import mousePress, mouseMove, mouseRelease, mouseDrag, mouseClick
|
from .ui_testing import resizeWindow, mousePress, mouseMove, mouseRelease, mouseDrag, mouseClick
|
||||||
|
@ -22,9 +22,17 @@ def testSolve3D():
|
|||||||
assert_array_almost_equal(tr[:3], tr2[:3])
|
assert_array_almost_equal(tr[:3], tr2[:3])
|
||||||
|
|
||||||
|
|
||||||
def test_interpolateArray():
|
def test_interpolateArray_order0():
|
||||||
|
check_interpolateArray(order=0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_interpolateArray_order1():
|
||||||
|
check_interpolateArray(order=1)
|
||||||
|
|
||||||
|
|
||||||
|
def check_interpolateArray(order):
|
||||||
def interpolateArray(data, x):
|
def interpolateArray(data, x):
|
||||||
result = pg.interpolateArray(data, x)
|
result = pg.interpolateArray(data, x, order=order)
|
||||||
assert result.shape == x.shape[:-1] + data.shape[x.shape[-1]:]
|
assert result.shape == x.shape[:-1] + data.shape[x.shape[-1]:]
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@ -48,17 +56,17 @@ def test_interpolateArray():
|
|||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
interpolateArray(data, np.ones((5, 5, 3,)))
|
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.501, 1. ], # NOTE: testing at exactly 0.5 can yield different results from map_coordinates
|
||||||
[ 0.5, 2.5],
|
[ 0.501, 2.501], # due to differences in rounding
|
||||||
[ 10. , 10. ]])
|
[ 10. , 10. ]])
|
||||||
|
|
||||||
result = interpolateArray(data, x)
|
result = interpolateArray(data, x)
|
||||||
#import scipy.ndimage
|
# make sure results match ndimage.map_coordinates
|
||||||
#spresult = scipy.ndimage.map_coordinates(data, x.T, order=1)
|
import scipy.ndimage
|
||||||
spresult = np.array([ 5.92, 20. , 11. , 0. , 0. ]) # generated with the above line
|
spresult = scipy.ndimage.map_coordinates(data, x.T, order=order)
|
||||||
|
#spresult = np.array([ 5.92, 20. , 11. , 0. , 0. ]) # generated with the above line
|
||||||
|
|
||||||
assert_array_almost_equal(result, spresult)
|
assert_array_almost_equal(result, spresult)
|
||||||
|
|
||||||
@ -74,28 +82,17 @@ def test_interpolateArray():
|
|||||||
|
|
||||||
|
|
||||||
# test mapping 2D array of locations
|
# test mapping 2D array of locations
|
||||||
x = np.array([[[0.5, 0.5], [0.5, 1.0], [0.5, 1.5]],
|
x = np.array([[[0.501, 0.501], [0.501, 1.0], [0.501, 1.501]],
|
||||||
[[1.5, 0.5], [1.5, 1.0], [1.5, 1.5]]])
|
[[1.501, 0.501], [1.501, 1.0], [1.501, 1.501]]])
|
||||||
|
|
||||||
r1 = 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=order)
|
||||||
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))
|
||||||
|
@ -1,11 +1,32 @@
|
|||||||
|
import time
|
||||||
|
from ..Qt import QtCore, QtGui, QtTest, QT_LIB
|
||||||
|
|
||||||
|
|
||||||
|
def resizeWindow(win, w, h, timeout=2.0):
|
||||||
|
"""Resize a window and wait until it has the correct size.
|
||||||
|
|
||||||
|
This is required for unit testing on some platforms that do not guarantee
|
||||||
|
immediate response from the windowing system.
|
||||||
|
"""
|
||||||
|
QtGui.QApplication.processEvents()
|
||||||
|
# Sometimes the window size will switch multiple times before settling
|
||||||
|
# on its final size. Adding qWaitForWindowShown seems to help with this.
|
||||||
|
QtTest.QTest.qWaitForWindowShown(win)
|
||||||
|
win.resize(w, h)
|
||||||
|
start = time.time()
|
||||||
|
while True:
|
||||||
|
w1, h1 = win.width(), win.height()
|
||||||
|
if (w,h) == (w1,h1):
|
||||||
|
return
|
||||||
|
QtTest.QTest.qWait(10)
|
||||||
|
if time.time()-start > timeout:
|
||||||
|
raise TimeoutError("Window resize failed (requested %dx%d, got %dx%d)" % (w, h, w1, h1))
|
||||||
|
|
||||||
|
|
||||||
# Functions for generating user input events.
|
# Functions for generating user input events.
|
||||||
# We would like to use QTest for this purpose, but it seems to be broken.
|
# We would like to use QTest for this purpose, but it seems to be broken.
|
||||||
# See: http://stackoverflow.com/questions/16299779/qt-qgraphicsview-unit-testing-how-to-keep-the-mouse-in-a-pressed-state
|
# See: http://stackoverflow.com/questions/16299779/qt-qgraphicsview-unit-testing-how-to-keep-the-mouse-in-a-pressed-state
|
||||||
|
|
||||||
from ..Qt import QtCore, QtGui, QT_LIB
|
|
||||||
|
|
||||||
|
|
||||||
def mousePress(widget, pos, button, modifier=None):
|
def mousePress(widget, pos, button, modifier=None):
|
||||||
if isinstance(widget, QtGui.QGraphicsView):
|
if isinstance(widget, QtGui.QGraphicsView):
|
||||||
widget = widget.viewport()
|
widget = widget.viewport()
|
||||||
@ -52,4 +73,3 @@ def mouseClick(widget, pos, button, modifier=None):
|
|||||||
mouseMove(widget, pos)
|
mouseMove(widget, pos)
|
||||||
mousePress(widget, pos, button, modifier)
|
mousePress(widget, pos, button, modifier)
|
||||||
mouseRelease(widget, pos, button, modifier)
|
mouseRelease(widget, pos, button, modifier)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user