Skip to content

Commit 9cb8763

Browse files
Implement segmentation post-processing widget
1 parent b75b968 commit 9cb8763

File tree

3 files changed

+96
-1
lines changed

3 files changed

+96
-1
lines changed

synapse_net/napari.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ contributions:
2222
- id: synapse_net.vesicle_pooling
2323
python_name: synapse_net.tools.vesicle_pool_widget:VesiclePoolWidget
2424
title: Pool Assignment
25+
- id: synapse_net.postprocessing
26+
python_name: synapse_net.tools.postprocessing_widget:PostprocessingWidget
27+
title: Segmentation Postprocessing
2528

2629
# Commands for sample data.
2730
- id: synapse_net.sample_data_tem_2d
@@ -48,6 +51,8 @@ contributions:
4851
display_name: Morphology Analysis
4952
- command: synapse_net.vesicle_pooling
5053
display_name: Pool Assignment
54+
- command: synapse_net.postprocessing
55+
display_name: Segmentation Postprocessing
5156

5257
sample_data:
5358
- command: synapse_net.sample_data_tem_2d
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import napari
2+
import napari.layers
3+
import napari.viewer
4+
5+
import numpy as np
6+
7+
from napari.utils.notifications import show_info
8+
from qtpy.QtWidgets import QVBoxLayout, QPushButton
9+
from skimage.measure import regionprops
10+
from skimage.segmentation import find_boundaries
11+
12+
from .base_widget import BaseWidget
13+
14+
15+
class PostprocessingWidget(BaseWidget):
16+
def __init__(self):
17+
super().__init__()
18+
19+
self.viewer = napari.current_viewer()
20+
layout = QVBoxLayout()
21+
22+
# Create the dropdown to select the segmentation to post-process.
23+
self.segmentation_selector_name = "Segmentation"
24+
self.segmentation_selector_widget = self._create_layer_selector(
25+
self.segmentation_selector_name, layer_type="Labels"
26+
)
27+
layout.addWidget(self.segmentation_selector_widget)
28+
29+
# Create dropdown to select the mask for filtering / intersection.
30+
self.mask_selector_name = "Mask"
31+
self.mask_selector_widget = self._create_layer_selector(self.mask_selector_name, layer_type="Labels")
32+
layout.addWidget(self.mask_selector_widget)
33+
34+
# Create input for label id in the mask.
35+
self.mask_id_param, _ = self._add_int_param(
36+
"mask_id", 0, min_val=0, max_val=1000, layout=layout, title="Mask ID"
37+
)
38+
39+
# Create text field to choose the name of the output layer.
40+
self.output_layer_param, _ = self._add_string_param("output_layer", "", title="Output Layer", layout=layout)
41+
42+
# First postprocessing option: Filter with mask.
43+
self.button1 = QPushButton("Filter")
44+
self.button1.clicked.connect(self.on_filter)
45+
layout.addWidget(self.button1)
46+
47+
# Second postprocessing option: intersect with boundary of the mask.
48+
self.button2 = QPushButton("Intersect with Boundary")
49+
self.button2.clicked.connect(self.on_intersect)
50+
layout.addWidget(self.button2)
51+
52+
# Add the widgets to the layout.
53+
self.setLayout(layout)
54+
55+
def _write_pp(self, segmentation):
56+
layer_name = self.output_layer_param.text()
57+
if layer_name in self.viewer.layers:
58+
self.viewer.layers[layer_name].data = segmentation
59+
else:
60+
self.viewer.add_labels(segmentation, name=layer_name)
61+
62+
def _conditions_met(self):
63+
if self.output_layer_param.text() == "":
64+
show_info("Please choose an output layer.")
65+
return False
66+
return True
67+
68+
def _get_segmentation_and_mask(self):
69+
segmentation = self._get_layer_selector_data(self.segmentation_selector_name).copy()
70+
mask = self._get_layer_selector_data(self.mask_selector_name)
71+
mask_id = self.mask_id_param.value()
72+
if mask_id != 0:
73+
mask = (mask == mask_id).astype(mask.dtype)
74+
return segmentation, mask
75+
76+
def on_filter(self):
77+
if not self._conditions_met():
78+
return
79+
segmentation, mask = self._get_segmentation_and_mask()
80+
props = regionprops(segmentation, mask)
81+
filter_ids = [prop.label for prop in props if prop.max_intensity == 0]
82+
segmentation[np.isin(segmentation, filter_ids)] = 0
83+
self._write_pp(segmentation)
84+
85+
def on_intersect(self):
86+
if not self._conditions_met():
87+
return
88+
segmentation, mask = self._get_segmentation_and_mask()
89+
boundary = find_boundaries(mask)
90+
segmentation = np.logical_and(segmentation > 0, boundary).astype(segmentation.dtype)
91+
self._write_pp(segmentation)

synapse_net/tools/vesicle_pool_widget.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
COLORMAP = ["red", "blue", "yellow", "cyan", "purple", "magenta", "orange", "green"]
1616

1717

18-
# TODO Make selection of the distance layers optional and add a second distance layer.
1918
class VesiclePoolWidget(BaseWidget):
2019
def __init__(self):
2120
super().__init__()

0 commit comments

Comments
 (0)