Skip to content

Commit 69d5021

Browse files
Implement vesicle pool correction WIP
1 parent 03dc031 commit 69d5021

File tree

7 files changed

+319
-130
lines changed

7 files changed

+319
-130
lines changed

scripts/otoferlin/automatic_processing.py

Lines changed: 2 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,17 @@
22

33
import h5py
44
import numpy as np
5-
import pandas as pd
65

76
from skimage.measure import label
87
from skimage.segmentation import relabel_sequential
98

10-
from synapse_net.distance_measurements import measure_segmentation_to_object_distances, load_distances
9+
from synapse_net.distance_measurements import measure_segmentation_to_object_distances
1110
from synapse_net.file_utils import read_mrc
1211
from synapse_net.inference.vesicles import segment_vesicles
1312
from synapse_net.tools.util import get_model, compute_scale_from_voxel_size, _segment_ribbon_AZ
1413
from tqdm import tqdm
1514

16-
from common import STRUCTURE_NAMES, get_all_tomograms, get_seg_path, get_adapted_model
15+
from common import get_all_tomograms, get_seg_path, get_adapted_model
1716

1817
# These are tomograms for which the sophisticated membrane processing fails.
1918
# In this case, we just select the largest boundary piece.
@@ -159,74 +158,6 @@ def postprocess_vesicles(mrc_path, output_path, process_center_crop):
159158
f.create_dataset(key, data=vesicles, compression="gzip")
160159

161160

162-
def measure_distances(mrc_path, seg_path, output_folder):
163-
result_folder = os.path.join(output_folder, "distances")
164-
if os.path.exists(result_folder):
165-
return
166-
167-
# Get the voxel size.
168-
_, voxel_size = read_mrc(mrc_path)
169-
resolution = tuple(voxel_size[ax] for ax in "zyx")
170-
171-
# Load the segmentations.
172-
with h5py.File(seg_path, "r") as f:
173-
g = f["segmentation"]
174-
vesicles = g["vesicles"][:]
175-
structures = {name: g[name][:] for name in STRUCTURE_NAMES}
176-
177-
# Measure all the object distances.
178-
os.makedirs(result_folder, exist_ok=True)
179-
for name, seg in structures.items():
180-
if seg.sum() == 0:
181-
print(name, "was not found, skipping the distance computation.")
182-
continue
183-
print("Compute vesicle distances to", name)
184-
save_path = os.path.join(result_folder, f"{name}.npz")
185-
measure_segmentation_to_object_distances(vesicles, seg, save_path=save_path, resolution=resolution)
186-
187-
188-
def assign_vesicle_pools(output_folder):
189-
assignment_path = os.path.join(output_folder, "vesicle_pools.csv")
190-
if os.path.exists(assignment_path):
191-
return
192-
193-
distance_folder = os.path.join(output_folder, "distances")
194-
distance_paths = {name: os.path.join(distance_folder, f"{name}.npz") for name in STRUCTURE_NAMES}
195-
if not all(os.path.exists(path) for path in distance_paths.values()):
196-
print("Skip vesicle pool assignment, because some distances are missing.")
197-
print("This is probably due to the fact that the corresponding structures were not found.")
198-
return
199-
distances = {name: load_distances(path) for name, path in distance_paths.items()}
200-
201-
# The distance criteria.
202-
rav_ribbon_distance = 80 # nm
203-
mpv_pd_distance = 100 # nm
204-
mpv_mem_distance = 50 # nm
205-
docked_pd_distance = 100 # nm
206-
docked_mem_distance = 2 # nm
207-
208-
rav_distances, seg_ids = distances["ribbon"][0], np.array(distances["ribbon"][-1])
209-
rav_ids = seg_ids[rav_distances < rav_ribbon_distance]
210-
211-
pd_distances, mem_distances = distances["PD"][0], distances["membrane"][0]
212-
assert len(pd_distances) == len(mem_distances) == len(rav_distances)
213-
214-
mpv_ids = seg_ids[np.logical_and(pd_distances < mpv_pd_distance, mem_distances < mpv_mem_distance)]
215-
docked_ids = seg_ids[np.logical_and(pd_distances < docked_pd_distance, mem_distances < docked_mem_distance)]
216-
217-
# Create a dictionary to map vesicle ids to their corresponding pool.
218-
# (RA-V get's over-written by MP-V, which is correct).
219-
pool_assignments = {vid: "RA-V" for vid in rav_ids}
220-
pool_assignments.update({vid: "MP-V" for vid in mpv_ids})
221-
pool_assignments.update({vid: "Docked-V" for vid in docked_ids})
222-
223-
pool_assignments = pd.DataFrame({
224-
"vesicle_id": list(pool_assignments.keys()),
225-
"pool": list(pool_assignments.values()),
226-
})
227-
pool_assignments.to_csv(assignment_path, index=False)
228-
229-
230161
def process_tomogram(mrc_path):
231162
output_path = get_seg_path(mrc_path)
232163
output_folder = os.path.split(output_path)[0]
@@ -238,13 +169,6 @@ def process_tomogram(mrc_path):
238169
process_ribbon_structures(mrc_path, output_path, process_center_crop)
239170
postprocess_vesicles(mrc_path, output_path, process_center_crop)
240171

