ROI tests pass with row-major axis order

This commit is contained in:
Luke Campagnola 2016-08-25 18:18:15 -07:00
parent 956251f7ee
commit df691596a7
5 changed files with 80 additions and 25 deletions

View File

@ -3,6 +3,7 @@ from .Qt import QtCore, QtGui
from .Point import Point
import numpy as np
class SRTTransform(QtGui.QTransform):
"""Transform that can always be represented as a combination of 3 matrices: scale * rotate * translate
This transform has no shear; angles are always preserved.
@ -165,6 +166,7 @@ class SRTTransform(QtGui.QTransform):
def matrix(self):
return np.array([[self.m11(), self.m12(), self.m13()],[self.m21(), self.m22(), self.m23()],[self.m31(), self.m32(), self.m33()]])
if __name__ == '__main__':
from . import widgets

View File

@ -37,9 +37,6 @@ class GraphicsItem(object):
if register:
GraphicsScene.registerObject(self) ## workaround for pyqt bug in graphicsscene.items()
def getViewWidget(self):
"""
Return the view widget for this item.
@ -95,7 +92,6 @@ class GraphicsItem(object):
def forgetViewBox(self):
self._viewBox = None
def deviceTransform(self, viewportTransform=None):
"""
Return the transform that converts local item coordinates to device coordinates (usually pixels).

View File

@ -279,6 +279,42 @@ class ImageItem(GraphicsObject):
if gotNewData:
self.sigImageChanged.emit()
def dataTransform(self):
"""Return the transform that maps from this image's input array to its
local coordinate system.
This transform corrects for the transposition that occurs when image data
is interpreted in row-major order.
"""
# Might eventually need to account for downsampling / clipping here
tr = QtGui.QTransform()
if self.axisOrder == 'row-major':
# transpose
tr.scale(1, -1)
tr.rotate(-90)
return tr
def inverseDataTransform(self):
"""Return the transform that maps from this image's local coordinate
system to its input array.
See dataTransform() for more information.
"""
tr = QtGui.QTransform()
if self.axisOrder == 'row-major':
# transpose
tr.scale(1, -1)
tr.rotate(-90)
return tr
def mapToData(self, obj):
tr = self.inverseDataTransform()
return tr.map(obj)
def mapFromData(self, obj):
tr = self.dataTransform()
return tr.map(obj)
def quickMinMax(self, targetSize=1e6):
"""
Estimate the min/max values of the image data by subsampling.

View File

