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

View File

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

View File

@ -279,6 +279,42 @@ class ImageItem(GraphicsObject):
if gotNewData: if gotNewData:
self.sigImageChanged.emit() 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): def quickMinMax(self, targetSize=1e6):
""" """
Estimate the min/max values of the image data by subsampling. Estimate the min/max values of the image data by subsampling.

View File

@ -1104,7 +1104,8 @@ class ROI(GraphicsObject):
shape, vectors, origin = self.getAffineSliceParams(data, img, axes, fromBoundingRect=fromBR) shape, vectors, origin = self.getAffineSliceParams(data, img, axes, fromBoundingRect=fromBR)
if not returnMappedCoords: 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: 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)
@ -1119,29 +1120,34 @@ class ROI(GraphicsObject):
(shape, vectors, origin) to extract a subset of *data* using this ROI (shape, vectors, origin) to extract a subset of *data* using this ROI
and *img* to specify the subset. 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. See :func:`getArrayRegion <pyqtgraph.ROI.getArrayRegion>` for more information.
""" """
if self.scene() is not img.scene(): if self.scene() is not img.scene():
raise Exception("ROI and target item must be members of the same 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 and vy point in the directions of the slice axes, but must be scaled properly
vx = self.mapToItem(img, QtCore.QPointF(1, 0)) - origin vx = img.mapToData(self.mapToItem(img, QtCore.QPointF(1, 0))) - origin
vy = self.mapToItem(img, QtCore.QPointF(0, 1)) - origin vy = img.mapToData(self.mapToItem(img, QtCore.QPointF(0, 1))) - origin
lvx = np.sqrt(vx.x()**2 + vx.y()**2) lvx = np.sqrt(vx.x()**2 + vx.y()**2)
lvy = np.sqrt(vy.x()**2 + vy.y()**2) lvy = np.sqrt(vy.x()**2 + vy.y()**2)
pxLen = img.width() / float(data.shape[axes[0]]) #pxLen = img.width() / float(data.shape[axes[0]])
#img.width is number of pixels, not width of item. ##img.width is number of pixels, not width of item.
#need pxWidth and pxHeight instead of pxLen ? ##need pxWidth and pxHeight instead of pxLen ?
sx = pxLen / lvx #sx = pxLen / lvx
sy = pxLen / lvy #sy = pxLen / lvy
sx = 1.0 / lvx
sy = 1.0 / lvy
vectors = ((vx.x()*sx, vx.y()*sx), (vy.x()*sy, vy.y()*sy)) vectors = ((vx.x()*sx, vx.y()*sx), (vy.x()*sy, vy.y()*sy))
if fromBoundingRect is True: if fromBoundingRect is True:
shape = self.boundingRect().width(), self.boundingRect().height() 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()) origin = (origin.x(), origin.y())
else: else:
shape = self.state['size'] shape = self.state['size']
@ -1150,9 +1156,9 @@ class ROI(GraphicsObject):
shape = [abs(shape[0]/sx), abs(shape[1]/sy)] shape = [abs(shape[0]/sx), abs(shape[1]/sy)]
if getConfigOption('imageAxisOrder') == 'row-major': if getConfigOption('imageAxisOrder') == 'row-major':
vectors = [vectors[1][::-1], vectors[0][::-1]] # transpose output
vectors = vectors[::-1]
shape = shape[::-1] shape = shape[::-1]
origin = origin[::-1]
return shape, vectors, origin return shape, vectors, origin

View File

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