Summary
itk::TransformFileReaderTemplate<T> (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<TParametersValueType>::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<TParametersValueType>::ReadParameters() (itkHDF5TransformIO.cxx:159-201), the numeric conversion is an explicit per-element static_cast<ParametersValueType>(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<float> and TransformFileReaderTemplate<double> 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
- 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.
- 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: SimpleITK's Transform wraps itk::TransformBaseTemplate<double> 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.
Summary
itk::TransformFileReaderTemplate<T>(and every concreteTransformIOBasebackend: HDF5, Legacy/Txt, Matlab) silently coerces a stored transform's parameter precision to whateverTthe 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
DisplacementFieldTransformstored 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 thedoublereader) has any chance to notice or opt out.Where this happens
All three read backends funnel through the same choke point,
TransformIOBaseTemplate<TParametersValueType>::CorrectTransformPrecisionType():Modules/IO/TransformHDF5/src/itkHDF5TransformIO.cxx:317Modules/IO/TransformInsightLegacy/src/itkTxtTransformIO.cxx:171Modules/IO/TransformMatlab/src/itkMatlabTransformIO.cxx:103Each reads the on-disk type-name string (e.g.
"DisplacementFieldTransform_float_3_3") into a local variable, then callsSuperclass::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) beforeCreateTransform()instantiates the transform. The original, as-stored name is discarded — there is no accessor that exposes it afterward.Concretely, in
HDF5TransformIOTemplate<TParametersValueType>::ReadParameters()(itkHDF5TransformIO.cxx:159-201), the numeric conversion is an explicit per-elementstatic_cast<ParametersValueType>(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<float>andTransformFileReaderTemplate<double>are fully instantiated and available, but neither one's success/failure is diagnostic of the file's real precision:CorrectTransformPrecisionTypeunconditionally 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
ImageIOBase::ReadImageInformation()for images — without reading the full parameter array. Ideally exposed uniformly across backends viaTransformIOBaseTemplate, since format-specific type-name parsing is already centralized inCorrectTransformPrecisionType.TransformIOBaseTemplatebeforeCorrectTransformPrecisionTypemutates 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 asTransformFileReader::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: SimpleITK's
Transformwrapsitk::TransformBaseTemplate<double>exclusively, sositk.ReadTransform()always uses the double-precision reader. For a float-precisionDisplacementFieldTransformstored on disk, this means every read silently doubles the field's memory footprint with no diagnostic — worse than the already-reported cost ofsitk.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 explicitsitk.Cast(field, sitk.sitkVectorFloat64)+DisplacementFieldTransformconstruction 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.