|
| 1 | +import os |
| 2 | +from glob import glob |
| 3 | + |
| 4 | +import pandas as pd |
| 5 | +from flamingo_tools.validation import create_consensus_annotations |
| 6 | + |
| 7 | +IMAGE_ROOT = "/mnt/vast-nhr/projects/nim00007/data/moser/cochlea-lightsheet/AnnotatedImageCrops/F1ValidationSGNs/for_consensus_annotation" # noqa |
| 8 | +ROOT = "/mnt/vast-nhr/projects/nim00007/data/moser/cochlea-lightsheet/AnnotatedImageCrops/F1ValidationSGNs/final_annotations" # noqa |
| 9 | + |
| 10 | +ANNOTATION_FOLDERS = ["AnnotationsAMD", "AnnotationsEK", "AnnotationsLR"] |
| 11 | +COLOR = ["blue", "yellow", "orange"] |
| 12 | +CONSENSUS_ANNOTATIONS = "consensus_annotations" |
| 13 | +OUTPUT_FOLDER = os.path.join(ROOT, "final_consensus_annotations") |
| 14 | + |
| 15 | + |
| 16 | +def match_annotations(image_path, annotation_folders): |
| 17 | + annotations = {} |
| 18 | + prefix = os.path.basename(image_path).split("_")[:3] |
| 19 | + prefix = "_".join(prefix) |
| 20 | + |
| 21 | + annotations = {} |
| 22 | + for annotation_folder in annotation_folders: |
| 23 | + all_annotations = glob(os.path.join(ROOT, annotation_folder, "*.csv")) |
| 24 | + matches = [ |
| 25 | + ann for ann in all_annotations if (os.path.basename(ann).startswith(prefix) and "negative" not in ann) |
| 26 | + ] |
| 27 | + if len(matches) != 1: |
| 28 | + breakpoint() |
| 29 | + assert len(matches) == 1 |
| 30 | + annotation_path = matches[0] |
| 31 | + annotations[annotation_folder] = annotation_path |
| 32 | + |
| 33 | + assert len(annotations) == len(annotation_folders) |
| 34 | + return annotations |
| 35 | + |
| 36 | + |
| 37 | +def create_consensus_step3(image_path, check): |
| 38 | + print("Compute consensus annotations for", image_path) |
| 39 | + annotation_paths = match_annotations(image_path, ANNOTATION_FOLDERS) |
| 40 | + matching_distance = 8 |
| 41 | + consensus_annotations, unmatched_annotations = create_consensus_annotations( |
| 42 | + annotation_paths, matching_distance=matching_distance, min_matches_for_consensus=2, |
| 43 | + ) |
| 44 | + fname = os.path.basename(image_path) |
| 45 | + |
| 46 | + prev_consensus = match_annotations(image_path, [CONSENSUS_ANNOTATIONS])[CONSENSUS_ANNOTATIONS] |
| 47 | + prev_consensus = pd.read_csv(prev_consensus)[["axis-0", "axis-1", "axis-2"]] |
| 48 | + |
| 49 | + if check: |
| 50 | + import napari |
| 51 | + import tifffile |
| 52 | + |
| 53 | + consensus_annotations = consensus_annotations[["axis-0", "axis-1", "axis-2"]].values |
| 54 | + unmatched_annotators = unmatched_annotations.annotator.values |
| 55 | + unmatched_annotations = unmatched_annotations[["axis-0", "axis-1", "axis-2"]].values |
| 56 | + |
| 57 | + image = tifffile.imread(image_path) |
| 58 | + v = napari.Viewer() |
| 59 | + v.add_image(image) |
| 60 | + if prev_consensus is not None: |
| 61 | + v.add_points(prev_consensus.values, face_color="gray", name="previous-consensus-annotations") |
| 62 | + v.add_points(consensus_annotations, face_color="green") |
| 63 | + v.add_points( |
| 64 | + unmatched_annotations, |
| 65 | + properties={"annotator": unmatched_annotators}, |
| 66 | + face_color="annotator", |
| 67 | + face_color_cycle=COLOR, # TODO reorder |
| 68 | + ) |
| 69 | + v.title = os.path.basename(fname) |
| 70 | + napari.run() |
| 71 | + |
| 72 | + else: |
| 73 | + # Combine consensus and previous annotations. |
| 74 | + consensus_annotations = consensus_annotations[["axis-0", "axis-1", "axis-2"]] |
| 75 | + n_consensus = len(consensus_annotations) |
| 76 | + n_unmatched = len(unmatched_annotations) |
| 77 | + if prev_consensus is not None: |
| 78 | + n_prev = len(prev_consensus) |
| 79 | + print("Number of previous consensus annotations:", n_prev) |
| 80 | + |
| 81 | + print("Number of new consensus annotations:", n_consensus) |
| 82 | + print("Number of unmatched annotations:", n_unmatched) |
| 83 | + |
| 84 | + consensus_annotations = pd.concat([consensus_annotations, prev_consensus]) |
| 85 | + |
| 86 | + out_name = fname.replace(".tif", ".csv") |
| 87 | + out_path = os.path.join(OUTPUT_FOLDER, out_name) |
| 88 | + |
| 89 | + os.makedirs(OUTPUT_FOLDER, exist_ok=True) |
| 90 | + consensus_annotations.to_csv(out_path, index=False) |
| 91 | + |
| 92 | + |
| 93 | +def main(): |
| 94 | + import argparse |
| 95 | + parser = argparse.ArgumentParser() |
| 96 | + parser.add_argument("--images", nargs="+") |
| 97 | + parser.add_argument("--check", action="store_true") |
| 98 | + args = parser.parse_args() |
| 99 | + |
| 100 | + if args.images is None: |
| 101 | + image_paths = sorted(glob(os.path.join(IMAGE_ROOT, "*.tif"))) |
| 102 | + else: |
| 103 | + image_paths = args.images |
| 104 | + |
| 105 | + for image_path in image_paths: |
| 106 | + create_consensus_step3(image_path, args.check) |
| 107 | + |
| 108 | + |
| 109 | +if __name__ == "__main__": |
| 110 | + main() |
0 commit comments