diff --git a/.github/workflows/EVENT_merge_to_master.yml b/.github/workflows/EVENT_merge_to_master.yml index 6f14c2eb5..4841f8fbd 100644 --- a/.github/workflows/EVENT_merge_to_master.yml +++ b/.github/workflows/EVENT_merge_to_master.yml @@ -49,6 +49,4 @@ jobs: repo: context.repo.repo, sha: context.sha, state: 'success' - }) - - + }) \ No newline at end of file diff --git a/darwin/exporter/formats/nifti.py b/darwin/exporter/formats/nifti.py index 65576952a..e4db64e46 100644 --- a/darwin/exporter/formats/nifti.py +++ b/darwin/exporter/formats/nifti.py @@ -72,10 +72,17 @@ def export(annotation_files: Iterable[dt.AnnotationFile], output_dir: Path) -> N for ann in video_annotation.annotations if ann.annotation_class.annotation_type == "polygon" ] + # Check if there are any rasters in the annotation, these are created with a _m suffix + # in addition to those created from polygons. + annotation_types = [ + a.annotation_class.annotation_type for a in video_annotation.annotations + ] + mask_present = "raster_layer" in annotation_types and "mask" in annotation_types output_volumes = build_output_volumes( video_annotation, class_names_to_export=polygon_class_names, from_raster_layer=False, + mask_present=mask_present, ) slot_map = {slot.name: slot for slot in video_annotation.slots} polygon_annotations = [ @@ -90,13 +97,8 @@ def export(annotation_files: Iterable[dt.AnnotationFile], output_dir: Path) -> N write_output_volume_to_disk( output_volumes, image_id=image_id, output_dir=output_dir ) - # Check if there are any rasters in the annotation, these are created with a _m suffix - # in addition to those created from polygons. - annotation_types = [ - a.annotation_class.annotation_type for a in video_annotation.annotations - ] # Need to map raster layers to SeriesInstanceUIDs - if "raster_layer" in annotation_types and "mask" in annotation_types: + if mask_present: mask_id_to_classname = { ann.id: ann.annotation_class.name for ann in video_annotation.annotations @@ -130,6 +132,7 @@ def build_output_volumes( video_annotation: dt.AnnotationFile, from_raster_layer: bool = False, class_names_to_export: List[str] = None, + mask_present: Optional[bool] = False, ) -> Dict: """ This is a function to create the output volumes based on the whole annotation file @@ -142,6 +145,8 @@ def build_output_volumes( Whether the output volumes are being built from raster layers or not class_names_to_export : List[str] The list of class names to export + mask_present: bool + If mask annotations are present in the annotation Returns ------- output_volumes: Dict @@ -160,6 +165,10 @@ def build_output_volumes( ) # Builds output volumes per class volume_dims, pixdims, affine, original_affine = process_metadata(slot.metadata) + if not mask_present and not class_names_to_export: + class_names_to_export = [ + "" + ] # If there are no annotations to export, we still need to create an empty volume output_volumes[series_instance_uid] = { class_name: Volume( pixel_array=np.zeros(volume_dims), diff --git a/darwin/importer/formats/nifti.py b/darwin/importer/formats/nifti.py index 54821c48a..476b9a6c0 100644 --- a/darwin/importer/formats/nifti.py +++ b/darwin/importer/formats/nifti.py @@ -16,8 +16,8 @@ from scipy.ndimage import zoom except ImportError: import_fail_string = """ - You must install darwin-py with pip install nibabel connected-components-3d - in order to import with using nifti format + You must install darwin-py with pip install darwin-py\[medical] + in order to import using nifti format """ console.print(import_fail_string) sys.exit(1) diff --git a/tests/darwin/exporter/formats/export_nifti_test.py b/tests/darwin/exporter/formats/export_nifti_test.py index 648e1ed72..0bfd6d167 100644 --- a/tests/darwin/exporter/formats/export_nifti_test.py +++ b/tests/darwin/exporter/formats/export_nifti_test.py @@ -102,7 +102,7 @@ def test_export_calls_populate_output_volumes_from_polygons( / team_slug_darwin_json_v2 / "nifti/releases/latest/annotations" ) - video_annotation_filepaths = [annotations_dir / "polygon_no_mask.json"] + video_annotation_filepaths = [annotations_dir / "polygon_only.json"] video_annotations = list( darwin_to_dt_gen(video_annotation_filepaths, False) ) @@ -124,9 +124,48 @@ def test_export_calls_populate_output_volumes_from_raster_layer( / team_slug_darwin_json_v2 / "nifti/releases/latest/annotations" ) - video_annotation_filepaths = [annotations_dir / "mask_no_polygon.json"] + video_annotation_filepaths = [annotations_dir / "mask_only.json"] video_annotations = list( darwin_to_dt_gen(video_annotation_filepaths, False) ) nifti.export(video_annotations, output_dir=Path(tmpdir)) mock.assert_called() + + +def test_export_creates_file_for_polygons_and_masks( + team_slug_darwin_json_v2: str, +): + with tempfile.TemporaryDirectory() as tmpdir: + with ZipFile("tests/data.zip") as zfile: + zfile.extractall(tmpdir) + annotations_dir = ( + Path(tmpdir) + / team_slug_darwin_json_v2 + / "nifti/releases/latest/annotations" + ) + video_annotation_files = { + "mask_only.json": ["hippocampus_multislot_3_test_hippo_LOIN_m.nii.gz"], + "polygon_only.json": [ + "hippocampus_multislot_3_test_hippo_create_class_1.nii.gz" + ], + "polygon_and_mask.json": [ + "hippocampus_multislot_3_test_hippo_create_class_1.nii.gz", + "hippocampus_multislot_3_test_hippo_LOIN_m.nii.gz", + ], + "empty.json": ["hippocampus_multislot_3_test_hippo_.nii.gz"], + } + for video_annotation_file in video_annotation_files: + video_annotation_filepaths = [annotations_dir / video_annotation_file] + video_annotations = list( + darwin_to_dt_gen(video_annotation_filepaths, False) + ) + nifti.export(video_annotations, output_dir=Path(tmpdir)) + for output_file in video_annotation_files[video_annotation_file]: + assert ( + Path(tmpdir) / output_file + ).exists(), ( + f"Expected file {output_file} does not exist in {tmpdir}" + ) + # Empty the directory for the next test + for output_file in video_annotation_files[video_annotation_file]: + (Path(tmpdir) / output_file).unlink() diff --git a/tests/data.zip b/tests/data.zip index dedd46487..3d36760e7 100644 Binary files a/tests/data.zip and b/tests/data.zip differ