@@ -2067,6 +2067,10 @@ def gui_createToolBars(self):
2067
2067
)
2068
2068
self.addDelPolyLineRoiAction.roiType = 'polyline'
2069
2069
2070
+ self.drawClearRegionAction = editToolBar.addWidget(
2071
+ self.drawClearRegionButton
2072
+ )
2073
+
2070
2074
editToolBar.addAction(self.delBorderObjAction)
2071
2075
2072
2076
self.addDelRoiAction.toolbar = editToolBar
@@ -2896,6 +2900,15 @@ def gui_createControlsToolbar(self):
2896
2900
self.copyLostObjToolbar.setVisible(False)
2897
2901
self.controlToolBars.append(self.copyLostObjToolbar)
2898
2902
2903
+ # Copy lost object contour toolbar
2904
+ self.drawClearRegionToolbar = widgets.DrawClearRegionToolbar(
2905
+ "Draw freehand region and clear objects controls", self
2906
+ )
2907
+
2908
+ self.addToolBar(Qt.TopToolBarArea, self.drawClearRegionToolbar)
2909
+ self.drawClearRegionToolbar.setVisible(False)
2910
+ self.controlToolBars.append(self.drawClearRegionToolbar)
2911
+
2899
2912
# Empty toolbar to avoid weird ranges on image when showing the
2900
2913
# other toolbars --> placeholder
2901
2914
placeHolderToolbar = widgets.ToolBar("Place holder", self)
@@ -3396,15 +3409,20 @@ def gui_createActions(self):
3396
3409
self.addDelRoiAction.roiType = 'rect'
3397
3410
self.addDelRoiAction.setIcon(QIcon(":addDelRoi.svg"))
3398
3411
3399
-
3400
3412
self.addDelPolyLineRoiButton = QToolButton(self)
3401
3413
self.addDelPolyLineRoiButton.setCheckable(True)
3402
3414
self.addDelPolyLineRoiButton.setIcon(QIcon(":addDelPolyLineRoi.svg"))
3403
3415
3404
3416
self.checkableButtons.append(self.addDelPolyLineRoiButton)
3405
3417
self.LeftClickButtons.append(self.addDelPolyLineRoiButton)
3418
+
3419
+ self.drawClearRegionButton = QToolButton(self)
3420
+ self.drawClearRegionButton.setCheckable(True)
3421
+ self.drawClearRegionButton.setIcon(QIcon(":clear_freehand_region.svg"))
3422
+
3423
+ self.checkableButtons.append(self.drawClearRegionButton)
3424
+ self.LeftClickButtons.append(self.drawClearRegionButton)
3406
3425
3407
-
3408
3426
self.delBorderObjAction = QAction(self)
3409
3427
self.delBorderObjAction.setIcon(QIcon(":delBorderObj.svg"))
3410
3428
@@ -3743,6 +3761,7 @@ def gui_connectEditActions(self):
3743
3761
self.curvToolButton.toggled.connect(self.curvTool_cb)
3744
3762
self.wandToolButton.toggled.connect(self.wand_cb)
3745
3763
self.labelRoiButton.toggled.connect(self.labelRoi_cb)
3764
+ self.drawClearRegionButton.toggled.connect(self.drawClearRegion_cb)
3746
3765
self.reInitCcaAction.triggered.connect(self.reInitCca)
3747
3766
self.moveLabelToolButton.toggled.connect(self.moveLabelButtonToggled)
3748
3767
self.editCcaToolAction.triggered.connect(
@@ -6040,7 +6059,7 @@ def gui_mouseDragEventImg1(self, event):
6040
6059
mode = str(self.modeComboBox.currentText())
6041
6060
if mode == 'Viewer':
6042
6061
return
6043
-
6062
+
6044
6063
posData = self.data[self.pos_i]
6045
6064
Y, X = self.get_2Dlab(posData.lab).shape
6046
6065
xdata, ydata = int(x), int(y)
@@ -6189,6 +6208,10 @@ def gui_mouseDragEventImg1(self, event):
6189
6208
self.labelRoiItem.setSize((w, h))
6190
6209
elif self.labelRoiIsFreeHandRadioButton.isChecked():
6191
6210
self.freeRoiItem.addPoint(xdata, ydata)
6211
+
6212
+ # Draw freehand clear region --> draw region
6213
+ elif self.isMouseDragImg1 and self.drawClearRegionButton.isChecked():
6214
+ self.freeRoiItem.addPoint(xdata, ydata)
6192
6215
6193
6216
# @exec_time
6194
6217
def fillHolesID(self, ID, sender='brush'):
@@ -7454,6 +7477,10 @@ def gui_mouseReleaseEventImg1(self, event):
7454
7477
7455
7478
self.clickedOnBud = False
7456
7479
self.BudMothTempLine.setData([], [])
7480
+
7481
+ elif self.isMouseDragImg1 and self.drawClearRegionButton.isChecked():
7482
+ self.freeRoiItem.closeCurve()
7483
+ self.clearObjsFreehandRegion()
7457
7484
7458
7485
def gui_clickedDelRoi(self, event, left_click, right_click):
7459
7486
posData = self.data[self.pos_i]
@@ -7587,7 +7614,7 @@ def gui_mousePressEventImg1(self, event):
7587
7614
)
7588
7615
findNextMotherButtonON = self.findNextMotherButton.isChecked()
7589
7616
unknownLineageButtonON = self.unknownLineageButton.isChecked()
7590
-
7617
+ drawClearRegionON = self.drawClearRegionButton.isChecked()
7591
7618
7592
7619
# Check if right-click on segment of polyline roi to add segment
7593
7620
segments = self.gui_getHoveredSegmentsPolyLineRoi()
@@ -7613,7 +7640,7 @@ def gui_mousePressEventImg1(self, event):
7613
7640
and not curvToolON and not eraserON and not rulerON
7614
7641
and not wandON and not polyLineRoiON and not labelRoiON
7615
7642
and not middle_click and not keepObjON and not separateON
7616
- and not manualBackgroundON
7643
+ and not manualBackgroundON and not drawClearRegionON
7617
7644
and addPointsByClickingButton is None
7618
7645
)
7619
7646
if isPanImageClick:
@@ -7680,69 +7707,77 @@ def gui_mousePressEventImg1(self, event):
7680
7707
and not brushON and not dragImgLeft and not eraserON
7681
7708
and not polyLineRoiON and not labelRoiON
7682
7709
and addPointsByClickingButton is None
7683
- and not manualBackgroundON
7710
+ and not manualBackgroundON and not canDrawClearRegion
7684
7711
)
7685
7712
canBrush = (
7686
7713
brushON and not curvToolON and not rulerON
7687
7714
and not dragImgLeft and not eraserON and not wandON
7688
7715
and not labelRoiON and not manualBackgroundON
7689
- and addPointsByClickingButton is None
7716
+ and addPointsByClickingButton is None and not canDrawClearRegion
7690
7717
)
7691
7718
canErase = (
7692
7719
eraserON and not curvToolON and not rulerON
7693
7720
and not dragImgLeft and not brushON and not wandON
7694
7721
and not polyLineRoiON and not labelRoiON
7695
7722
and addPointsByClickingButton is None
7696
- and not manualBackgroundON
7723
+ and not manualBackgroundON and not canDrawClearRegion
7697
7724
)
7698
7725
canRuler = (
7699
7726
rulerON and not curvToolON and not brushON
7700
7727
and not dragImgLeft and not brushON and not wandON
7701
7728
and not polyLineRoiON and not labelRoiON
7702
7729
and addPointsByClickingButton is None
7703
- and not manualBackgroundON
7730
+ and not manualBackgroundON and not canDrawClearRegion
7704
7731
)
7705
7732
canWand = (
7706
7733
wandON and not curvToolON and not brushON
7707
7734
and not dragImgLeft and not brushON and not rulerON
7708
7735
and not polyLineRoiON and not labelRoiON
7709
7736
and addPointsByClickingButton is None
7710
- and not manualBackgroundON
7737
+ and not manualBackgroundON and not canDrawClearRegion
7711
7738
)
7712
7739
canPolyLine = (
7713
7740
polyLineRoiON and not wandON and not curvToolON and not brushON
7714
7741
and not dragImgLeft and not brushON and not rulerON
7715
7742
and not labelRoiON and not manualBackgroundON
7716
7743
and addPointsByClickingButton is None
7744
+ and not canDrawClearRegion
7717
7745
)
7718
7746
canLabelRoi = (
7719
7747
labelRoiON and not wandON and not curvToolON and not brushON
7720
7748
and not dragImgLeft and not brushON and not rulerON
7721
7749
and not polyLineRoiON and not keepObjON
7722
7750
and addPointsByClickingButton is None
7723
- and not manualBackgroundON
7751
+ and not manualBackgroundON and not canDrawClearRegion
7724
7752
)
7725
7753
canKeep = (
7726
7754
keepObjON and not wandON and not curvToolON and not brushON
7727
7755
and not dragImgLeft and not brushON and not rulerON
7728
7756
and not polyLineRoiON and not labelRoiON
7729
7757
and addPointsByClickingButton is None
7730
- and not manualBackgroundON
7758
+ and not manualBackgroundON and not canDrawClearRegion
7731
7759
)
7732
7760
canAddPoint = (
7733
7761
self.togglePointsLayerAction.isChecked()
7734
7762
and addPointsByClickingButton is not None and not wandON
7735
7763
and not curvToolON and not brushON
7736
7764
and not dragImgLeft and not brushON and not rulerON
7737
7765
and not polyLineRoiON and not labelRoiON and not keepObjON
7738
- and not manualBackgroundON
7766
+ and not manualBackgroundON and not canDrawClearRegion
7739
7767
)
7740
7768
canAddManualBackgroundObj = (
7741
7769
manualBackgroundON and not wandON and not curvToolON and not brushON
7742
7770
and not dragImgLeft and not brushON and not rulerON
7743
7771
and not polyLineRoiON and not labelRoiON
7744
7772
and addPointsByClickingButton is None
7745
- and not keepObjON
7773
+ and not keepObjON and not canDrawClearRegion
7774
+ )
7775
+ canDrawClearRegion = (
7776
+ drawClearRegionON and not wandON and not curvToolON and not brushON
7777
+ and not dragImgLeft and not brushON and not rulerON
7778
+ and not labelRoiON and not manualBackgroundON
7779
+ and addPointsByClickingButton is None
7780
+ and not polyLineRoiON
7746
7781
)
7747
7782
7748
7783
# Enable dragging of the image window or the scalebar
@@ -7913,6 +7948,13 @@ def gui_mousePressEventImg1(self, event):
7913
7948
self.addClickedPoint(action, x, y, id)
7914
7949
self.drawPointsLayers(computePointsLayers=False)
7915
7950
7951
+ elif left_click and canDrawClearRegion:
7952
+ x, y = event.pos().x(), event.pos().y()
7953
+ xdata, ydata = int(x), int(y)
7954
+ self.freeRoiItem.addPoint(xdata, ydata)
7955
+
7956
+ self.isMouseDragImg1 = True
7957
+
7916
7958
elif left_click and canRuler or canPolyLine:
7917
7959
x, y = event.pos().x(), event.pos().y()
7918
7960
xdata, ydata = int(x), int(y)
@@ -12638,6 +12680,7 @@ def connectLeftClickButtons(self):
12638
12680
self.eraserButton.toggled.connect(self.Eraser_cb)
12639
12681
self.wandToolButton.toggled.connect(self.wand_cb)
12640
12682
self.labelRoiButton.toggled.connect(self.labelRoi_cb)
12683
+ self.drawClearRegionButton.toggled.connect(self.drawClearRegion_cb)
12641
12684
self.expandLabelToolButton.toggled.connect(self.expandLabelCallback)
12642
12685
self.addDelPolyLineRoiButton.toggled.connect(self.addDelPolyLineRoi_cb)
12643
12686
self.manualBackgroundButton.toggled.connect(self.manualBackground_cb)
@@ -12872,6 +12915,26 @@ def labelRoiTrangeCheckboxToggled(self, checked):
12872
12915
self.labelRoiStartFrameNoSpinbox.setValue(posData.frame_i+1)
12873
12916
self.labelRoiStopFrameNoSpinbox.setValue(posData.SizeT)
12874
12917
12918
+ def drawClearRegion_cb(self, checked):
12919
+ posData = self.data[self.pos_i]
12920
+ if checked:
12921
+ self.disconnectLeftClickButtons()
12922
+ self.uncheckLeftClickButtons(self.drawClearRegionButton)
12923
+ self.connectLeftClickButtons()
12924
+
12925
+ self.drawClearRegionToolbar.setVisible(checked)
12926
+
12927
+ if not self.isSegm3D:
12928
+ self.drawClearRegionToolbar.setZslicesControlEnabled(False)
12929
+ return
12930
+
12931
+ if not checked:
12932
+ return
12933
+
12934
+ self.drawClearRegionToolbar.setZslicesControlEnabled(
12935
+ True, SizeZ=posData.SizeZ
12936
+ )
12937
+
12875
12938
def labelRoi_cb(self, checked):
12876
12939
posData = self.data[self.pos_i]
12877
12940
if checked:
@@ -12936,6 +12999,54 @@ def labelRoi_cb(self, checked):
12936
12999
self.ax1.removeItem(self.labelRoiItem)
12937
13000
self.updateLabelRoiCircularCursor(None, None, False)
12938
13001
13002
+ def clearObjsFreehandRegion(self):
13003
+ self.logger.info('Clearing objects inside freehand region...')
13004
+
13005
+ # Store undo state before modifying stuff
13006
+ self.storeUndoRedoStates(False, storeImage=False, storeOnlyZoom=True)
13007
+
13008
+ posData = self.data[self.pos_i]
13009
+ zRange = None
13010
+ if self.isSegm3D:
13011
+ z_slice = self.z_lab()
13012
+ zRange = self.drawClearRegionToolbar.zRange(z_slice, posData.SizeZ)
13013
+
13014
+ regionSlice = self.freeRoiItem.slice(zRange=zRange)
13015
+ mask = self.freeRoiItem.mask()
13016
+
13017
+ regionLab = posData.lab[..., *regionSlice].copy()
13018
+ regionLab[..., ~mask] = 0
13019
+
13020
+ clearBorders = (
13021
+ self.drawClearRegionToolbar
13022
+ .clearOnlyEnclosedObjsRadioButton
13023
+ .isChecked()
13024
+ )
13025
+ if clearBorders:
13026
+ regionLab = skimage.segmentation.clear_border(regionLab)
13027
+
13028
+ regionRp = skimage.measure.regionprops(regionLab)
13029
+ clearIDs = [obj.label for obj in regionRp]
13030
+
13031
+ if not clearIDs:
13032
+ if clearBorders:
13033
+ self.logger.warning(
13034
+ 'None of the objects in the freehand region are '
13035
+ 'fully enclosed'
13036
+ )
13037
+ else:
13038
+ self.logger.warning(
13039
+ 'None of the objects are touching the freehand region'
13040
+ )
13041
+ return
13042
+
13043
+ self.deleteIDmiddleClick(clearIDs, False, False)
13044
+ self.update_cca_df_deletedIDs(posData, clearIDs)
13045
+
13046
+ self.freeRoiItem.clear()
13047
+
13048
+ self.updateAllImages()
13049
+
12939
13050
def labelRoiWorkerFinished(self):
12940
13051
self.logger.info('Magic labeller closed.')
12941
13052
worker = self.labelRoiActiveWorkers.pop(-1)
0 commit comments