Skip to content

Commit 664f203

Browse files
Implement size filtering widget
1 parent 10cbfd5 commit 664f203

File tree

2 files changed

+77
-0
lines changed

2 files changed

+77
-0
lines changed

synapse_net/napari.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ contributions:
2525
- id: synapse_net.postprocessing
2626
python_name: synapse_net.tools.postprocessing_widget:PostprocessingWidget
2727
title: Segmentation Postprocessing
28+
- id: synapse_net.size_filter
29+
python_name: synapse_net.tools.size_filter_widget:SizeFilterWidget
30+
title: Size Filter
2831

2932
# Commands for sample data.
3033
- id: synapse_net.sample_data_tem_2d
@@ -59,6 +62,8 @@ contributions:
5962
display_name: Pool Assignment
6063
- command: synapse_net.postprocessing
6164
display_name: Segmentation Postprocessing
65+
- command: synapse_net.size_filter
66+
display_name: Size Filter
6267

6368
sample_data:
6469
- command: synapse_net.sample_data_tem_2d
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import napari
2+
import napari.layers
3+
import napari.viewer
4+
5+
import numpy as np
6+
7+
from qtpy.QtWidgets import QHBoxLayout, QVBoxLayout, QPushButton, QSpinBox, QLabel
8+
from skimage.measure import regionprops, label
9+
10+
from .base_widget import BaseWidget
11+
12+
13+
class SizeFilterWidget(BaseWidget):
14+
def __init__(self):
15+
super().__init__()
16+
17+
self.viewer = napari.current_viewer()
18+
layout = QVBoxLayout()
19+
20+
# Create the dropdown to select the segmentation to filter.
21+
self.segmentation_selector_name = "Segmentation"
22+
self.segmentation_selector_widget = self._create_layer_selector(
23+
self.segmentation_selector_name, layer_type="Labels"
24+
)
25+
layout.addWidget(self.segmentation_selector_widget)
26+
27+
# Add a field for entering the minimal size.
28+
self.size_threshold_widget = QSpinBox()
29+
self.size_threshold_widget.setRange(int(-1e9), int(1e9))
30+
self.size_threshold_widget.setSingleStep(1)
31+
32+
threshold_layout = QHBoxLayout()
33+
threshold_layout.addWidget(QLabel("Minimal Size"))
34+
threshold_layout.addWidget(self.size_threshold_widget)
35+
layout.addLayout(threshold_layout)
36+
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"))
40+
41+
# Add an optional output layer name. If not given the segmentation will be over-written.
42+
self.output_layer_param, _ = self._add_string_param("output_layer", "", title="Output Layer", layout=layout)
43+
44+
self.button = QPushButton("Apply Size Filter")
45+
self.button.clicked.connect(self.on_size_filter)
46+
layout.addWidget(self.button)
47+
48+
# Add the widgets to the layout.
49+
self.setLayout(layout)
50+
51+
def on_size_filter(self):
52+
size_threshold = self.size_threshold_widget.value()
53+
seg_layer = self._get_layer_selector_layer(self.segmentation_selector_name)
54+
segmentation = seg_layer.data.copy()
55+
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)
64+
65+
# Write or overwrite segmentation layer.
66+
layer_name = self.output_layer_param.text()
67+
if layer_name is None or layer_name == "":
68+
seg_layer.data = segmentation
69+
elif layer_name in self.viewer.layers:
70+
self.viewer.layers[layer_name].data = segmentation
71+
else:
72+
self.viewer.add_labels(segmentation, name=layer_name)

0 commit comments

Comments
 (0)