241-
# We don't need to do the analysis of the auto semgentation, it only
242-
# makes sense to do this after segmentation. I am leaving this here for
243-
# now, to move it to the files for analysis later.
244-
245-
# measure_distances(mrc_path, output_path, output_folder)
246-
# assign_vesicle_pools(output_folder)
247-
248172

249173
def main():
250174
tomograms = get_all_tomograms()

scripts/otoferlin/check_automatic_result.py

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import h5py
44
import napari
55
import numpy as np
6-
import pandas as pd
76

87
from synapse_net.file_utils import read_mrc
98
from skimage.exposure import equalize_adapthist
@@ -12,22 +11,6 @@
1211
from common import get_all_tomograms, get_seg_path, get_colormaps
1312

1413

15-
def _get_vesicle_pools(seg, assignment_path):
16-
assignments = pd.read_csv(assignment_path)
17-
pool_names = pd.unique(assignments.pool).tolist()
18-
pools = np.zeros_like(seg)
19-
20-
pool_colors = get_colormaps()["pools"]
21-
colormap = {}
22-
for pool_id, pool_name in enumerate(pool_names, 1):
23-
pool_vesicle_ids = assignments[assignments.pool == pool_name].vesicle_id
24-
pool_mask = np.isin(seg, pool_vesicle_ids)
25-
pools[pool_mask] = pool_id
26-
colormap[pool_id] = pool_colors[pool_name]
27-
28-
return pools, colormap
29-
30-
3114
def check_automatic_result(mrc_path, version, use_clahe=False, center_crop=True, segmentation_group="segmentation"):
3215
tomogram, _ = read_mrc(mrc_path)
3316
if center_crop:
@@ -53,11 +36,6 @@ def check_automatic_result(mrc_path, version, use_clahe=False, center_crop=True,
5336
segmentations[name] = ds[bb]
5437
colormaps[name] = get_colormaps().get(name, None)
5538

56-
output_folder = os.path.split(seg_path)[0]
57-
assignment_path = os.path.join(output_folder, "vesicle_pools.csv")
58-
if os.path.exists(assignment_path) and "vesicles" in segmentations:
59-
segmentations["pools"], colormaps["pools"] = _get_vesicle_pools(segmentations["vesicles"], assignment_path)
60-
6139
v = napari.Viewer()
6240
v.add_image(tomogram)
6341
for name, seg in segmentations.items():

scripts/otoferlin/common.py

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import os
22
from glob import glob
33

4+
import imageio.v3 as imageio
5+
import h5py
46
from synapse_net.tools.util import load_custom_model
57

68

@@ -62,16 +64,29 @@ def get_colormaps():
6264
"Docked-V": (1, 1, 0),
6365
None: "gray",
6466
}
67+
ribbon_map = {1: "red", None: "gray"}
6568
membrane_map = {1: "purple", None: "gray"}
6669
pd_map = {1: "magenta", None: "gray"}
67-
return {"pools": pool_map, "membrane": membrane_map, "PD": pd_map}
68-
69-
70-
# TODO: sync the ukon folder with the tomograms.
71-
# UKON Path:
72-
# /run/user/1000/gvfs/smb-share:server=wfs-medizin.top.gwdg.de,share=ukon-all$/UKON100/archiv/EM/For Segmentation
73-
def sync_tomograms():
74-
pass
70+
return {"pools": pool_map, "membrane": membrane_map, "PD": pd_map, "ribbon": ribbon_map}
71+
72+
73+
def load_segmentations(seg_path):
74+
# Keep the typo in the name, as these are the hdf5 keys!
75+
seg_names = {"vesicles": "veiscles_postprocessed"}
76+
seg_names.update({name: name for name in STRUCTURE_NAMES})
77+
78+
segmentations = {}
79+
correction_folder = os.path.join(os.path.split(seg_path)[0], "correction")
80+
with h5py.File(seg_path, "r") as f:
81+
g = f["segmentation"]
82+
for out_name, name in seg_names.items():
83+
correction_path = os.path.join(correction_folder, f"{name}.tif")
84+
if os.path.exists(correction_path):
85+
print("Loading corrected", name, "segmentation from", correction_path)
86+
segmentations[out_name] = imageio.imread(correction_path)
87+
else:
88+
segmentations[out_name] = g[f"{name}"][:]
89+
return segmentations
7590

