Skip to content

Add nibabel volume conversions and tests#411

Open
mccle wants to merge 3 commits into
v0.28.0devfrom
feature/nibabel_volume_conversion
Open

Add nibabel volume conversions and tests#411
mccle wants to merge 3 commits into
v0.28.0devfrom
feature/nibabel_volume_conversion

Conversation

@mccle

@mccle mccle commented May 19, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

Comment thread src/highdicom/volume.py Outdated
' Casting to float64 is not possible.'
)

nifti = nib.Nifti1Image(

@mccle mccle May 19, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nibabel has several other image classes that I believe could represent a volume, listed here. Not all of them are relevant, but the MINC and MGH formats seem worth consideration. I think at the very least, we should support both Nifti1Image and Nifti2Image, meaning the to_nib method should already be changed to allow the user to specify the class to which they wish to convert. Additionally, several of these classes can accept an array, affine, and dtype exactly as the Nifti1Image class or with extremely minor changes.

However, I do not think all of the formats support the same data types. Are you interested in supporting other formats @CPBridge? If so, would it be best to group different classes into separate to_* methods or have any branching logic be included in a single to_nib method?

@mccle mccle marked this pull request as ready for review June 11, 2026 19:21

@CPBridge CPBridge left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few suggestions

Comment thread pyproject.toml
]
itk = ["itk>=5.4.0"]
sitk = ["SimpleITK>=2.2.1"]
nib = ["nibabel>=4.0.0"]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be clearer to use the full name

Suggested change
nib = ["nibabel>=4.0.0"]
nibabel = ["nibabel>=4.0.0"]

Comment thread src/highdicom/volume.py
Comment on lines +3925 to +3932
image_class: Literal[
'Nifti1Image',
'Nifti2Image',
'MGHImage',
'Minc1Image',
'Minc2Image',
'AnalyzeImage'
] = 'Nifti1Image'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Elsewhere in the library, we use enum.Enums for a defined set of options. I think it would be better to stick to that convention here. Literal is only useful for type checking. It does nothing at runtime.

Comment thread src/highdicom/volume.py
The Volume is converted to one of several 3D Image classes (
``nibabel.Nifti1Image``, ``nibabel.Nifti2Image``, ``nibabel.MGHImage``,
``nibabel.Minc1Image``, ``nibabel.Minc2Image``, ``nibabel.AnalyzeImage``
), defaulting to ``nibabel.Nifti1Image``. If its array'current datatype

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo

Suggested change
), defaulting to ``nibabel.Nifti1Image``. If its array'current datatype
), defaulting to ``nibabel.Nifti1Image``. If its array's current datatype

Comment thread src/highdicom/volume.py
self = self.astype(np.uint8)

dtype = self.dtype
image_class = getattr(nib, image_class)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would guard this with a check on the values, and issue a more helpful error message if an inappropriate value is passed. As noted above, this would probably be better handled with an enum (there are examples of this all over the library, for example the segmentation_type parameter of the Segmentation constructor)

Comment thread src/highdicom/volume.py
Comment on lines +4053 to +4054
else:
dtype_map = {}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will just cause confusion downstream I would think

Comment thread src/highdicom/volume.py
Returns
-------
highdicom.Volume:
Volume constructed from the `itk.Image`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Volume constructed from the `itk.Image`.
Volume constructed from the Nibabel image.

Comment thread src/highdicom/volume.py
@classmethod
def from_nibabel(
cls,
nib_im: 'nibabel.spatialimages.SpatialImage', # noqa: F821

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have generally been verbose with parameter names

Suggested change
nib_im: 'nibabel.spatialimages.SpatialImage', # noqa: F821
nibabel_image: 'nibabel.spatialimages.SpatialImage', # noqa: F821

Comment thread src/highdicom/volume.py
Raises
------
ValueError
When the volume is not 3D (multiple channels are unsupported).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not a volume at this stage

Suggested change
When the volume is not 3D (multiple channels are unsupported).
When the input image is not 3D (multiple channels are unsupported).

Comment thread tests/test_nib.py
Comment on lines +34 to +36
def read_multiframe_ct_volume():
im = imread(get_testdata_file('eCT_Supplemental.dcm'))
return im.get_volume()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's some repetition in some of these functions I think. It might make sense to factor these out into the tests/utils.py file?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants