Skip to content

Commit ba56b57

Browse files
Update tomo sample data and example analysis pipeline
1 parent fb1fcd0 commit ba56b57

File tree

2 files changed

+77
-17
lines changed

2 files changed

+77
-17
lines changed

examples/analysis_pipeline.py

Lines changed: 75 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
import napari
22
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
38

49
from synapse_net.file_utils import read_mrc
510
from synapse_net.sample_data import get_sample_data
11+
from synapse_net.distance_measurements import measure_segmentation_to_object_distances
612
from 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+
3343
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.
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

91151
if __name__ == "__main__":

synapse_net/sample_data.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ def get_sample_data(name: str) -> str:
1515
"""
1616
registry = {
1717
"tem_2d.mrc": "3c6f9ff6d7673d9bf2fd46c09750c3c7dbb8fa1aa59dcdb3363b65cc774dcf28",
18-
"tem_tomo.mrc": "eb790f83efb4c967c96961239ae52578d95da902fc32307629f76a26c3dc61fa",
18+
"tem_tomo.mrc": "fe862ce7c22000d4440e3aa717ca9920b42260f691e5b2ab64cd61c928693c99",
1919
}
2020
urls = {
2121
"tem_2d.mrc": "https://owncloud.gwdg.de/index.php/s/5sAQ0U4puAspcHg/download",
22-
"tem_tomo.mrc": "https://owncloud.gwdg.de/index.php/s/TmLjDCXi42E49Ef/download",
22+
"tem_tomo.mrc": "https://owncloud.gwdg.de/index.php/s/FJDhDfbT4UxhtOn/download",
2323
}
2424
key = f"{name}.mrc"
2525

0 commit comments

Comments
 (0)