@ -1017,7 +1017,7 @@ class ROI(GraphicsObject):
If returnSlice is set to False, the function returns a pair of tuples with the values that would have
been used to generate the slice objects. ((ax0Start, ax0Stop), (ax1Start, ax1Stop))
If the slice can not be computed (usually because the scene/transforms are not properly
If the slice cannot be computed (usually because the scene/transforms are not properly
constructed yet), then the method returns None.
"""
## Determine shape of array along ROI axes
@ -1104,7 +1104,8 @@ class ROI(GraphicsObject):
shape, vectors, origin = self.getAffineSliceParams(data, img, axes, fromBoundingRect=fromBR)
if not returnMappedCoords:
return fn.affineSlice(data, shape=shape, vectors=vectors, origin=origin, axes=axes, **kwds)
rgn = fn.affineSlice(data, shape=shape, vectors=vectors, origin=origin, axes=axes, **kwds)
return rgn
else:
kwds['returnCoords'] = True
result, coords = fn.affineSlice(data, shape=shape, vectors=vectors, origin=origin, axes=axes, **kwds)
@ -1119,29 +1120,34 @@ class ROI(GraphicsObject):
(shape, vectors, origin) to extract a subset of *data* using this ROI
and *img* to specify the subset.
If *fromBoundingRect* is True, then the ROI's bounding rectangle is used
rather than the shape of the ROI.
See :func:`getArrayRegion <pyqtgraph.ROI.getArrayRegion>` for more information.
"""
if self.scene() is not img.scene():
raise Exception("ROI and target item must be members of the same scene.")
origin = self.mapToItem(img, QtCore.QPointF(0, 0))
origin = img.mapToData(self.mapToItem(img, QtCore.QPointF(0, 0)))
## vx and vy point in the directions of the slice axes, but must be scaled properly
vx = self.mapToItem(img, QtCore.QPointF(1, 0)) - origin
vy = self.mapToItem(img, QtCore.QPointF(0, 1)) - origin
vx = img.mapToData(self.mapToItem(img, QtCore.QPointF(1, 0))) - origin
vy = img.mapToData(self.mapToItem(img, QtCore.QPointF(0, 1))) - origin
lvx = np.sqrt(vx.x()**2 + vx.y()**2)
lvy = np.sqrt(vy.x()**2 + vy.y()**2)
pxLen = img.width() / float(data.shape[axes[0]])
#img.width is number of pixels, not width of item.
#need pxWidth and pxHeight instead of pxLen ?
sx = pxLen / lvx
sy = pxLen / lvy
#pxLen = img.width() / float(data.shape[axes[0]])
##img.width is number of pixels, not width of item.
##need pxWidth and pxHeight instead of pxLen ?
#sx = pxLen / lvx
#sy = pxLen / lvy
sx = 1.0 / lvx
sy = 1.0 / lvy
vectors = ((vx.x()*sx, vx.y()*sx), (vy.x()*sy, vy.y()*sy))
if fromBoundingRect is True:
shape = self.boundingRect().width(), self.boundingRect().height()
origin = self.mapToItem(img, self.boundingRect().topLeft())
origin = img.mapToData(self.mapToItem(img, self.boundingRect().topLeft()))
origin = (origin.x(), origin.y())
else:
shape = self.state['size']
@ -1150,10 +1156,10 @@ class ROI(GraphicsObject):
shape = [abs(shape[0]/sx), abs(shape[1]/sy)]
if getConfigOption('imageAxisOrder') == 'row-major':
vectors = [vectors[1][::-1], vectors[0][::-1]]
# transpose output
vectors = vectors[::-1]
shape = shape[::-1]
origin = origin[::-1]
return shape, vectors, origin
def renderShapeMask(self, width, height):

View File

@ -8,7 +8,7 @@ from pyqtgraph.tests import assertImageApproved, mouseMove, mouseDrag, mouseClic
app = pg.mkQApp()
def test_getArrayRegion():
def test_getArrayRegion(transpose=False):
pr = pg.PolyLineROI([[0, 0], [27, 0], [0, 28]], closed=True)
pr.setPos(1, 1)
rois = [
@ -23,13 +23,19 @@ def test_getArrayRegion():
origMode = pg.getConfigOption('imageAxisOrder')
try:
pg.setConfigOptions(imageAxisOrder='col-major')
check_getArrayRegion(roi, 'roi/'+name, testResize)
pg.setConfigOptions(imageAxisOrder='row-major')
check_getArrayRegion(roi, 'roi/'+name, testResize, transpose=True)
if transpose:
pg.setConfigOptions(imageAxisOrder='row-major')
check_getArrayRegion(roi, 'roi/'+name, testResize, transpose=True)
else:
pg.setConfigOptions(imageAxisOrder='col-major')
check_getArrayRegion(roi, 'roi/'+name, testResize)
finally:
pg.setConfigOptions(imageAxisOrder=origMode)
def test_getArrayRegion_axisorder():
test_getArrayRegion(transpose=True)
def check_getArrayRegion(roi, name, testResize=True, transpose=False):
initState = roi.getState()
@ -55,8 +61,8 @@ def check_getArrayRegion(roi, name, testResize=True, transpose=False):
vb2.setPos(6, 203)
vb2.resize(188, 191)
img1 = TransposedImageItem(border='w', transpose=transpose)
img2 = TransposedImageItem(border='w', transpose=transpose)
img1 = pg.ImageItem(border='w')
img2 = pg.ImageItem(border='w')
vb1.addItem(img1)
vb2.addItem(img2)
@ -68,6 +74,9 @@ def check_getArrayRegion(roi, name, testResize=True, transpose=False):
data[:, :, 2, :] += 10
data[:, :, :, 3] += 10
if transpose:
data = data.transpose(0, 2, 1, 3)
img1.setImage(data[0, ..., 0])
vb1.setAspectLocked()
vb1.enableAutoRange(True, True)
@ -75,6 +84,12 @@ def check_getArrayRegion(roi, name, testResize=True, transpose=False):
roi.setZValue(10)
vb1.addItem(roi)
if isinstance(roi, pg.RectROI):
if transpose:
assert roi.getAffineSliceParams(data, img1, axes=(1, 2)) == ([28.0, 27.0], ((1.0, 0.0), (0.0, 1.0)), (1.0, 1.0))
else:
assert roi.getAffineSliceParams(data, img1, axes=(1, 2)) == ([27.0, 28.0], ((1.0, 0.0), (0.0, 1.0)), (1.0, 1.0))
rgn = roi.getArrayRegion(data, img1, axes=(1, 2))
#assert np.all((rgn == data[:, 1:-2, 1:-2, :]) | (rgn == 0))
img2.setImage(rgn[0, ..., 0])