7691

7792
if __name__ == "__main__":

scripts/otoferlin/correct_structure_segmentation.py

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,25 @@
1-
import os
21
from pathlib import Path
32

4-
import imageio.v3 as imageio
5-
import h5py
63
import napari
74

85
from synapse_net.file_utils import read_mrc
9-
from common import get_all_tomograms, get_seg_path
6+
from common import get_all_tomograms, get_seg_path, load_segmentations, get_colormaps
107

118

129
def correct_structure_segmentation(mrc_path):
1310
seg_path = get_seg_path(mrc_path)
1411

1512
data, _ = read_mrc(mrc_path)
16-
correction_folder = os.path.join(os.path.split(seg_path)[0], "correction")
17-
fname = Path(mrc_path).stem
18-
19-
names = ("ribbon", "PD", "membrane", "veiscles_postprocessed")
20-
segmentations = {}
21-
with h5py.File(seg_path, "r") as f:
22-
for name in names:
23-
correction_path = os.path.join(correction_folder, f"{name}.tif")
24-
if os.path.exists(correction_path):
25-
print("Loading segmentation for", name, "from", correction_path)
26-
segmentations[name] = imageio.imread(correction_path)
27-
else:
28-
segmentations[name] = f[f"segmentation/{name}"][:]
29-
color_maps = {
30-
"ribbon": {1: "red", None: "gray"},
31-
"PD": {1: "purple", None: "gray"},
32-
"membrane": {1: "magenta", None: "gray"},
33-
}
13+
segmentations = load_segmentations(seg_path)
14+
color_maps = get_colormaps()
3415

