3
3
4
4
import h5py
5
5
import numpy as np
6
+ import napari
7
+ import pandas as pd
6
8
7
9
from scipy .ndimage import binary_closing
8
10
from skimage .measure import label
9
11
from synaptic_reconstruction .ground_truth .shape_refinement import edge_filter
12
+ from synaptic_reconstruction .morphology import skeletonize_object
13
+ from synaptic_reconstruction .distance_measurements import measure_segmentation_to_object_distances
10
14
from tqdm import tqdm
11
15
12
- ROOT = "/mnt/lustre-emmy-hdd/projects/nim00007/data/synaptic-reconstruction/cooper/20241102_TOMO_DATA_Imig2014/final_Imig2014_seg_autoComp" # noqa
16
+ from compute_skeleton_area import calculate_surface_area
13
17
14
- OUTPUT_AZ = "./boundary_az"
18
+ ROOT = "./imig_data" # noqa
19
+ OUTPUT_AZ = "./az_segmentation"
20
+
21
+ RESOLUTION = (1.554 ,) * 3
15
22
16
23
17
24
def filter_az (path ):
@@ -20,6 +27,7 @@ def filter_az(path):
20
27
ds = os .path .basename (ds )
21
28
out_path = os .path .join (OUTPUT_AZ , ds , fname )
22
29
os .makedirs (os .path .join (OUTPUT_AZ , ds ), exist_ok = True )
30
+
23
31
if os .path .exists (out_path ):
24
32
return
25
33
@@ -56,11 +64,192 @@ def filter_az(path):
56
64
f .create_dataset ("filtered_az" , data = az_filtered , compression = "gzip" )
57
65
58
66
59
- def main ():
67
+ def filter_all_azs ():
60
68
files = sorted (glob (os .path .join (ROOT , "**/*.h5" ), recursive = True ))
61
- for ff in tqdm (files ):
69
+ for ff in tqdm (files , desc = "Filter AZ segmentations." ):
62
70
filter_az (ff )
63
71
64
72
73
+ def process_az (path , view = True ):
74
+ key = "thin_az"
75
+
76
+ with h5py .File (path , "r" ) as f :
77
+ if key in f and not view :
78
+ return
79
+ az_seg = f ["filtered_az" ][:]
80
+
81
+ az_thin = skeletonize_object (az_seg )
82
+
83
+ if view :
84
+ ds , fname = os .path .split (path )
85
+ ds = os .path .basename (ds )
86
+ raw_path = os .path .join (ROOT , ds , fname )
87
+ with h5py .File (raw_path , "r" ) as f :
88
+ raw = f ["raw" ][:]
89
+ v = napari .Viewer ()
90
+ v .add_image (raw )
91
+ v .add_labels (az_seg )
92
+ v .add_labels (az_thin )
93
+ napari .run ()
94
+ else :
95
+ with h5py .File (path , "a" ) as f :
96
+ f .create_dataset (key , data = az_thin , compression = "gzip" )
97
+
98
+
99
+ # Apply thinning to all active zones to obtain 1d surface.
100
+ def process_all_azs ():
101
+ files = sorted (glob (os .path .join (OUTPUT_AZ , "**/*.h5" ), recursive = True ))
102
+ for ff in tqdm (files , desc = "Thin AZ segmentations." ):
103
+ process_az (ff , view = False )
104
+
105
+
106
+ def measure_az_area (path ):
107
+ from skimage import measure
108
+
109
+ with h5py .File (path , "r" ) as f :
110
+ seg = f ["thin_az" ][:]
111
+
112
+ # Try via surface mesh.
113
+ verts , faces , normals , values = measure .marching_cubes (seg , spacing = RESOLUTION )
114
+ surface_area1 = measure .mesh_surface_area (verts , faces )
115
+
116
+ # Try via custom function.
117
+ surface_area2 = calculate_surface_area (seg , voxel_size = RESOLUTION )
118
+
119
+ ds , fname = os .path .split (path )
120
+ ds = os .path .basename (ds )
121
+
122
+ return pd .DataFrame ({
123
+ "Dataset" : [ds ],
124
+ "Tomogram" : [fname ],
125
+ "surface_mesh [nm^2]" : [surface_area1 ],
126
+ "surface_custom [nm^2]" : [surface_area2 ],
127
+ })
128
+
129
+
130
+ # Measure the AZ surface areas.
131
+ def measure_all_areas ():
132
+ save_path = "./results/area_measurements.xlsx"
133
+ if os .path .exists (save_path ):
134
+ return
135
+
136
+ files = sorted (glob (os .path .join (OUTPUT_AZ , "**/*.h5" ), recursive = True ))
137
+ area_table = []
138
+ for ff in tqdm (files , desc = "Measure AZ areas." ):
139
+ area = measure_az_area (ff )
140
+ area_table .append (area )
141
+ area_table = pd .concat (area_table )
142
+ area_table .to_excel (save_path , index = False )
143
+
144
+ manual_results = "/home/pape/Work/my_projects/synaptic-reconstruction/scripts/cooper/debug/surface/manualAZ_surface_area.xlsx" # noqa
145
+ manual_results = pd .read_excel (manual_results )[["Dataset" , "Tomogram" , "manual" ]]
146
+ comparison_table = pd .merge (area_table , manual_results , on = ["Dataset" , "Tomogram" ], how = "inner" )
147
+ comparison_table .to_excel ("./results/area_comparison.xlsx" , index = False )
148
+
149
+
150
+ def analyze_areas ():
151
+ import seaborn as sns
152
+ import matplotlib .pyplot as plt
153
+
154
+ table = pd .read_excel ("./results/area_comparison.xlsx" )
155
+
156
+ fig , axes = plt .subplots (2 )
157
+ sns .scatterplot (data = table , x = "manual" , y = "surface_mesh [nm^2]" , ax = axes [0 ])
158
+ sns .scatterplot (data = table , x = "manual" , y = "surface_custom [nm^2]" , ax = axes [1 ])
159
+ plt .show ()
160
+
161
+
162
+ def measure_distances (ves_path , az_path ):
163
+ with h5py .File (az_path , "r" ) as f :
164
+ az = f ["thin_az" ][:]
165
+
166
+ with h5py .File (ves_path , "r" ) as f :
167
+ vesicles = f ["vesicles/segment_from_combined_vesicles" ][:]
168
+
169
+ distances , _ , _ , _ = measure_segmentation_to_object_distances (vesicles , az , resolution = RESOLUTION )
170
+
171
+ ds , fname = os .path .split (az_path )
172
+ ds = os .path .basename (ds )
173
+
174
+ return pd .DataFrame ({
175
+ "Dataset" : [ds ] * len (distances ),
176
+ "Tomogram" : [fname ] * len (distances ),
177
+ "Distance" : distances ,
178
+ })
179
+
180
+
181
+ # Measure the AZ vesicle distances for all vesicles.
182
+ def measure_all_distances ():
183
+ save_path = "./results/vesicle_az_distances.xlsx"
184
+ if os .path .exists (save_path ):
185
+ return
186
+
187
+ ves_files = sorted (glob (os .path .join (ROOT , "**/*.h5" ), recursive = True ))
188
+ az_files = sorted (glob (os .path .join (OUTPUT_AZ , "**/*.h5" ), recursive = True ))
189
+ assert len (ves_files ) == len (az_files )
190
+
191
+ dist_table = []
192
+ for ves_file , az_file in tqdm (zip (ves_files , az_files ), total = len (az_files ), desc = "Measure distances." ):
193
+ dist = measure_distances (ves_file , az_file )
194
+ dist_table .append (dist )
195
+ dist_table = pd .concat (dist_table )
196
+
197
+ dist_table .to_excel (save_path , index = False )
198
+
199
+
200
+ def reformat_distances ():
201
+ tab = pd .read_excel ("./results/vesicle_az_distances.xlsx" )
202
+
203
+ munc_ko = {}
204
+ munc_ctrl = {}
205
+
206
+ snap_ko = {}
207
+ snap_ctrl = {}
208
+
209
+ for _ , row in tab .iterrows ():
210
+ ds = row .Dataset
211
+ tomo = row .Tomogram
212
+
213
+ if ds == "Munc13DKO" :
214
+ if "CTRL" in tomo :
215
+ group = munc_ctrl
216
+ else :
217
+ group = munc_ko
218
+ else :
219
+ assert ds == "SNAP25"
220
+ if "CTRL" in tomo :
221
+ group = snap_ctrl
222
+ else :
223
+ group = snap_ko
224
+
225
+ name = os .path .splitext (tomo )[0 ]
226
+ val = row ["Distance [nm]" ]
227
+ if name in group :
228
+ group [name ].append (val )
229
+ else :
230
+ group [name ] = [val ]
231
+
232
+ def save_tab (group , path ):
233
+ n_ves_max = max (len (v ) for v in group .values ())
234
+ group = {k : v + [np .nan ] * (n_ves_max - len (v )) for k , v in group .items ()}
235
+ group_tab = pd .DataFrame (group )
236
+ group_tab .to_excel (path , index = False )
237
+
238
+ os .makedirs ("./results/distances_formatted" , exist_ok = True )
239
+ save_tab (munc_ko , "./results/distances_formatted/munc_ko.xlsx" )
240
+ save_tab (munc_ctrl , "./results/distances_formatted/munc_ctrl.xlsx" )
241
+ save_tab (snap_ko , "./results/distances_formatted/snap_ko.xlsx" )
242
+ save_tab (snap_ctrl , "./results/distances_formatted/snap_ctrl.xlsx" )
243
+
244
+
245
+ def main ():
246
+ # filter_all_azs()
247
+ # process_all_azs()
248
+ # measure_all_areas()
249
+ # analyze_areas()
250
+ # measure_all_distances()
251
+ reformat_distances ()
252
+
253
+
65
254
if __name__ == "__main__" :
66
255
main ()
0 commit comments