11import napari
22import 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
38
49from synapse_net .file_utils import read_mrc
510from synapse_net .sample_data import get_sample_data
11+ from synapse_net .distance_measurements import measure_segmentation_to_object_distances
612from synapse_net .inference import compute_scale_from_voxel_size , get_model , run_segmentation
713
814
@@ -30,28 +36,77 @@ def segment_structures(tomogram, voxel_size):
3036 return {"vesicles" : vesicles , "active_zone" : active_zone , "compartments" : compartments }
3137
3238
39+ def n_vesicles (mask , ves ):
40+ return len (np .unique (ves [mask ])) - 1
41+
42+
3343def 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.
3547
48+ vesicles , compartments = segmentations ["vesicles" ], segmentations ["compartments" ]
3649
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" )
3955
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
4060
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
4372
4473
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+
47100 viewer = napari .Viewer ()
48101 viewer .add_image (tomogram )
49102 for name , segmentation in segmentations .items ():
50103 viewer .add_labels (segmentation , name = name )
104+ viewer .add_labels (vesicle_pools )
51105 napari .run ()
52106
53107
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 ):
55110 pass
56111
57112
@@ -64,28 +119,33 @@ def main():
64119 tomogram , voxel_size = read_mrc (mrc_path )
65120
66121 # 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.
68125 import h5py
126+ segmentations = {}
69127 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
72132
73133 # Post-process the segmentations, to find the presynaptic terminal,
74134 # filter out vesicles not in the terminal, and to 'snape' the AZ to the presynaptic boundary.
75135 segmentations = postprocess_segmentation (segmentations )
76136
77137 # Measure the distances between the AZ and vesicles.
78- distances = measure_distances (segmentations )
138+ vesicle_attributes = measure_distances (segmentations , voxel_size )
79139
80140 # 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 )
82142
83143 # Visualize the results.
84- visualize_results (tomogram , segmentations , vesicle_pools )
144+ visualize_results (tomogram , segmentations , vesicle_attributes )
85145
86146 # Compute the vesicle radii and combine and save all measurements.
87147 save_path = "analysis_results.xlsx"
88- save_analysis (segmentations , distances , vesicle_pools , save_path )
148+ save_analysis (segmentations , vesicle_attributes , save_path )
89149
90150
91151if __name__ == "__main__" :
0 commit comments