nnUNet
nnUNet copied to clipboard
Clarify that the default `.nii.gz` reader (`SimpleITKIO`) results in an axis swap when loading numpy arrays
Hi there! I come from a lab that primarily uses nibabel
over SimpleITK
for image processing.
When using nnUNetV2, we did the following:
- Training:
nnunetv2_train
on.nii.gz
images (no setting ofoverwrite_image_reader_writer
in thedataset.json
file)-
SimpleITKIO
was implicitly used as the image reader/writer by default
-
- Inference: Load images in our Python script using
nibabel
, then pass data array topredict_single_npy_array
When following this approach, we learned of a quirk in SimpleITK's behavior:
SimpleITK and numpy indexing access is in opposite order!
SimpleITK: image[x,y,z] numpy: image_numpy_array[z,y,x]
Because of this, the x and z axes get swapped within nnUNet
at this call:
https://github.com/MIC-DKFZ/nnUNet/blob/997804c7510634dc8fd83f1194b434c60815a93e/nnunetv2/imageio/simpleitk_reader_writer.py#L41
As a result, when loading a data array using nibabel
(for inference purposes), the array (and the spacings!) must be transposed ([2, 1, 0]
) to match the format of the training data. In fact, we had no idea that the training data had been transposed by SimpleITK in the first place! (In essence, even though our training data was in LPI orientation, the model was effectively trained using IPL orientation due to the axis swap!)
It might be good to emphasize in the relevant documentation (1, 2) ~the priority difference for .nii.gz
image readers (SimpleITK > nibabel), as well as the data loading differences between the SimpleITKIO
loader and the NibabelIO
loader~. (EDIT: I now realize that NibabelIO
will result in the same axis-swapping, making the choice of reader not as important as I thought.)
The axis-swapping behavior is largely invisible to the user (due to the use of npy
arrays being internal to nnUNet)... until they try using predict_single_npy_array
and get an empty prediction, at which point the training has already been completed. (Quite the "gotcha!")