Skip to content

Commit 009d6cd

Browse files
Some updates to ChReef analysis
1 parent 78e5c11 commit 009d6cd

File tree

2 files changed

+72
-33
lines changed

2 files changed

+72
-33
lines changed

flamingo_tools/segmentation/chreef_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def find_inbetween_ids(
128128
Args:
129129
arr_negexc: Array with all negatives excluded.
130130
arr_allweak: Array with all weak positives.
131-
roi_sgn: Region of interest of segmentation.
131+
roi_seg: Region of interest of segmentation.
132132
133133
Returns:
134134
A list of the ids that are in between the respective thresholds.

scripts/measurements/evaluate_marker_annotations.py

Lines changed: 71 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import argparse
2+
import json
23
import os
34
from typing import List, Optional
45

@@ -9,6 +10,22 @@
910
from flamingo_tools.segmentation.chreef_utils import localize_median_intensities, find_annotations
1011

1112
MARKER_DIR = "/mnt/vast-nhr/projects/nim00007/data/moser/cochlea-lightsheet/ChReef_PV-GFP/2025-07_PV_GFP_SGN"
13+
# The cochlea for the CHReef analysis.
14+
COCHLEAE = [
15+
"M_LR_000143_L",
16+
"M_LR_000144_L",
17+
"M_LR_000145_L",
18+
"M_LR_000153_L",
19+
"M_LR_000155_L",
20+
"M_LR_000189_L",
21+
"M_LR_000143_R",
22+
"M_LR_000144_R",
23+
"M_LR_000145_R",
24+
"M_LR_000153_R",
25+
"M_LR_000155_R",
26+
"M_LR_000189_L",
27+
"M_LR_000189_R",
28+
]
1229

1330

1431
def get_length_fraction_from_center(table, center_str):
@@ -76,13 +93,40 @@ def apply_nearest_threshold(intensity_dic, table_seg, table_measurement):
7693
return table_seg
7794

7895

96+
def find_thresholds(cochlea_annotations, cochlea, data_seg, table_measurement):
97+
# Find the median intensities by averaging th individual annotations for specific crops
98+
annotation_dics = {}
99+
annotated_centers = []
100+
for annotation_dir in cochlea_annotations:
101+
annotation_dic = localize_median_intensities(annotation_dir, cochlea, data_seg, table_measurement)
102+
annotated_centers.extend(annotation_dic["center_strings"])
103+
annotation_dics[annotation_dir] = annotation_dic
104+
105+
annotated_centers = list(set(annotated_centers))
106+
intensity_dic = {}
107+
# loop over all annotated blocks
108+
for annotated_center in annotated_centers:
109+
intensities = []
110+
# loop over annotated block from single user
111+
for annotator_key in annotation_dics.keys():
112+
if annotated_center not in annotation_dics[annotator_key]["center_strings"]:
113+
continue
114+
else:
115+
intensities.append(annotation_dics[annotator_key][annotated_center]["median_intensity"])
116+
intensity_dic[annotated_center] = {"median_intensity": float(sum(intensities) / len(intensities))}
117+
118+
return intensity_dic
119+
120+
79121
def evaluate_marker_annotation(
80122
cochleae,
81123
output_dir: str,
82124
annotation_dirs: Optional[List[str]] = None,
83125
seg_name: str = "SGN_v2",
84126
marker_name: str = "GFP",
85-
):
127+
threshold_save_dir: Optional[str] = None,
128+
force: bool = False,
129+
) -> None:
86130
"""Evaluate marker annotations of a single or multiple annotators.
87131
Segmentation instances are assigned a positive (1) or negative label (2)
88132
in form of the "marker_label" component of the output segmentation table.
@@ -95,6 +139,8 @@ def evaluate_marker_annotation(
95139
annotation_dirs: List of directories containing marker annotations by annotator(s).
96140
seg_name: Identifier for segmentation.
97141
marker_name: Identifier for marker stain.
142+
threshold_save_dir: Optional directory for saving the thresholds.
143+
force: Whether to overwrite already existing results.
98144
"""
99145
input_key = "s0"
100146

