ROI tests pass

FIX: PolyLineROI.setPoints() did not clear points previously
API: Allow ROI.setPos(x, y) in addition to setPos([x, y])
This commit is contained in:
Luke Campagnola 2016-05-17 18:04:52 -07:00
parent ccf2ae4db4
commit bb507cf6d0
2 changed files with 51 additions and 25 deletions

View File

@ -213,7 +213,7 @@ class ROI(GraphicsObject):
"""Return the angle of the ROI in degrees.""" """Return the angle of the ROI in degrees."""
return self.getState()['angle'] return self.getState()['angle']
def setPos(self, pos, update=True, finish=True): def setPos(self, pos, y=None, update=True, finish=True):
"""Set the position of the ROI (in the parent's coordinate system). """Set the position of the ROI (in the parent's coordinate system).
By default, this will cause both sigRegionChanged and sigRegionChangeFinished to be emitted. By default, this will cause both sigRegionChanged and sigRegionChangeFinished to be emitted.
@ -225,10 +225,13 @@ class ROI(GraphicsObject):
multiple change functions to be called sequentially while minimizing processing overhead multiple change functions to be called sequentially while minimizing processing overhead
and repeated signals. Setting update=False also forces finish=False. and repeated signals. Setting update=False also forces finish=False.
""" """
# This avoids the temptation to do setPos(x, y) if y is None:
if not isinstance(update, bool): pos = Point(pos)
raise TypeError("update argument must be bool.") else:
pos = Point(pos) # avoid ambiguity where update is provided as a positional argument
if isinstance(y, bool):
raise TypeError("Positional arguments to setPos() must be numerical.")
pos = Point(pos, y)
self.state['pos'] = pos self.state['pos'] = pos
QtGui.QGraphicsItem.setPos(self, pos) QtGui.QGraphicsItem.setPos(self, pos)
if update: if update:
@ -921,8 +924,9 @@ class ROI(GraphicsObject):
if self.lastState is None: if self.lastState is None:
changed = True changed = True
else: else:
for k in list(self.state.keys()): state = self.getState()
if self.state[k] != self.lastState[k]: for k in list(state.keys()):
if state[k] != self.lastState[k]:
changed = True changed = True
self.prepareGeometryChange() self.prepareGeometryChange()
@ -942,7 +946,7 @@ class ROI(GraphicsObject):
self.sigRegionChanged.emit(self) self.sigRegionChanged.emit(self)
self.freeHandleMoved = False self.freeHandleMoved = False
self.lastState = self.stateCopy() self.lastState = self.getState()
if finish: if finish:
self.stateChangeFinished() self.stateChangeFinished()
@ -1133,6 +1137,9 @@ class ROI(GraphicsObject):
This can be used to mask array selections. This can be used to mask array selections.
""" """
if width == 0 or height == 0:
return np.empty((width, height), dtype=float)
# QImage(width, height, format) # QImage(width, height, format)
im = QtGui.QImage(width, height, QtGui.QImage.Format_ARGB32) im = QtGui.QImage(width, height, QtGui.QImage.Format_ARGB32)
im.fill(0x0) im.fill(0x0)
@ -1864,6 +1871,8 @@ class PolyLineROI(ROI):
if closed is not None: if closed is not None:
self.closed = closed self.closed = closed
self.clearPoints()
for p in points: for p in points:
self.addFreeHandle(p) self.addFreeHandle(p)
@ -1877,7 +1886,14 @@ class PolyLineROI(ROI):
Remove all handles and segments. Remove all handles and segments.
""" """
while len(self.handles) > 0: while len(self.handles) > 0:
self.removeHandle(self.handles[0]['item']) update = len(self.handles) == 1
self.removeHandle(self.handles[0]['item'], updateSegments=update)
def getState(self):
state = ROI.getState(self)
state['closed'] = self.closed
state['points'] = [Point(h.pos()) for h in self.getHandles()]
return state
def saveState(self): def saveState(self):
state = ROI.saveState(self) state = ROI.saveState(self)
@ -1887,7 +1903,6 @@ class PolyLineROI(ROI):
def setState(self, state): def setState(self, state):
ROI.setState(self, state) ROI.setState(self, state)
self.clearPoints()
self.setPoints(state['points'], closed=state['closed']) self.setPoints(state['points'], closed=state['closed'])
def addSegment(self, h1, h2, index=None): def addSegment(self, h1, h2, index=None):
@ -1912,6 +1927,7 @@ class PolyLineROI(ROI):
def addHandle(self, info, index=None): def addHandle(self, info, index=None):
h = ROI.addHandle(self, info, index=index) h = ROI.addHandle(self, info, index=index)
h.sigRemoveRequested.connect(self.removeHandle) h.sigRemoveRequested.connect(self.removeHandle)
self.stateChanged(finish=True)
return h return h
def segmentClicked(self, segment, ev=None, pos=None): ## pos should be in this item's coordinate system def segmentClicked(self, segment, ev=None, pos=None): ## pos should be in this item's coordinate system
@ -1944,6 +1960,7 @@ class PolyLineROI(ROI):
handles.remove(handle) handles.remove(handle)
segments[0].replaceHandle(handle, handles[0]) segments[0].replaceHandle(handle, handles[0])
self.removeSegment(segments[1]) self.removeSegment(segments[1])
self.stateChanged(finish=True)
def removeSegment(self, seg): def removeSegment(self, seg):
for handle in seg.handles[:]: for handle in seg.handles[:]:
@ -1973,19 +1990,23 @@ class PolyLineROI(ROI):
for i in range(len(self.handles)): for i in range(len(self.handles)):
p.lineTo(self.handles[i]['item'].pos()) p.lineTo(self.handles[i]['item'].pos())
p.lineTo(self.handles[0]['item'].pos()) p.lineTo(self.handles[0]['item'].pos())
return p return p
def getArrayRegion(self, data, img, axes=(0,1)): def getArrayRegion(self, data, img, axes=(0,1)):
""" """
Return the result of ROI.getArrayRegion(), masked by the shape of the Return the result of ROI.getArrayRegion(), masked by the shape of the
ROI. Values outside the ROI shape are set to 0. ROI. Values outside the ROI shape are set to 0.
""" """
br = self.boundingRect()
if br.width() > 1000:
raise Exception()
sliced = ROI.getArrayRegion(self, data, img, axes=axes, fromBoundingRect=True) sliced = ROI.getArrayRegion(self, data, img, axes=axes, fromBoundingRect=True)
mask = self.renderShapeMask(sliced.shape[axes[0]], sliced.shape[axes[1]]) mask = self.renderShapeMask(sliced.shape[axes[0]], sliced.shape[axes[1]])
shape = [1] * data.ndim shape = [1] * data.ndim
shape[axes[0]] = sliced.shape[axes[0]] shape[axes[0]] = sliced.shape[axes[0]]
shape[axes[1]] = sliced.shape[axes[1]] shape[axes[1]] = sliced.shape[axes[1]]
mask = mask.reshape(shape) mask = mask.reshape(shape)
return sliced * mask return sliced * mask
def setPen(self, *args, **kwds): def setPen(self, *args, **kwds):

View File

@ -8,17 +8,24 @@ app = pg.mkQApp()
def test_getArrayRegion(): def test_getArrayRegion():
pr = pg.PolyLineROI([[0, 0], [27, 0], [0, 28]], closed=True)
pr.setPos(1, 1)
rois = [ rois = [
(pg.ROI([1, 1], [27, 28], pen='y'), 'baseroi'), (pg.ROI([1, 1], [27, 28], pen='y'), 'baseroi'),
(pg.RectROI([1, 1], [27, 28], pen='y'), 'rectroi'), (pg.RectROI([1, 1], [27, 28], pen='y'), 'rectroi'),
(pg.EllipseROI([1, 1], [27, 28], pen='y'), 'ellipseroi'), (pg.EllipseROI([1, 1], [27, 28], pen='y'), 'ellipseroi'),
(pg.PolyLineROI([[0, 0], [27, 0], [0, 28]], closed=True), 'polylineroi'), (pr, 'polylineroi'),
] ]
for roi, name in rois: for roi, name in rois:
check_getArrayRegion(roi, name) # For some ROIs, resize should not be used.
testResize = not isinstance(roi, pg.PolyLineROI)
check_getArrayRegion(roi, 'roi/'+name, testResize)
def check_getArrayRegion(roi, name): def check_getArrayRegion(roi, name, testResize=True):
initState = roi.getState()
win = pg.GraphicsLayoutWidget() win = pg.GraphicsLayoutWidget()
win.show() win.show()
win.resize(200, 400) win.resize(200, 400)
@ -46,7 +53,7 @@ def check_getArrayRegion(roi, name):
vb1.addItem(roi) vb1.addItem(roi)
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])
vb2.setAspectLocked() vb2.setAspectLocked()
vb2.enableAutoRange(True, True) vb2.enableAutoRange(True, True)
@ -56,7 +63,7 @@ def check_getArrayRegion(roi, name):
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):
roi.setPos(0, 0) roi.setPos(0, False)
roi.setPos([0.5, 1.5]) roi.setPos([0.5, 1.5])
rgn = roi.getArrayRegion(data, img1, axes=(1, 2)) rgn = roi.getArrayRegion(data, img1, axes=(1, 2))
@ -71,11 +78,12 @@ def check_getArrayRegion(roi, name):
app.processEvents() app.processEvents()
assertImageApproved(win, name+'/roi_getarrayregion_rotate', 'Simple ROI region selection, rotation.') assertImageApproved(win, name+'/roi_getarrayregion_rotate', 'Simple ROI region selection, rotation.')
roi.setSize([60, 60]) if testResize:
rgn = roi.getArrayRegion(data, img1, axes=(1, 2)) roi.setSize([60, 60])
img2.setImage(rgn[0, ..., 0]) rgn = roi.getArrayRegion(data, img1, axes=(1, 2))
app.processEvents() img2.setImage(rgn[0, ..., 0])
assertImageApproved(win, name+'/roi_getarrayregion_resize', 'Simple ROI region selection, resized.') app.processEvents()
assertImageApproved(win, name+'/roi_getarrayregion_resize', 'Simple ROI region selection, resized.')
img1.scale(1, -1) img1.scale(1, -1)
img1.setPos(0, img1.height()) img1.setPos(0, img1.height())
@ -91,13 +99,10 @@ def check_getArrayRegion(roi, name):
app.processEvents() app.processEvents()
assertImageApproved(win, name+'/roi_getarrayregion_inverty', 'Simple ROI region selection, view inverted.') assertImageApproved(win, name+'/roi_getarrayregion_inverty', 'Simple ROI region selection, view inverted.')
roi.setAngle(0) roi.setState(initState)
roi.setSize(30, 30)
roi.setPos([0, 0])
img1.resetTransform() img1.resetTransform()
img1.setPos(0, 0) img1.setPos(0, 0)
img1.scale(1, 0.5) img1.scale(1, 0.5)
#img1.scale(0.5, 1)
rgn = roi.getArrayRegion(data, img1, axes=(1, 2)) rgn = roi.getArrayRegion(data, img1, axes=(1, 2))
img2.setImage(rgn[0, ..., 0]) img2.setImage(rgn[0, ..., 0])
app.processEvents() app.processEvents()