InsightSoftwareConsortium / InsightSoftwareConsortium/ITK
Cannot detect a transform file's stored parameter precision without fully reading it
- Dominant language
- C++
- Stars
- 1.7k
- Forks
- 748
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 64
Description
## Summary
`itk::TransformFileReaderTemplate` (and every concrete `TransformIOBase` backend: HDF5, Legacy/Txt, Matlab) silently coerces a stored transform's parameter precision to whatever `T` the reader was instantiated with, with no way for a caller to discover the file's *actual* on-disk precision without fully reading it into that precision.
This is a real cost for large transforms. A dense `DisplacementFieldTransform` stored in single precision (float) on disk gets its parameter array upconverted to double *during the read itself*, doubling that array's memory footprint, before a caller (e.g. SimpleITK, which only ever instantiates the `double` reader) has any chance to notice or opt out.
## Where this happens
All three read backends funnel through the same choke point, `TransformIOBaseTemplate::CorrectTransformPrecisionType()`:
- `Modules/IO/TransformHDF5/src/itkHDF5TransformIO.cxx:317`
- `Modules/IO/TransformInsightLegacy/src/itkTxtTransformIO.cxx:171`
- `Modules/IO/TransformMatlab/src/itkMatlabTransformIO.cxx:103`
Each reads the on-disk type-name string (e.g. `"DisplacementFieldTransform_float_3_3"`) into a local variable, then calls `Superclass::CorrectTransformPrecisionType(name)` (`Modules/IO/TransformBase/include/itkTransformIOBase.h:185-215`), which mutates that string in place so it matches the reader's own template parameter (`"float"` -> `"double"` or vice versa) before `CreateTransform()` instantiates the transform. The original, as-stored name is discarded — there is no accessor that exposes it afterward.
Concretely, in `HDF5TransformIOTemplate::ReadParameters()` (`itkHDF5TransformIO.cxx:159-201`), the numeric conversion is an explicit per-element `static_cast(buf[i])` from whatever the file actually stored. There is no way to ask "is this file float or double?" without going through this same path — i.e., without reading (and precision-converting) the entire parameter array.
Both `TransformFileReaderTemplate` and `TransformFileReaderTemplate` are fully instantiated and available, but neither one's success/failure is diagnostic of the file's real precision: `CorrectTransformPrecisionType` unconditionally coerces the name to match whichever template you picked, so reading a double-precision file with the float reader "succeeds" too (silently downcasting), and vice versa.
## Request
1. A cheap way to query a transform file's stored parameter precision (float/double) — analogous to `ImageIOBase::ReadImageInformation()` for images — without reading the full parameter array. Ideally exposed uniformly across backends via `TransformIOBaseTemplate`, since format-specific type-name parsing is already centralized in `CorrectTransformPrecisionType`.
2. Failing that (as a smaller first step), simply preserve the as-stored type name string as a member on `TransformIOBaseTemplate` before `CorrectTransformPrecisionType` mutates it, with a public getter (e.g. `GetReadTransformTypeName()`). This costs nothing extra (the string is already read for every transform read) and would let callers such as `TransformFileReader::GetTransformIO()->GetReadTransformTypeName()` at least detect *after* a read that an implicit precision conversion happened, and warn the user.
## Motivating context
This surfaced while investigating [SimpleITK#2710](https://github.com/SimpleITK/SimpleITK/issues/2710): SimpleITK's `Transform` wraps `itk::TransformBaseTemplate` exclusively, so `sitk.ReadTransform()` always uses the double-precision reader. For a float-precision `DisplacementFieldTransform` stored on disk, this means every read silently doubles the field's memory footprint with no diagnostic — worse than the already-reported cost of `sitk.DisplacementFieldTransform(image)`, which at least throws when handed a float32 image directly rather than converting silently.
Measured example: a 4003 float32 displacement field (768 MiB on disk) read via `sitk.ReadTransform()` added ~3.6 GiB of RSS versus ~1.5 GiB for an explicit `sitk.Cast(field, sitk.sitkVectorFloat64)` + `DisplacementFieldTransform` construction of the same field read as a plain image — the transform-file path is both silent and, in this measurement, more expensive than the already-known workaround.
Contributor guide
Research direction
Start with TransformIOBaseTemplate in Modules/IO/TransformBase/include/itkTransformIOBase.h, then trace CorrectTransformPrecisionType and the ReadParameters paths in itkHDF5TransformIO.cxx, itkTxtTransformIO.cxx, and itkMatlabTransformIO.cxx. Check how TransformFileReader exposes its TransformIO; done means the stored precision can be queried without reading the full parameter array, or the original type name remains available after reading.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- backend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100