Skip to content

Commit 6e3db6e

Browse files
Update size filter plugin to remove disconnected components
1 parent 7046235 commit 6e3db6e

File tree

1 file changed

+29
-11
lines changed

1 file changed

+29
-11
lines changed

synapse_net/tools/size_filter_widget.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import napari.viewer
44

55
import numpy as np
6+
import nifty.tools as nt
67

78
from qtpy.QtWidgets import QHBoxLayout, QVBoxLayout, QPushButton, QSpinBox, QLabel
89
from skimage.measure import regionprops, label
@@ -34,9 +35,9 @@ def __init__(self):
3435
threshold_layout.addWidget(self.size_threshold_widget)
3536
layout.addLayout(threshold_layout)
3637

37-
# Add a boolean field for processing binary masks.
38-
self.is_mask = False
39-
layout.addWidget(self._add_boolean_param("is_mask", self.is_mask, title="Binary Mask"))
38+
# Add a boolean field for applying a label operation before size filtering.
39+
self.apply_label = True
40+
layout.addWidget(self._add_boolean_param("apply_label", self.apply_label, title="Remove Disconnected Pieces"))
4041

4142
# Add an optional output layer name. If not given the segmentation will be over-written.
4243
self.output_layer_param, _ = self._add_string_param("output_layer", "", title="Output Layer", layout=layout)
@@ -48,19 +49,36 @@ def __init__(self):
4849
# Add the widgets to the layout.
4950
self.setLayout(layout)
5051

52+
def _filter_mask(self, segmentation, size_threshold):
53+
segmentation = label(segmentation).astype(segmentation.dtype)
54+
props = regionprops(segmentation)
55+
filter_ids = [prop.label for prop in props if prop.area < size_threshold]
56+
segmentation[np.isin(segmentation, filter_ids)] = 0
57+
segmentation = (segmentation > 0).astype(segmentation.dtype)
58+
return segmentation
59+
60+
def _filter_segmentation(self, segmentation, size_threshold, apply_label):
61+
dtype = segmentation.dtype
62+
if apply_label:
63+
original_segmentation = segmentation.copy()
64+
segmentation = label(segmentation)
65+
props = regionprops(segmentation, original_segmentation)
66+
else:
67+
props = regionprops(segmentation)
68+
filter_ids = [prop.label for prop in props if prop.area < size_threshold]
69+
segmentation[np.isin(segmentation, filter_ids)] = 0
70+
if apply_label:
71+
mapping = {prop.label: int(prop.max_intensity) for prop in props if prop.label not in filter_ids}
72+
mapping[0] = 0
73+
segmentation = nt.takeDict(mapping, segmentation)
74+
return segmentation.astype(dtype)
75+
5176
def on_size_filter(self):
5277
size_threshold = self.size_threshold_widget.value()
5378
seg_layer = self._get_layer_selector_layer(self.segmentation_selector_name)
5479
segmentation = seg_layer.data.copy()
5580

56-
if self.is_mask:
57-
segmentation = label(segmentation).astype(segmentation.dtype)
58-
59-
props = regionprops(segmentation)
60-
filter_ids = [prop.label for prop in props if prop.area < size_threshold]
61-
segmentation[np.isin(segmentation, filter_ids)] = 0
62-
if self.is_mask:
63-
segmentation = (segmentation > 0).astype(segmentation.dtype)
81+
segmentation = self._filter_segmentation(segmentation, size_threshold, self.apply_label)
6482

6583
# Write or overwrite segmentation layer.
6684
layer_name = self.output_layer_param.text()

0 commit comments

Comments
 (0)