1
1
import napari
2
2
import pandas as pd
3
+ import numpy as np
4
+
5
+ from scipy .ndimage import binary_closing
6
+ from skimage .measure import regionprops
7
+ from skimage .segmentation import find_boundaries
3
8
4
9
from synapse_net .file_utils import read_mrc
5
10
from synapse_net .sample_data import get_sample_data
11
+ from synapse_net .distance_measurements import measure_segmentation_to_object_distances
6
12
from synapse_net .inference import compute_scale_from_voxel_size , get_model , run_segmentation
7
13
8
14
@@ -30,28 +36,77 @@ def segment_structures(tomogram, voxel_size):
30
36
return {"vesicles" : vesicles , "active_zone" : active_zone , "compartments" : compartments }
31
37
32
38
39
+ def n_vesicles (mask , ves ):
40
+ return len (np .unique (ves [mask ])) - 1
41
+
42
+
33
43
def postprocess_segmentation (segmentations ):
34
- pass
44
+ # We find the compartment corresponding to the presynaptic terminal
45
+ # by selecting the compartment with most vesicles. We filter out all
46
+ # vesicles that do not overlap with this compartment.
35
47
48
+ vesicles , compartments = segmentations ["vesicles" ], segmentations ["compartments" ]
36
49
37
- def measure_distances (segmentations ):
38
- pass
50
+ # First, we find the compartment with most vesicles.
51
+ props = regionprops (compartments , intensity_image = vesicles , extra_properties = [n_vesicles ])
52
+ compartment_ids = [prop .label for prop in props ]
53
+ vesicle_counts = [prop .n_vesicles for prop in props ]
54
+ compartments = (compartments == compartment_ids [np .argmax (vesicle_counts )]).astype ("uint8" )
39
55
56
+ # Filter all vesicles that are not in the compartment.
57
+ props = regionprops (vesicles , compartments )
58
+ filter_ids = [prop .label for prop in props if prop .max_intensity == 0 ]
59
+ vesicles [np .isin (vesicles , filter_ids )] = 0
40
60
41
- def assign_vesicle_pools (distances ):
42
- pass
61
+ segmentations ["vesicles" ], segmentations ["compartments" ] = vesicles , compartments
62
+
63
+ # We also apply closing to the active zone segmentation to avoid gaps and then
64
+ # intersect it with the boundary of the presynaptic compartment.
65
+ active_zone = segmentations ["active_zone" ]
66
+ active_zone = binary_closing (active_zone , iterations = 4 )
67
+ boundary = find_boundaries (compartments )
68
+ active_zone = np .logical_and (active_zone , boundary ).astype ("uint8" )
69
+ segmentations ["active_zone" ] = active_zone
70
+
71
+ return segmentations
43
72
44
73
45
- def visualize_results (tomogram , segmentations , vesicle_pools ):
46
- # TODO vesicle pool visualization
74
+ def measure_distances (segmentations , voxel_size ):
75
+ vesicles , active_zone = segmentations ["vesicles" ], segmentations ["active_zone" ]
76
+ voxel_size = tuple (voxel_size [ax ] for ax in "zyx" )
77
+ distances , _ , _ , vesicle_ids = measure_segmentation_to_object_distances (
78
+ vesicles , active_zone , resolution = voxel_size
79
+ )
80
+ return pd .DataFrame ({"vesicle_id" : vesicle_ids , "distance" : distances })
81
+
82
+
83
+ def assign_vesicle_pools (vesicle_attributes ):
84
+ docked_vesicle_distance = 2 # nm
85
+ vesicle_attributes ["pool" ] = vesicle_attributes ["distance" ].apply (
86
+ lambda x : "docked" if x < docked_vesicle_distance else "non-attached"
87
+ )
88
+ return vesicle_attributes
89
+
90
+
91
+ def visualize_results (tomogram , segmentations , vesicle_attributes ):
92
+
93
+ # Create a segmentation to visualize the vesicle pools.
94
+ docked_ids = vesicle_attributes [vesicle_attributes .pool == "docked" ].vesicle_id
95
+ non_attached_ids = vesicle_attributes [vesicle_attributes .pool == "non-attached" ].vesicle_id
96
+ vesicles = segmentations ["vesicles" ]
97
+ vesicle_pools = np .isin (vesicles , docked_ids ).astype ("uint8" )
98
+ vesicle_pools [np .isin (vesicles , non_attached_ids )] = 2
99
+
47
100
viewer = napari .Viewer ()
48
101
viewer .add_image (tomogram )
49
102
for name , segmentation in segmentations .items ():
50
103
viewer .add_labels (segmentation , name = name )
104
+ viewer .add_labels (vesicle_pools )
51
105
napari .run ()
52
106
53
107
54
- def save_analysis (segmentations , distances , vesicle_pools , save_path ):
108
+ # TODO compute the vesicle radii and other features and then save the attributes.
109
+ def save_analysis (segmentations , vesicle_attributes , save_path ):
55
110
pass
56
111
57
112
@@ -64,28 +119,33 @@ def main():
64
119
tomogram , voxel_size = read_mrc (mrc_path )
65
120
66
121
# Segment synaptic vesicles, the active zone, and the synaptic compartment.
67
- segmentations = segment_structures (tomogram , voxel_size )
122
+ # segmentations = segment_structures(tomogram, voxel_size)
123
+
124
+ # Load saved segmentations for development.
68
125
import h5py
126
+ segmentations = {}
69
127
with h5py .File ("seg.h5" , "r" ) as f :
70
- for name , seg in segmentations .items ():
71
- f .create_dataset (name , data = seg , compression = "gzip" )
128
+ for name , ds in f .items ():
129
+ # f.create_dataset(name, data=seg, compression="gzip")
130
+ seg = ds [:]
131
+ segmentations [name ] = seg
72
132
73
133
# Post-process the segmentations, to find the presynaptic terminal,
74
134
# filter out vesicles not in the terminal, and to 'snape' the AZ to the presynaptic boundary.
75
135
segmentations = postprocess_segmentation (segmentations )
76
136
77
137
# Measure the distances between the AZ and vesicles.
78
- distances = measure_distances (segmentations )
138
+ vesicle_attributes = measure_distances (segmentations , voxel_size )
79
139
80
140
# Assign the vesicle pools, 'docked' and 'non-attached' vesicles, based on the distances.
81
- vesicle_pools = assign_vesicle_pools (distances )
141
+ vesicle_attributes = assign_vesicle_pools (vesicle_attributes )
82
142
83
143
# Visualize the results.
84
- visualize_results (tomogram , segmentations , vesicle_pools )
144
+ visualize_results (tomogram , segmentations , vesicle_attributes )
85
145
86
146
# Compute the vesicle radii and combine and save all measurements.
87
147
save_path = "analysis_results.xlsx"
88
- save_analysis (segmentations , distances , vesicle_pools , save_path )
148
+ save_analysis (segmentations , vesicle_attributes , save_path )
89
149
90
150
91
151
if __name__ == "__main__" :
0 commit comments