Skip to content

Added a Nifti data loader for issue #270 #361

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

Merged
merged 1 commit into from
Nov 1, 2022
Merged

Conversation

vikashg
Copy link
Collaborator

@vikashg vikashg commented Oct 3, 2022

Signed-off-by: gupta [email protected]

@sonarqubecloud
Copy link

sonarqubecloud bot commented Oct 3, 2022

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 1 Code Smell

No Coverage information No Coverage information
0.0% 0.0% Duplication

image_reader.SetFileName(str(nii_path))
image = image_reader.Execute()
image_np = np.transpose(SimpleITK.GetArrayFromImage(image), [2, 1, 0])
return image_np
Copy link
Collaborator

Choose a reason for hiding this comment

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

So this will only return the ndarray, how would the metadata be preserved? Metadata, e.g. spacing and affine are important and needed for the inference transforms.

In App SDK there is a Image domain class, very primitive with only ndarray and a list of metadata in a dict. We used this object to preserve image ndarray as well as a list of required metadata from the DICOM Series to vol image. The built-in inference operators are expecting Image type as input.

Also, with NIfTI image only, unless the original slices are converted to a DICOM series, the DICOM Seg Writer operator can not be used, simply because the Seg needs to refer back to the original DICOM instances.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I wrote it before metatensor became a norm in MONAI. I wanted an operator which just reads an images and hands off the numpy array to the next operator for inference. I do not think we can or need to worry about DICOM Seg operator if someone is doing inference on Nifti operators. It is not expected. I will look into this class and ensure that we can output metadata from the operator. I will try to tie in this operator with an example to make a complete use case.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Unless the input images in NIfTI have already been resampled to the spacing/orientation to fit the model, resampling and orientation are needed in the pre-processing step, granted in many cases and in competitions the training and validation images have been pre-prepared.
The MONAI built-in readers simply returns the ndarray and a metadata dict containing certain entries common/required by the transforms. The LoadImage class then convert both into a MetaTensor object.


def compute(self, op_input: InputContext, op_output: OutputContext, context: ExecutionContext):
input_path = op_input.get().path
image_np = self.convert_and_save(input_path)
Copy link
Collaborator

Choose a reason for hiding this comment

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

The op_input path is typically a folder, even though file path is also possible depending on how the input is set up. It is important that the path itself is checked for what type it is.

Also, in the case of folder, there has to logic to scan the file(s) for supported types, e.g. nii, nii.gz, mhd, etc. The monai core has the LoadImage class that has a set of supported readers, including nii, and wonder why not simple use it in the application, or make use of it here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes monai core has the logic for LoadImage. But, it is also very specific in terms of how it wants to handle images. Now it is using metadata, there is this idea of channel first etc. I wanted a very raw example, which someone can look and manipulate more easily if needed. We will talk about it today.
Thanks @MMelQin

Copy link
Member

Choose a reason for hiding this comment

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

LoadImage shouldn't manipulate the image data at all and leaves that to subsequent transforms. It should preserve the Nifti header in the metadata so you can use it later especially for saving output images back to the same format as the input. It doesn't however take as input single file names so to scan a directory we typically use glob to get a list of files.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@MMelQin I understand that the op_input path is typically a folder. But that is a valid point for dicom images. The nifti images are just files. In a typical scenario, all the nifti images will be contained in a single folder so it makes sense to use op_input path for a file instead of a folder.

@ericspod and @MMelQin
Yes LoadImage can be used for loading the images. However, in the monai environment the channels are considered first by default. and there might be use cases where that is not the case. In addition, I thought it will be nice to have an example where you can show that monai deploy operators do not necessarily depend on the monai core components and it is possible to play around with other readers and demonstrate the flexibility it offers.
But if you think, it is not particularly useful please go ahead with the changes.

Copy link
Member

Choose a reason for hiding this comment

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

Channels are added first because that's what Pytorch wants when considering what the dimensions of a tensor represent. What we often do is use Orientation/Orientationd transforms to change the orientation of the image based on what the Nifti header says so that the spatial dimensions always represent the same thing, like here. If that's the kind of situation in which you wouldn't want to add a channel dimension then I'd say the use of these transforms together is the correct solution.

I'm obviously biased but I'd always recommend using MONAI code rather than other libraries for doing the same thing.

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.

4 participants