-
Notifications
You must be signed in to change notification settings - Fork 47
[DAR-4924][External] Resolving issues with import & export of NifTI annotations
#979
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,7 +25,6 @@ def _console_theme() -> Theme: | |
| console = Console(theme=_console_theme()) | ||
| try: | ||
| import nibabel as nib | ||
| from nibabel.orientations import io_orientation, ornt_transform | ||
| except ImportError: | ||
| import_fail_string = r""" | ||
| You must install darwin-py with pip install darwin-py\[medical] | ||
|
|
@@ -128,7 +127,11 @@ def export( | |
| polygon_annotations, slot_map, output_volumes, legacy=legacy | ||
| ) | ||
| write_output_volume_to_disk( | ||
| output_volumes, image_id=image_id, output_dir=output_dir, legacy=legacy | ||
| output_volumes, | ||
| image_id=image_id, | ||
| output_dir=output_dir, | ||
| legacy=legacy, | ||
| filename=video_annotation.filename, | ||
| ) | ||
| # Need to map raster layers to SeriesInstanceUIDs | ||
| if mask_present: | ||
|
|
@@ -161,6 +164,7 @@ def export( | |
| image_id=image_id, | ||
| output_dir=output_dir, | ||
| legacy=legacy, | ||
| filename=video_annotation.filename, | ||
| ) | ||
|
|
||
|
|
||
|
|
@@ -456,6 +460,7 @@ def write_output_volume_to_disk( | |
| image_id: str, | ||
| output_dir: Union[str, Path], | ||
| legacy: bool = False, | ||
| filename: str = None, | ||
| ) -> None: | ||
| """Writes the given output volumes to disk. | ||
|
|
||
|
|
@@ -470,6 +475,8 @@ def write_output_volume_to_disk( | |
| legacy : bool, default=False | ||
| If ``True``, the exporter will use the legacy calculation. | ||
| If ``False``, the exporter will use the new calculation by dividing with pixdims. | ||
| filename: str | ||
| The filename of the dataset item | ||
|
|
||
| Returns | ||
| ------- | ||
|
|
@@ -489,18 +496,10 @@ def unnest_dict_to_list(d: Dict) -> List: | |
| volumes = unnest_dict_to_list(output_volumes) | ||
| for volume in volumes: | ||
| img = nib.Nifti1Image( | ||
| dataobj=np.flip(volume.pixel_array, (0, 1, 2)).astype(np.int16), | ||
| dataobj=volume.pixel_array.astype(np.int16), | ||
| affine=volume.affine, | ||
| ) | ||
JBWilkie marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if legacy and volume.original_affine is not None: | ||
| orig_ornt = io_orientation( | ||
| volume.original_affine | ||
| ) # Get orientation of current affine | ||
| img_ornt = io_orientation(volume.affine) # Get orientation of RAS affine | ||
| from_canonical = ornt_transform( | ||
| img_ornt, orig_ornt | ||
| ) # Get transform from RAS to current affine | ||
| img = img.as_reoriented(from_canonical) | ||
| img = _get_reoriented_nifti_image(img, volume, legacy, filename) | ||
| if volume.from_raster_layer: | ||
| output_path = Path(output_dir) / f"{image_id}_{volume.class_name}_m.nii.gz" | ||
| else: | ||
|
|
@@ -510,6 +509,46 @@ def unnest_dict_to_list(d: Dict) -> List: | |
| nib.save(img=img, filename=output_path) | ||
|
|
||
|
|
||
| def _get_reoriented_nifti_image( | ||
| img: nib.Nifti1Image, volume: Dict, legacy: bool, filename: str | ||
| ) -> nib.Nifti1Image: | ||
| """ | ||
| Reorients the given NIfTI image based on the affine of the originally uploaded file. | ||
|
|
||
| Files that were uploaded before the `MED_2D_VIEWER` feature are `legacy`. Non-legacy | ||
| files are uploaded and re-oriented to the `LPI` orientation. Legacy NifTI | ||
| files were treated differently. These files were re-oriented to `LPI`, but their | ||
| affine was stored as `RAS`, which is the opposite orientation. We therefore need to | ||
| flip the axes of these images to ensure alignment. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| img: nib.Nifti1Image | ||
| The NIfTI image to be reoriented | ||
| volume: Dict | ||
| The volume containing the affine and original affine | ||
| legacy: bool | ||
| If ``True``, the exporter will flip all axes of the image if the dataset item | ||
| is not a DICOM | ||
| If ``False``, the exporter will not flip the axes | ||
| filename: str | ||
| The filename of the dataset item | ||
| """ | ||
| if volume.original_affine is not None: | ||
| img_ax_codes = nib.orientations.aff2axcodes(volume.affine) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this is correct for pre MED_2D due to the volume.affine being (or expected to be at least) RAS So this flow will try to go from RAS -> original orientation. But really its meant to go from LPI -> original orientation. I think this is why
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated |
||
| orig_ax_codes = nib.orientations.aff2axcodes(volume.original_affine) | ||
| img_ornt = nib.orientations.axcodes2ornt(img_ax_codes) | ||
| orig_ornt = nib.orientations.axcodes2ornt(orig_ax_codes) | ||
| transform = nib.orientations.ornt_transform(img_ornt, orig_ornt) | ||
| img = img.as_reoriented(transform) | ||
| is_dicom = filename.lower().endswith(".dcm") | ||
| if legacy and not is_dicom: | ||
| img = nib.Nifti1Image( | ||
| np.flip(img.get_fdata(), (0, 1, 2)).astype(np.int16), img.affine | ||
| ) | ||
| return img | ||
|
|
||
|
|
||
| def shift_polygon_coords( | ||
| polygon: List[Dict], pixdim: List[Number], legacy: bool = False | ||
| ) -> List: | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.