3516
v = napari.Viewer()
3617
v.add_image(data)
3718
for name, seg in segmentations.items():
19+
if name == "vesicles":
20+
name = "veiscles_postprocessed"
3821
v.add_labels(seg, name=name, colormap=color_maps.get(name, None))
22+
fname = Path(mrc_path).stem
3923
v.title = fname
4024
napari.run()
4125

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import os
2+
3+
import imageio.v3 as imageio
4+
import napari
5+
import numpy as np
6+
import pandas as pd
7+
from magicgui import magicgui
8+
9+
from synapse_net.file_utils import read_mrc
10+
from skimage.measure import regionprops
11+
from common import load_segmentations, get_seg_path, get_all_tomograms, get_colormaps, STRUCTURE_NAMES
12+
13+
14+
def _create_pool_layer(seg, assignment_path):
15+
assignments = pd.read_csv(assignment_path)
16+
pool_names = pd.unique(assignments.pool).tolist()
17+
pools = np.zeros_like(seg)
18+
19+
pool_colors = get_colormaps()["pools"]
20+
colormap = {}
21+
for pool_id, pool_name in enumerate(pool_names, 1):
22+
pool_vesicle_ids = assignments[assignments.pool == pool_name].vesicle_id
23+
pool_mask = np.isin(seg, pool_vesicle_ids)
24+
pools[pool_mask] = pool_id
25+
colormap[pool_id] = pool_colors[pool_name]
26+
27+
return pools, colormap
28+
29+
30+
def _update_assignments(vesicles, pool_correction, assignment_path):
31+
old_assignments = pd.read_csv(assignment_path)
32+
props = regionprops(vesicles, pool_correction)
33+
34+
new_assignments = old_assignments.copy()
35+
val_to_pool = {1: "RA-V", 2: "MP-V", 3: "Docked-V", 4: None}
36+
for prop in props:
37+
correction_val = prop.max_intensity
38+
if correction_val == 0:
39+
continue
40+
new_assignments[new_assignments.vesicle_id == prop.label] = val_to_pool[correction_val]
41+
42+
new_assignments.to_csv(assignment_path, index=False)
43+
44+
45+
# TODO: also enable correcting vesicle segmentation???
46+
def correct_vesicle_pools(mrc_path):
47+
seg_path = get_seg_path(mrc_path)
48+
49+
output_folder = os.path.split(seg_path)[0]
50+
assignment_path = os.path.join(output_folder, "vesicle_pools.csv")
51+
52+
data, _ = read_mrc(mrc_path)
53+
segmentations = load_segmentations(seg_path)
54+
vesicles = segmentations["vesicles"]
55+
56+
colormaps = get_colormaps()
57+
pool_colors = colormaps["pools"]
58+
correction_colors = {
59+
1: pool_colors["RA-V"], 2: pool_colors["MP-V"], 3: pool_colors["Docked-V"], 4: "Gray", None: "Gray"
60+
}
61+
62+
vesicle_pools, pool_colors = _create_pool_layer(vesicles, assignment_path)
63+
64+
pool_correction_path = os.path.join(output_folder, "correction", "pool_correction.tif")
65+
if os.path.exists(pool_correction_path):
66+
pool_correction = imageio.imread(pool_correction_path)
67+
else:
68+
pool_correction = np.zeros_like(vesicles)
69+
70+
v = napari.Viewer()
71+
v.add_image(data)
72+
v.add_labels(vesicle_pools, colormap=pool_colors)
73+
v.add_labels(pool_correction, colormap=correction_colors)
74+
v.add_labels(vesicles, visible=False)
75+
for name in STRUCTURE_NAMES:
76+
v.add_labels(segmentations[name], name=name, visible=False, colormap=colormaps[name])
77+
78+
@magicgui(call_button="Update Pools")
79+
def update_pools(viewer: napari.Viewer):
80+
pool_data = viewer.layers["vesicle_pools"].data
81+
vesicles = viewer.layers["vesicles"].data
82+
pool_correction = viewer.layers["pool_correction"].data
83+
_update_assignments(vesicles, pool_correction, assignment_path)
84+
imageio.imwrite(pool_correction_path, pool_correction, compression="zlib")
85+
pool_data, pool_colors = _create_pool_layer(vesicles, assignment_path)
86+
viewer.layers["vesicle_pools"].data = pool_data
87+
88+
v.window.add_dock_widget(update_pools)
89+
90+
napari.run()
91+
92+
93+
def main():
94+
tomograms = get_all_tomograms()
95+
for tomo in tomograms:
96+
correct_vesicle_pools(tomo)
97+
98+
99+
if __name__ == "__main__":
100+
main()

0 commit comments

Comments
 (0)