Skip to content

Commit ea4bb5d

Browse files
Add volume analysis
1 parent 433b913 commit ea4bb5d

File tree

3 files changed

+126
-3
lines changed

3 files changed

+126
-3
lines changed

flamingo_tools/measurements.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def _get_bounding_box_and_center(table, seg_id, resolution, shape, dilation):
4343
if dilation is not None and dilation > 0:
4444
bb_extension = dilation + 1
4545
else:
46-
bb_extension = 1
46+
bb_extension = 2
4747

4848
bb_min = np.array([
4949
row.bb_min_z.item(), row.bb_min_y.item(), row.bb_min_x.item()
@@ -166,6 +166,21 @@ def _default_object_features(
166166
return measures
167167

168168

169+
def _morphology_features(seg_id, table, image, segmentation, resolution, **kwargs):
170+
measures = {"label_id": seg_id}
171+
172+
bb, center = _get_bounding_box_and_center(table, seg_id, resolution, image.shape, dilation=0)
173+
mask = segmentation[bb] == seg_id
174+
175+
# Hard-coded value for LaVision cochleae. This is a hack for the wrong voxel size in MoBIE.
176+
# resolution = (3.0, 0.76, 0.76)
177+
178+
volume, surface = _measure_volume_and_surface(mask, resolution)
179+
measures["volume"] = volume
180+
measures["surface"] = surface
181+
return measures
182+
183+
169184
def _regionprops_features(seg_id, table, image, segmentation, resolution, background_mask=None, dilation=None):
170185
bb, _ = _get_bounding_box_and_center(table, seg_id, resolution, image.shape, dilation)
171186

@@ -219,6 +234,7 @@ def get_object_measures_from_table(arr_seg, table, keyword="median"):
219234
"skimage": _regionprops_features,
220235
"default_background_norm": partial(_default_object_features, background_radius=75, norm=np.divide),
221236
"default_background_subtract": partial(_default_object_features, background_radius=75, norm=np.subtract),
237+
"morphology": _morphology_features,
222238
}
223239
"""The different feature functions that are supported in `compute_object_measures` and
224240
that can be selected via the feature_set argument. Currently this supports:

scripts/measurements/density_analysis.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,7 @@ def check_implementation():
7373

7474
def compare_gerbil_cochleae():
7575
# We need the tonotopic mapping for G_EK_000233_L
76-
# cochleae = ["G_EK_000233_L", "G_EK_000049_L", "G_EK_000049_R"]
77-
cochleae = ["G_EK_000049_L", "G_EK_000049_R"]
76+
cochleae = ["G_EK_000233_L", "G_EK_000049_L", "G_EK_000049_R"]
7877

7978
plt.figure(figsize=(6, 3))
8079
for cochlea in cochleae:
@@ -88,6 +87,10 @@ def compare_gerbil_cochleae():
8887
plt.show()
8988

9089

90+
# TODO: implement the same for mouse cochleae (healthy vs. opto treatment)
91+
# also show this in tonotopic mapping
92+
93+
9194
def main():
9295
# check_implementation()
9396
compare_gerbil_cochleae()
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import os
2+
3+
import matplotlib.pyplot as plt
4+
import numpy as np
5+
import pandas as pd
6+
7+
from flamingo_tools.s3_utils import get_s3_path
8+
from flamingo_tools.measurements import compute_object_measures
9+
10+
11+
def compute_sgn_volume(cochlea, output_path, voxel_spacing, seg_name="SGN_v2"):
12+
img_path = f"{cochlea}/images/ome-zarr/PV.ome.zarr"
13+
seg_path = f"{cochlea}/images/ome-zarr/{seg_name}.ome.zarr"
14+
15+
img_path, _ = get_s3_path(img_path)
16+
seg_path, _ = get_s3_path(seg_path)
17+
18+
segmentation_table_path = f"{cochlea}/tables/{seg_name}/default.tsv"
19+
feature_set = "morphology"
20+
compute_object_measures(
21+
image_path=img_path,
22+
segmentation_path=seg_path,
23+
segmentation_table_path=segmentation_table_path,
24+
output_table_path=output_path,
25+
resolution=voxel_spacing,
26+
feature_set=feature_set,
27+
component_list=[1],
28+
s3_flag=True,
29+
image_key="s0",
30+
segmentation_key="s0",
31+
)
32+
33+
34+
def compute_volumes_flamingo():
35+
output_folder = "./data/volumes_flamingo"
36+
os.makedirs(output_folder, exist_ok=True)
37+
cochleae = ["M_LR_000226_L", "M_LR_000226_R", "M_LR_000227_L", "M_LR_000227_R"]
38+
voxel_spacing = (0.38, 0.38, 0.38)
39+
for cochlea in cochleae:
40+
output_path = os.path.join(output_folder, f"{cochlea}.tsv")
41+
if os.path.exists(output_path):
42+
continue
43+
compute_sgn_volume(cochlea, output_path, voxel_spacing)
44+
45+
46+
def compute_volumes_lavision():
47+
output_folder = "./data/volumes_lavision"
48+
os.makedirs(output_folder, exist_ok=True)
49+
cochleae = ["LaVision-M02", "LaVision-M03"]
50+
# Note: we used a wrong voxel spacing in MoBIE (1.9 micron in-plane instead of 0.76)
51+
# We solved this here in a hacky fashion by hard-coding the resolution for the volume
52+
# calculation temporariliy in the measurement function.
53+
voxel_spacing = (3.0, 1.887779, 1.887779)
54+
# voxel_spacing = (3.0, 0.76, 0.76)
55+
for cochlea in cochleae:
56+
output_path = os.path.join(output_folder, f"{cochlea}.tsv")
57+
if os.path.exists(output_path):
58+
continue
59+
compute_sgn_volume(cochlea, output_path, voxel_spacing, seg_name="SGN_LOWRES-v5c")
60+
61+
62+
def compare_volumes():
63+
cochleae_flamingo = ["M_LR_000226_L", "M_LR_000226_R", "M_LR_000227_L", "M_LR_000227_R"]
64+
cochleae_lavision = ["LaVision-M02", "LaVision-M03"]
65+
66+
folder_flamingo = "./data/volumes_flamingo"
67+
folder_lavision = "./data/volumes_lavision"
68+
69+
data_flamingo = []
70+
for cochlea in cochleae_flamingo:
71+
x = pd.read_csv(os.path.join(folder_flamingo, f"{cochlea}.tsv"), sep="\t")
72+
volumes = x["volume"].values
73+
volumes = volumes[~np.isnan(volumes)]
74+
data_flamingo.append(volumes)
75+
76+
data_lavision = []
77+
for cochlea in cochleae_lavision:
78+
x = pd.read_csv(os.path.join(folder_lavision, f"{cochlea}.tsv"), sep="\t")
79+
volumes = x["volume"].values
80+
volumes = volumes[~np.isnan(volumes)]
81+
data_lavision.append(volumes)
82+
83+
fig, axes = plt.subplots(2, sharey=True)
84+
85+
ax = axes[0]
86+
ax.boxplot(data_flamingo, tick_labels=cochleae_flamingo)
87+
ax.set_ylabel("SGN Volume [µm^3]")
88+
89+
ax = axes[1]
90+
ax.boxplot(data_lavision, tick_labels=cochleae_lavision)
91+
ax.set_ylabel("SGN Volume [µm^3]")
92+
93+
plt.show()
94+
95+
96+
def main():
97+
compute_volumes_flamingo()
98+
compute_volumes_lavision()
99+
100+
compare_volumes()
101+
102+
103+
if __name__ == "__main__":
104+
main()

0 commit comments

Comments
 (0)