Skip to content

SaveImage retains a channel-axis setting between saves and truncates later volumes #9242

Open

Description

@leecoring

The contributor explanation below was written by leecoring in Korean and translated with OpenAI Codex. The technical clarification, reproduction code and diagnostic report were prepared with Codex. A friend originally suggested this bug candidate.

Contributor explanation

When saving multiple images with the same saver, the channel-axis setting from the first image can remain in the saver. A setting that should apply only to that image is then reused for later images, even when those images have a different channel layout, causing incorrect saving in SaveImage.

Previously, the saver kept and applied that modified setting. With the fix, it retains the original configuration and uses a separate copy of the settings for the current image during the actual save.

Technical clarification — assisted by Codex

The verified trigger is saving a channel-less array, using spatial_shape metadata, and then saving a channel-first volume with the same SaveImage instance. The first call sets the instance's data_kwargs["channel_dim"] to None. The later call then interprets its dimensions incorrectly. A fresh instance saves the same channel-first volume correctly.

Reproduction code — prepared with Codex

Run from an unmodified checkout of the official dev commit listed below, with PyTorch, NumPy and NiBabel installed:

import tempfile
from pathlib import Path

import nibabel as nib
import numpy as np

from monai.transforms import SaveImage

volume = np.arange(24, dtype=np.float32).reshape(2, 3, 4)
metadata = {"spatial_shape": volume.shape}

with tempfile.TemporaryDirectory() as directory:
    saver = SaveImage(writer="NibabelWriter", resample=False, print_log=False)
    saver(volume, metadata, filename=Path(directory) / "without_channel")
    saver(volume[None], metadata, filename=Path(directory) / "with_channel")
    saved = nib.load(Path(directory) / "with_channel.nii.gz")
    print(saved.shape)
    np.testing.assert_array_equal(saved.get_fdata(), volume)

Expected and observed behavior — Codex-run local diagnostics

Expected: the second file has shape (2, 3, 4) and preserves all 24 voxel values.

Observed before the fix: the second file has shape (1, 2, 3) and retains only 6 values. The first file is correct. The failure also occurs without metadata on the second call. SaveImaged can reproduce this when a channel-less image and a channel-first label share its saver.

A local fix using a per-call copy of the data options preserves all values. Nine regression cases fail before that fix and pass afterward. The patch and tests are available locally for a follow-up PR.

Environment

  • Official MONAI dev commit: 4e9b19ccb5199df8648e1dcdd3002a19f9103e8a.
  • macOS 15.5, arm64; Python 3.14.7; PyTorch 2.15.0; NumPy 2.5.4; NiBabel 5.4.2.
  • Inputs are small synthetic arrays; no patient data is used.

Public GitHub searches for SaveImage, SaveImaged and "data_kwargs" on 2026-10-30 returned 97 distinct Issue/PR records. No matching open fix was identified within that search scope.

Activity

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

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