Repository navigation
Refactor(io): NRRD and MINC images hold a record and a lazy raw array; saves over a read file keep its data (#415, pass 3c) - #427
Open
balbasty wants to merge 20 commits into
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
…#415) NrrdReaderWriter is removed. The header becomes the NrrdRaw record, which keeps the bytes it was read from, and NrrdMetadata reads and writes it without the samples. NrrdImage holds `raw`, a lazy array that reads the samples by name each time and presents them in image order, and `metadata`. The writer encodes over the record: the storage follows `raw`, the geometry of the record is kept while the preferred transformation is the decoded one, and an untouched image is written byte for byte, both files of a detached image included. Saves are written to a temporary file and moved over the target, and an image written over the files that its lazy array reads is pointed at the new files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
…feat/415-nrrd-minc Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
…415) MincReader is removed. The header becomes the MincRaw record (version, dimensions and stored type), read without the voxels: brainhops reads the NetCDF header of MINC1 itself, so that a gzipped or cut file can be read, and h5py reads the groups and attributes of MINC2. MincMetadata reads that record. MincImage holds `raw`, a lazy array that reads the scaled voxels with nibabel by name, or from the bytes of a stream, and `metadata`; `data` reverses the axes of `raw`. The `dimensions`, `vox2world` and `version` attributes move to `metadata.raw`. MINC stays read only, and the conformance checks of writing make sure it refuses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
…= on unoriented worlds (#415) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
…pe; MRtrix drops stale caches (#415) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
…TI, MGH, AFNI) (#415, #426) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
…spaces and MINC signtype (#415, #426) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
… readers refuse unknown options (#415, #426) Writers of NRRD, MRtrix, AFNI, MGH and NIfTI write each local file beside its target and move it over the target (`replace_file` and `write_file` in `io/base/_utils_files.py`, with `is_local` moved there from the NIfTI files). An image written over a file that its lazy array reads is pointed at the new file and drops its cached data, and the writers read a lazy array directly rather than through `data`. This fixes `.nii` files saved over themselves as zeros (#426). The readers of the migrated images and their metadata refuse unknown options with TypeError. NRRD refuses `space=` for a world without an anatomical orientation, reports unknown types as WriterError and keeps the spelling of the type of its source. MINC1 integer images without a signtype are refused when the header is read, and NUL padding is stripped from NetCDF text attributes. Fixes #426 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
…re_data_argument (#415) The data of NrrdImage and MincImage is a lazyproperty whose setter drops its own cache, the metadata invalidates the transformations by a single name, and the constructors store data= with store_data_argument. The writers that point raw at the file they replaced, for NIfTI images and transformations, MGH, MRtrix and NRRD, drop the cached views with drop_cache. Each pair of views moves beside its image format, under io/images, while the lazy arrays that they read stay in io/common. The helper that checks the NIfTI reader options moves to the bottom of its module, and the public methods that this change set touches document their parameters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
…ncoders (#415) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pass 3c of #415. It is stacked on #425 (pass 3b, AFNI and MRtrix) → #424 → #423 → #422 → #421 → #419 → #418 → #417. Design: #416 (
docs/design/format-records.md).NRRD (
.nrrd/.nhdr) and MINC (MINC1 and MINC2) images now hold a record of their header and a lazy array, instead of inheritingNrrdReaderWriterandMincReader. This PR also fixes #426 for every image format, where a save over the file that an image reads destroyed its data.What changes
io.common.nrrd):NrrdRaw, formerlyNrrdHeader, holds the header fields and the text they were read from.NrrdMetadata(MetadataFormat)is new and registered.io.common.minc):MincRawholds the dimensions, the voxel-to-world matrix and the version.MincMetadata(MetadataFormat)is new and registered._netcdf.py. It reads the header only, so the metadata loads without touching the voxels, from gzipped or cut files too.NrrdImageandMincImageholdrawandmetadata. They follow the same rules as the earlier images:datais a cached view ofraw, read-only with NumPy and lazy with Dask.shapecomes fromraw.systemis read-only.datareads the voxels.io/base/_utils_files.py:is_local,same_file,replace_fileandwrite_file. These replace the copies in the NIfTI and NRRD modules.Saves over the file an image reads (#426)
rawat the new file and drops its cacheddata, so it stays readable and can be saved again.io.load("x.nii").save("x.nii")wrote zeros, and.mgh/.mgzwere left empty. Now every format keeps its data. The newtests/test_io_lazy_saves.pycovers this under NumPy and Dask, with new storage options too.Fixes #426.
Write precedence of
NrrdImage.to_rawshortforint16.space=on a world without an anatomical orientation raisesWriterError.keyvalueis merged last. Invalid keys raiseWriterError.Visible behaviour changes
NrrdReaderWriter,NrrdHeader,MincReader, and the.header,._headerand.dataobjattributes. Also removed are MINC's.dimensions,.vox2worldand.version, which now live onimage.metadata.raw.NrrdRaw.from_fileobjreturns the record instead of a tuple.TypeErrorfor every migrated image: NIfTI, MGH, AFNI, MRtrix, NRRD and MINC, and their metadata. Before, they were silently ignored.minandmaxare dropped.signtypeis refused when read, as nibabel does.Tests
.nrrd,.nhdr) and MINC (MINC1, gzipped MINC1, MINC2) are conformance exemplars.Exemplar.sourcehook to build its test files.renamedfield lets check 5 compare detached files saved under the same name.Review
Fable reviewed this pass and required four fixes, all done in 281647e:
datastayed cached afterrawwas re-pointed, in NRRD and also in MRtrix and AFNI.space=was silently ignored on a world without an anatomical orientation.signtypewere accepted.The review also found #426. Its nits are fixed, and the tensions are logged in #416.
Checks
/tmp, Python 3.8): 6114 passed, 96 skipped, 1 xpassed.ruff check,ruff format --checkand codespell are clean.Part of #415.
🤖 Generated with Claude Code
https://claude.ai/code/session_01M3KmPTJ2CihFMCFaJ4twx8
Generated by Claude Code