Skip to content

Commit

Permalink
[DAR-2504][External] Generate empty NifTI volumes when no polygons ar…
Browse files Browse the repository at this point in the history
…e present (#863)

* Update EVENT_merge_to_master.yml with V3 > V7 github-script changes

* temp test

* temp test

* Undo temp changes

* Improved NifTI import dependency failure error message

* Undid GHA workflow changes

* ga .

* WIP

* Correct error message

* Corrected test failure output message

* Output empty file if no annotations & unit tests
  • Loading branch information
JBWilkie committed Jun 14, 2024
1 parent 714a041 commit 0ea03b4
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 13 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/EVENT_merge_to_master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,4 @@ jobs:
repo: context.repo.repo,
sha: context.sha,
state: 'success'
})
})
21 changes: 15 additions & 6 deletions darwin/exporter/formats/nifti.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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),
Expand Down
4 changes: 2 additions & 2 deletions darwin/importer/formats/nifti.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
43 changes: 41 additions & 2 deletions tests/darwin/exporter/formats/export_nifti_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)
Expand All @@ -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()
Binary file modified tests/data.zip
Binary file not shown.

0 comments on commit 0ea03b4

Please sign in to comment.