@@ -104,11 +150,17 @@ def evaluate_marker_annotation(
104150
annotation_dirs = [entry.path for entry in os.scandir(marker_dir)
105151
if os.path.isdir(entry) and "Results" in entry.name]
106152

153+
seg_string = "-".join(seg_name.split("_"))
107154
for cochlea in cochleae:
155+
cochlea_str = "-".join(cochlea.split("_"))
156+
out_path = os.path.join(output_dir, f"{cochlea_str}_{marker_name}_{seg_string}.tsv")
157+
if os.path.exists(out_path) and not force:
158+
continue
159+
108160
cochlea_annotations = [a for a in annotation_dirs if len(find_annotations(a, cochlea)["center_strings"]) != 0]
109161
print(f"Evaluating data for cochlea {cochlea} in {cochlea_annotations}.")
110162

111-
# get segmentation data
163+
# Get the segmentation data and table.
112164
input_path = f"{cochlea}/images/ome-zarr/{seg_name}.ome.zarr"
113165
input_path, fs = get_s3_path(input_path)
114166
data_seg = read_image_data(input_path, input_key)
@@ -118,37 +170,24 @@ def evaluate_marker_annotation(
118170
with fs.open(table_path_s3, "r") as f:
119171
table_seg = pd.read_csv(f, sep="\t")
120172

121-
seg_string = "-".join(seg_name.split("_"))
122173
table_measurement_path = f"{cochlea}/tables/{seg_name}/{marker_name}_{seg_string}_object-measures.tsv"
123174
table_path_s3, fs = get_s3_path(table_measurement_path)
124175
with fs.open(table_path_s3, "r") as f:
125176
table_measurement = pd.read_csv(f, sep="\t")
126177

127-
# find median intensities by averaging all individual annotations for specific crops
128-
annotation_dics = {}
129-
annotated_centers = []
130-
for annotation_dir in cochlea_annotations:
131-
132-
annotation_dic = localize_median_intensities(annotation_dir, cochlea, data_seg, table_measurement)
133-
annotated_centers.extend(annotation_dic["center_strings"])
134-
annotation_dics[annotation_dir] = annotation_dic
135-
136-
annotated_centers = list(set(annotated_centers))
137-
intensity_dic = {}
138-
# loop over all annotated blocks
139-
for annotated_center in annotated_centers:
140-
intensities = []
141-
# loop over annotated block from single user
142-
for annotator_key in annotation_dics.keys():
143-
if annotated_center not in annotation_dics[annotator_key]["center_strings"]:
144-
continue
145-
else:
146-
intensities.append(annotation_dics[annotator_key][annotated_center]["median_intensity"])
147-
intensity_dic[annotated_center] = {"median_intensity": float(sum(intensities) / len(intensities))}
178+
# Find the threholds from the annotated blocks and save it if specified.
179+
intensity_dic = find_thresholds(cochlea_annotations, cochlea, data_seg, table_measurement)
180+
if threshold_save_dir is not None:
181+
os.makedirs(threshold_save_dir, exist_ok=True)
182+
threshold_out_path = os.path.join(threshold_save_dir, f"{cochlea_str}_{marker_name}_{seg_string}.json")
183+
with open(threshold_out_path, "w") as f:
184+
json.dump(intensity_dic, f, sort_keys=True, indent=4)
148185

186+
# Apply the threshold to all SGNs.
149187
table_seg = apply_nearest_threshold(intensity_dic, table_seg, table_measurement)
150-
cochlea_str = "-".join(cochlea.split("_"))
151-
out_path = os.path.join(output_dir, f"{cochlea_str}_{marker_name}_{seg_string}.tsv")
188+
189+
# Save the table with positives / negatives for all SGNs.
190+
os.makedirs(output_dir, exist_ok=True)
152191
table_seg.to_csv(out_path, sep="\t", index=False)
153192

154193

@@ -157,18 +196,18 @@ def main():
157196
description="Assign each segmentation instance a marker based on annotation thresholds."
158197
)
159198

160-
parser.add_argument("-c", "--cochlea", type=str, nargs="+", required=True,
161-
help="Cochlea(e) to process.")
199+
parser.add_argument("-c", "--cochlea", type=str, nargs="+", default=COCHLEAE, help="Cochlea(e) to process.")
162200
parser.add_argument("-o", "--output", type=str, required=True, help="Output directory.")
163-
164201
parser.add_argument("-a", "--annotation_dirs", type=str, nargs="+", default=None,
165202
help="Directories containing marker annotations.")
203+
parser.add_argument("--threshold_save_dir", "-t")
204+
parser.add_argument("-f", "--force", action="store_true")
166205

167206
args = parser.parse_args()
168-
169-
evaluate_marker_annotation(args.cochlea, args.output, args.annotation_dirs,)
207+
evaluate_marker_annotation(
208+
args.cochlea, args.output, args.annotation_dirs, threshold_save_dir=args.threshold_save_dir, force=args.force
209+
)
170210

171211

172212
if __name__ == "__main__":
173-
174213
main()

0 commit comments

Comments
 (0)