Skip to content

Saved Image is not equal to the original #2028

@cgrain

Description

@cgrain

Intro
The other day, I tried to speed up the training process of our project by saving training images after the deterministic transformations had been executed. (Resampling in particular). I encountered the following problem:

Expectation
Saved images are equal or almost equal (np.allclose(X, X_hat) == True)

Procedure

  1. Loading images
  2. Performing transformations (It turned out, the culprit is the resampling procedure) and getting X
  3. save image X to file but also keep it in memory. By default, it resamples the image back to the original affine_matrix. So, turn resampling off. It is possible to delete the original affine matrix, but that is not preferable.
  4. load file and store it in X_hat
  5. Check for equality, turns out, it is not.

Concluding thoughts
It seems that the orientation changes during saving, even if resampling is off. Is this by design, or is this a bug? I have tried all kinds of saving images, i.e. NiftiSaver, the transformation, etcetera. idem for the loader, i.e. LoadImage, ImageDataset, etcetera. Probably, they all share the same implementation, so that does not really help.
kind regards,

Code

import numpy as np
import monai


def create_random_img():
    Random = np.random.default_rng()
    image = Random.standard_normal((256, 256, 35))
    original_affine = np.asarray(
        [
            [1.1e-2, -4e-3, 4.3, 3.3e1],
            [-7e-1, -1e-2, 6.5e-2, 2.1e2],
            [-1e-2, 7e1, 2.9e-2, -1.9e2],
            [0, 0, 0, 1],
        ]
    )  # Copied from a dataset (So a valid sample!)
    original_file_name = "Test.nii.gz"
    meta_data = {
        "affine": original_affine,
        "original_affine": original_affine,
        "filename_or_obj": original_file_name,
    }
    return {"img": image, "img_meta_dict": meta_data}


def resample_image(image):
    RESAMPLERD = monai.transforms.SpacingD(
        keys=["img"], pixdim=[1, 1, 12], diagonal=True
    )
    return RESAMPLERD(image)


def save_image(image):
    SAVER = monai.data.NiftiSaver(resample=False)
    SAVER.save(image["img"], meta_data=image["img_meta_dict"])
    new_file_name = "./Test/Test_seg.nii.gz"  # Trust me on this
    return image, new_file_name


def load_image(file_name):
    LOADER = monai.transforms.LoadImage()
    return LOADER(file_name)


def main():
    image = create_random_img()
    resampled_img = resample_image(image)
    X = resampled_img
    X["img"] = np.expand_dims(X["img"], 0)

    _, file_name = save_image(X)
    X_hat = load_image(file_name)

    # This should (imo) not be necessary, but let's give it a go
    image_hat = np.moveaxis(X_hat[0], [0, 2], [2, 0])

    print(np.allclose(X["img"], image_hat))  # Expecting True (It is False)


if __name__ == "__main__":
    main()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions