| ⬜ Q1 |
Deconvolution family |
The same KernelZeroMagnitudeThreshold ε is compared against |H| by Inverse but against the |H|²-carrying denominator by Tikhonov/Wiener — "regularization → 0 equals inverse" fails in the band ε ≤ |H| < √ε. |
| ⬜ Q2 |
DiscreteGaussianDerivativeImageFilter |
Never calls FlipAxes(), unlike DerivativeImageFilter/GradientImageFilter (which do) — odd-order derivatives carry the opposite sign vs. its siblings. Also the inter-axis intermediate is Image<OutputPixelType>, so integer outputs truncate per axis pass (the local comments say "real to real"). |
| ⬜ Q3 |
ThresholdSegmentationLevelSetImageFilter |
The class doc (.h:63-65) states positive = inside — the opposite of the convention its own base class documents (itkSegmentationLevelSetImageFilter.h:82-84: negative inside). |
| ⬜ Q4 |
Segmentation level-set functions |
CurvatureSpeed is overridden to PropagationSpeed only by GeodesicActiveContour and ShapeDetection; Threshold/Laplacian/Canny keep the base-class constant 1 — an undocumented asymmetry across siblings. |
| ⬜ Q5 |
BinaryContourImageFilter |
Non-foreground pixels keep their original values verbatim (.hxx:164-179) — they are not normalized to BackgroundValue, so several distinct non-foreground input values all survive into the output. |
| ⬜ Q6 |
SimpleContourExtractorImageFilter |
InputForegroundValue == InputBackgroundValue marks every foreground pixel as contour (the neighborhood scan includes the center pixel, .hxx:84). |
| ⬜ Q7 |
BinaryReconstructionByErosionImageFilter |
Built on the complement via BinaryNot; BackgroundValue is consumed only as a self-consistent internal sentinel — the parameter has no effect on the output. |
| ⬜ Q8 |
AnisotropicDiffusionImageFilter |
An unstable time step warns and proceeds; the clamp is present but commented out (.hxx:68). |
| ⬜ Q9 |
ThresholdMaximumConnectedComponentsImageFilter |
m_LowerBoundary is written once in the constructor and read only by PrintSelf — dead configuration with no setter. |
| ⬜ Q10 |
MinMaxCurvatureFlowFunction |
SetStencilRadius clamps to ≥ 1 (.hxx:43), making the radius == 0 early-outs (.hxx:217-220, 276-279) unreachable dead code. |
| ⬜ Q11 |
ScalarImageKmeansImageFilter |
Overrides the estimator's defaults in GenerateData (SetMaximumIteration(200), threshold 0.0, .hxx:91-92), invisibly to users; and KdTreeBasedKmeansEstimator's loop tests the limit pre-increment after the refinement pass (.hxx:327-347) — MaximumIteration = n runs n+1 passes while GetCurrentIteration() reports n. |
| ⬜ Q12 |
SLICImageFilter |
Enforce-connectivity relabels an undersized component to the previously-encountered label, whatever it is (.hxx:544-546); there is no final relabel/compaction pass; per-pixel distances accumulate in float. |
| ⬜ Q13 |
BoxMean/BoxSigmaImageFilter |
The window crops at the image border and divides by the cropped count (itkBoxUtilities.h:322-324, 499-501) — border statistics use fewer samples. A ZeroFluxNeumannBoundaryCondition is declared (L215/389) but never used, suggesting replication was once intended. |
| ⬜ Q14 |
BinShrinkImageFilter |
Throws when a shrink factor exceeds the axis size where ShrinkImageFilter silently clamps to 1 (.hxx:289-296 vs itkShrinkImageFilter.hxx:265-267); integer outputs round half-up while float outputs truncate (RoundIfInteger, .h:135-149). |
| ⬜ Q15 |
AntiAliasBinaryImageFilter |
A constant input (even all-maximum) shifts to 0 under the max − (max−min)/2 iso-value and the strict > 0 sign test (.hxx:109; base .hxx:605) — the output is uniformly negative, i.e. "all inside". |
| ⬜ Q16 |
ReinitializeLevelSetImageFilter |
Narrow-band mode pre-fills the whole output with ±NumericTraits<PixelType>::max() and only overwrites band points (.hxx:206-225) — far-field pixels keep ±max rather than a distance-like magnitude. |
| ⬜ Q17 |
FastMarchingUpwindGradientImageFilter |
m_TargetValue is overwritten at every accepted point in NoTargets mode; in Some/AllTargets modes the reached test is sticky, so every later accepted point — target or not — keeps advancing it; OneTarget reports the last reached target, not the first (.hxx:154-227). |
| ⬜ Q18 |
CollidingFrontsImageFilter |
ApplyConnectivity's flood fill is seeded on SeedPoints1 alone (.hxx:114-122), so the forced −1e-6 at every SeedPoints2 seed (.hxx:92-97) is dropped from the output unless that seed is face-connected to front 1's region. |
| ⬜ Q19 |
itk::watershed::Segmenter |
Edge lists are sorted with a height-only operator< (itkWatershedSegmentTable.h:73-82); a NaN height compares "equivalent" to everything — a strict-weak-ordering violation (formal UB; benign merge-equal in libstdc++). |
| ⬜ Q20 |
itkMath.h FloatAlmostEqual |
The absolute-difference test (default 0.1 · epsilon) returns equal before the signbit check (itkMath.h:334-352) — 0.0 == -0.0, and near-zero pairs millions of ULPs apart compare equal, partially defeating the documented ULP semantics. |
| ⬜ Q21 |
MultiphaseDenseFiniteDifferenceImageFilter |
Computes the CFL time step then discards it: timeStep = 0.08; // FIXME !!! After all this work, assign a constant !!! Why ?? (.hxx:153-154). |
| ⬜ Q22 |
Chan-Vese UseImageSpacing |
Builds m_ScaleCoefficients that nothing in the Chan-Vese chain reads; the PDE divides by m_InvSpacing from the feature image unconditionally while the re-distancing Maurer map inherits the level-set image's spacing — the flag's only live effect is maurer->SetUseImageSpacing (.hxx:227). Two images with different spacings drive the PDE and the re-distancing with different metrics. |
| ⬜ Q23 |
ScalarChanAndVeseDenseLevelSet RMS/labeling |
m_ReinitializeCounter defaults to 1, so the reinit branch runs every iteration, zeroes the RMS accumulator, and refills it with the re-distancing residual (.hxx:214-248) — MaximumRMSError halting compares against the wrong quantity. CopyInputToOutput labels phi < 0 strictly (.hxx:69), so exactly-zero boundary pixels are excluded from the label. |
| ⬜ Q24 |
ObjectMorphologyImageFilter |
BeforeThreadedGenerateData's fill-then-conditional-copy always reduces to output := input (the fill sentinel is constructed to differ from ObjectValue, .hxx:87-113); UseBoundaryCondition is dead configuration (installed but never enabled, .h:145 even warns "Don't forget to set UseBoundaryCondition to true!"); boundary detection is a hard-coded 3^dim box decoupled from the kernel radius (.hxx:130-198) — dilation is unaffected, erosion diverges substantially from true erosion (the header caveats this). |
| 🔄 Q25 |
IterativeInverseDisplacementFieldImageFilter |
the coordinate-descent probe step is axis 0's spacing on every axis |
| 🔄 Q26 |
DisplacementFieldJacobianDeterminantFilter |
SetDerivativeWeights silently flips UseImageSpacing off — precedence between the two is last-writer-wins, while the class doc describes only one ordering |
| 🔄 Q27 |
WarpImageFilter |
m_OutputSize[0] == 0 alone keys "size unset"; output-grid defaults are unit/zero/identity from neither input; the field is sampled with edge replication, never out-of-domain |
| 🔄 Q28 |
VectorConnectedComponentImageFilter |
zero vectors join anything under the default threshold (no special case); concept macro name typo InputValyeTypeIsFloatingCheck |
| 🔄 Q29 |
MahalanobisDistanceMembershipFunction |
the det < 0 exception branch is unreachable; GetCovariance() never reflects the singular-case substitute |
| 🔄 Q30 |
FastSymmetricForcesDemonsRegistrationFilter |
its difference function is ESMDemonsRegistrationFunction; the identically-named FastSymmetricForcesDemonsRegistrationFunction class is orphaned from its own filter |
| 🔄 Q31 |
ExponentialDisplacementFieldImageFilter |
"take the ceil" comment vs + 1.0-then-truncate code (integral values overshoot by 1); a zero field still runs one squaring iteration |
| 🔄 Q32 |
WarpVectorImageFilter (as wired by DiffeomorphicDemonsRegistrationFilter) |
under the nearest-neighbor-extrapolating interpolator, IsInsideBuffer is always true and m_EdgePaddingValue is dead code |
| 🔄 Q33 |
ESMDemonsRegistrationFunction |
the WarpedMoving and MappedMoving gradient options coincide exactly at a zero displacement field (interior pixels, default linear interpolation, coincident grids) |
| 🔄 Q34 |
LevelSetMotionRegistrationFilter/Function |
dt = 1/maxL1 makes the applied step at the governing pixel exactly Alpha-independent; thresholded pixels count toward the metric while out-of-buffer pixels don't; Halt adds an RMS==0 clause the base can never fire |
| 🔄 Q35 |
LevelSetMotionRegistrationFunction |
one-sided difference steps add index-axis spacing to a physical coordinate — wrong direction and step under non-identity direction cosines |
| 🔄 Q36 |
demons family |
zero-iteration runs report GetRMSChange() == 0 but GetMetric() == NumericTraits<double>::max() in the two filters that don't override GetRMSChange |
| 🔄 Q37 |
LevelSetMotionRegistrationFunction smoothing |
the smoothed moving image is re-quantized to the moving image's own pixel type (u8 in → u8 smoothed) before the gradient; and any axis shorter than 4 pixels is a hard error for this family member only |
| 🔄 Q38 |
FastMarchingImageFilterBase::IsChangeWellComposed3D |
builds its neighborhood iterator over GetRequestedRegion() (empty, never set) where the 2-D sibling and the strict-topology check use GetBufferedRegion() — harmless (only SetLocation+GetPixel are used) but inconsistent (.hxx:779 vs :583/:619) |
| ⬜ Q39 |
LabelToRGBImageFilter |
background is always fixed black (m_BackgroundColor zero-filled at construction); the functor comment promising a "gray pixel matching the background intensity" describes LabelOverlayFunctor, not this class (itkLabelToRGBFunctor.h:93-98) |
| ⬜ Q40 |
LabelToRGB/LabelOverlay palette |
the 0–255 palette constants are rescaled by NumericTraits<output RGB component>::max() rather than a fixed 255 (itkLabelToRGBFunctor.h:110-117) — a double RGB output scales the palette into [0, ≈1.8e308] |
| ⬜ Q41 |
HessianToObjectnessMeasureImageFilter |
eigenvalues are sorted by |λ| with unstable std::sort (equal-magnitude order unspecified, .hxx:78-79); alpha == 0 skips only the R_A factor while beta == 0 (or a zero denominator) zeroes the whole measure (.hxx:111-151) — asymmetric parameter semantics |
| ⬜ Q42 |
ComplexToModulusImageFilter |
computes sqrt(re*re + im*im) in the component type (.h:46-50): ComplexFloat32 overflows to inf above |component| ≈ 1.8e19 and flushes to 0 near denormals, where std::hypot would not |
| ⬜ Q43 |
RelabelLabelMapFilter |
class doc says objects are reassigned via PushLabelObject; the inherited implementation uses SetLabel + AddLabelObject against a counter from 0 (itkAttributeRelabelLabelMapFilter.hxx:64-79) |
| ⬜ Q44 |
LabelMap::HasLabel |
doc says the background label also returns true; the implementation is a plain find(label) != end(), which returns false for background (.hxx:151-153) |
| ⬜ Q45 |
StatisticsLabelMapFilter weighted moments |
adds spacing²/12 (the pixel's own second moment) to every central-moment diagonal (.hxx:224-228) while ShapeLabelMapFilter does not — WeightedPrincipalMoments exceed PrincipalMoments by 1/12 per eigenvalue even on a constant feature image |
| ⬜ Q46 |
StatisticsLabelMapFilter extremum indices |
min/max index updates are non-strict (v <= min, v >= max, .hxx:118-127) — on ties the recorded index is the last tie in raster order |
| ⬜ Q47 |
itk::Statistics::Histogram::Initialize |
computes the bin interval and every non-final bin bound in float (itkHistogram.hxx:225-236) — a Histogram<double> gets float-quantized bin edges, which StatisticsLabelMapFilter's Median inherits |
| ⬜ Q48 |
ImageBase::IsCongruentImageGeometry |
scales the coordinate tolerance by this->GetSpacing()[0] only (itkImageBase.hxx:400-405) — the predicate is asymmetric when the two images differ in axis-0 spacing, and axes 1+ are judged against axis 0's scale |
| ⬜ Q49 |
DivideImageFilter::Div |
a near-zero divisor returns NumericTraits<TOutput>::max() regardless of the numerator's sign or magnitude (itkArithmeticOpsFunctors.h:142-151); through NormalizeToConstantImageFilter a zero-sum input sends the entire output to the type maximum |
| ⬜ Q50 |
VotingBinaryHoleFillingImageFilter |
the else-branch stamps it.Set(foregroundValue) for any center value other than backgroundValue (.hxx:138-141) — a third pixel value is silently promoted to foreground without counting toward GetNumberOfPixelsChanged(); the iterative sibling inherits this |
| ⬜ Q51 |
ExtractImageFilter |
the output origin ignores a collapsed axis's start index (and the collapsed↔retained direction coupling), so extracting slice k of an oblique volume reports slice 0's origin (.hxx:74-85, :162-179) |
| ⬜ Q52 |
CenteredVersorTransformInitializer::ComputeRotation |
uses R = M·Fᵀ (.hxx:47), which does not satisfy the covariance-aligning identity R·Cov_f·Rᵀ = Cov_m (that requires Mᵀ·F); with per-image independent eigenvector sign canonicalization the initializer can 180°-flip an axis |
| ⬜ Q53 |
CompositeTransform |
Get/SetParameters, Get/SetFixedParameters, and UpdateTransformParameters all flatten sub-transforms in reverse queue order (last-added first, .hxx:613,665-687,703,734,847) while GetNthTransform(n) indexes forward — two orderings in one class |
| ⬜ Q54 |
FFTPadImageFilter |
SizeGreatestPrimeFactor == 1 pads each axis to an even size (.hxx:64-68) while the class doc says "1 or less … disable the extra padding" (.h:90) — only 0 disables |
| ⬜ Q55 |
MaskedFFTNormalizedCorrelationImageFilter |
pads FFT sizes using only factors 2/3/5 (.hxx:528-545) while FFTConvolutionImageFilter uses the backend's greatest prime factor (11 under PocketFFT) — identical inputs get different padded sizes across the two FFT-based filters |
| ⬜ Q56 |
RegistrationParameterScalesFromIndexShift |
the step scale is computed in continuous-index (voxel) units while MaximumStepSizeInPhysicalUnits defaults to the minimum virtual spacing (mm) — the gradient-descent learning rate divides mm by voxels, dimensionally mixed on anisotropic spacing (.hxx:66-79; itkGradientDescentOptimizerv4.hxx:272) |
| ⬜ Q57 |
RegistrationParameterScalesEstimator CentralRegionRadius |
consulted only by CentralRegionSampling, which is selected only for displacement-field/B-spline transforms (.hxx:346-367, :203-211); linear/affine transforms take Corner/Random sampling and never read it, and for displacement fields the scales estimate is radius-independent |
| ⬜ Q58 |
ObjectToObjectOptimizerBase weights |
optimizer weights within 1e-4 of 1.0 are flagged identity and never multiplied into the gradient (strict >, so exactly 1e-4 is also discarded, itkObjectToObjectOptimizerBase.cxx:154-164); the sibling scales-identity test uses a 100× looser 0.01 |
| ⬜ Q59 |
v4 optimizer weights application |
every v4 optimizer validates the weights-array length in the base StartOptimization, but the vnl-based LBFGS/LBFGSB and Amoeba optimizers then apply only scales and silently ignore well-sized weights (itkSingleValuedNonLinearVnlOptimizerv4.cxx:53-58); LBFGS2Optimizerv4, by contrast, does apply them |
| ⬜ Q60 |
NiftiImageIO |
ITK_InputFilterName is encapsulated (:1102) and then wiped six lines later by SetImageIOMetadataFromNIfTI, whose first act is thisDic.Clear() (:630) |
| ⬜ Q61 |
NiftiImageIO |
the "original file was non-orthogonal" warning is dead: stored under ITK_sform_corrected (:632), looked up under nifti_sform_corrected (:2038) |
| ⬜ Q62 |
NiftiImageIO |
getQFormCodeFromDictionary/getSFormCodeFromDictionary results are unconditionally overwritten with NIFTI_XFORM_SCANNER_ANAT (:2077-2078) — yet their StringToInt32 still throws on a non-numeric dictionary value that would have been discarded anyway |
| ⬜ Q63 |
NiftiImageIO |
IMG.NII passes CanWriteFile (extension check lowercases) but WriteImageInformation compares the raw uppercase extension against lowercase spellings only and throws "Bad Nifti file name" (:1173-1202) |
| ⬜ Q64 |
niftilib (vendored) |
a magic-less (Analyze-7.5) header in a single .nii reports nifti_type == 1: convert_nhdr2nim sets ANALYZE, then nifti_set_type_from_names forces NIFTI1_1 whenever fname == iname — true for every single-file case (nifti1_io.c:3730, :3453-3454) |
| ⬜ Q65 |
GiplImageIO |
the magic number is validated only in CanReadFile; ReadImageInformation re-reads the same 4 bytes and discards them, so a file supplied via SetImageIO gets no magic check (:566-584) |
| ⬜ Q66 |
GiplImageIO |
NDims is a population count of non-trivial dims — a 2-D image round-trips as 3-D [nx, ny, 1], and malformed dims like [4, 0, 5, 1] give NDims == 2 with the 5 silently dropped (:294-312) |
| ⬜ Q67 |
GiplImageIO |
image_type is byte-swapped only in the BigEndian arm and min/max are never swapped — unobservable, since the constructor pins BigEndian and all these header fields are read then discarded (:78, :325-328, :443-461) |
| ⬜ Q68 |
GiplImageIO |
registers no read/write extensions (constructor omits AddSupported*Extension) and its real gate is a case-sensitive suffix test — IMAGE.GIPL is not recognized (:1079-1090) |
| ⬜ Q69 |
GiplImageIO |
the compressed path uses gzopen/gzread throughout, which transparently pass through a non-gzip stream — a .gipl.gz containing a plain GIPL file is accepted and read verbatim |
| ⬜ Q70 |
VTKImageIO::GetNextLine |
tolerates 5 consecutive empty lines and throws on the 6th, while its message says "consecutive 5 empty lines" — off by one from its own text (:44-46) |
| ⬜ Q71 |
VTKImageIO::GetNextLine |
tests eof() before using the line, so a header whose final line lacks a trailing newline throws "Premature EOF" even though getline delivered the content (:50-56) |
| ⬜ Q72 |
VTKImageIO |
the 2-D/3-D test is written twice and the first copy is dead — the surviving rule is "2-D iff dims[2] <= 1", so DIMENSIONS 4 1 3 is a one-row-tall 3-D volume (:210-221) |
| ⬜ Q73 |
VTKImageIO |
a round-trip collapses ComplexFloat32 to a 2-component VectorFloat32, and a 1-component vector image to a scalar — the legacy format cannot express either distinction (:701-709, :311-329) |
| ⬜ Q74 |
VTKImageIO |
the component count is never range-checked on either side (writer carries a // todo this should be asserted, :705) — COLOR_SCALARS c 7 reads as a 7-component VectorUInt8 |
| ⬜ Q75 |
ImageIOBase ASCII read |
is >> temp with an integer PrintType: a negative literal for an unsigned pixel type wraps rather than failing, and out-of-range 8-bit values narrow silently (−1 → 255, 300 → 44) (itkImageIOBase.cxx:804-811) |
| ⬜ Q76 |
MetaImageIO |
complex pixels cannot round-trip: ElementNumberOfChannels == 2 is indistinguishable from a 2-vector, and the reader forces VECTOR for any channels > 1 (itkMetaImageIO.cxx:241-244, in-source // BUG: 8732) |
| ⬜ Q77 |
MetaIO (vendored) |
MetaImage::CanRead re-tests the suffix case-sensitively against .mha/.mhd while the ITK factory and CanWriteFile match case-insensitively — IMG.MHA is writable but not readable (metaImage.cxx:1184-1199) |
| ⬜ Q78 |
MetaIO (vendored) |
booleans test only value[0] == 'T'|'t'|'1' — trash parses as true, yes as false (metaObject.cxx:1605-1642) |
| ⬜ Q79 |
MetaIO (vendored) |
header-alias precedence is M_Read's apply order, not file order: Origin > Position > Offset, TransformMatrix > Rotation > Orientation, BinaryDataByteOrderMSB > ElementByteOrderMSB — a trailing ElementByteOrderMSB = False loses to an earlier BinaryDataByteOrderMSB = True (metaObject.cxx:1618-1707) |
| ⬜ Q80 |
MetaIO (vendored) |
writes a zlib (RFC-1950) stream but reads zlib or gzip (inflateInit2(…, 47)) — the reader accepts a superset of what the writer produces (metaUtils.cxx:808, :862) |
| ⬜ Q81 |
vendored gzip readers |
NrrdIO's gzio and zlib's gz_look fall back to a byte-for-byte copy when the gzip magic is absent, while niftilib's nifti_is_gzfile is a pure name test — a genuine gzip stream named .nii is never inflated, and a .nii.gz holding raw bytes reads via the copy fallback (gzio.c:501-519; nifti1_io.c:2656-2668) — reviewed, no change (see below) |
| 🔄 Q82 |
NrrdImageIO |
only RAS/LAS/LPS are converted to LPS; every other space value (scanner-xyz, right-up, …) has its geometry used verbatim through an empty default: with no warning (itkNrrdImageIO.cxx:766-786) — fix: branch bug-nrrdio-6575 |
| ⬜ Q83 |
NrrdIO (vendored) |
data file: LIST reads filenames until EOF, so a blank line becomes an empty filename that later fails fopen("") — there is no in-band list terminator (parseNrrd.c:1347-1400) — reviewed, no change (see below) |
| 🔄 Q84 |
NrrdIO (vendored) |
formatNRRD_write emits comments and key/value pairs after the data file: field, defeating the writer's own "data file must be last" ordering — latent header corruption for LIST-form writes with comments (formatNRRD.c:783-857; cf. write.c:724-728) — fix: teem branch bug-nrrd-datafile-last (pushed to sourceforge; regenerates into ITK NrrdIO) + ITK-side bug-nrrdio-6575 |
| ⬜ Q85 |
TxtTransformIO |
Parameters: lines are parsed by vnl read_ascii, which stops at the first unparseable token and reports success — Parameters: 1 2 oops 4 silently yields [1, 2] (itkTxtTransformIO.cxx:183-186) |
| ⬜ Q86 |
TxtTransformIO |
one transform local is reassigned per Transform: line and buffered parameters apply only once both Parameters and FixedParameters have been seen — unpaired lines bind one transform's parameters to a different transform, silently (:168-232) |
| ⬜ Q87 |
TxtTransformIO |
the #Insight Transform File V1.0 header is swallowed by the #-comment skip and never validated; CanReadFile checks only the .txt/.tfm extension (:148, :39-46) |
| ⬜ Q88 |
TxtTransformIO |
an empty FixedParameters vector writes the line FixedParameters: with a trailing space (:238-250, :303-305) |
| ⬜ Q89 |
HDF5TransformIO |
CanWriteFile claims .hdf/.h4/.hdf4/.he4 — HDF4 container names — though Write always creates an HDF5 file, and the lowercase extension table rejects .H5/.HDF5 (itkHDF5TransformIO.cxx:83-84, :424) |
| ⬜ Q90 |
PNGImageIO::ReadImageInformation is a bare return; on a wrong PNG signature (:334-338) where Read throws "File is not png type: …" for the identical png_sig_cmp (:139-143) — a caller reaching it via SetImageIO (bypassing CanReadFile) gets a fabricated success with m_Dimensions unset. A missing file pointer returns silently the same way (:321-324) |
|
| ⬜ Q91 |
PNGImageIO::CanWriteFile passes ignoreCase = false to HasSupportedWriteExtension (:497), unlike the trait's own case-insensitive default; .png and .PNG are both registered (:281-287), so foo.PNG is claimed but foo.PnG is not |
|
| ⬜ Q92 |
m_ExpandRGBPalette's in-class initializer is false (itkImageIOBase.h:853) but ImageIOBase's constructor calls Reset(false), which unconditionally sets it true (itkImageIOBase.cxx:28,45) — PNGImageIO never touches the flag, so the header default is dead and every instance starts with palette expansion on |
|
| ⬜ Q93 |
HDF5TransformIO's ReadParameters/ReadFixedParameters emit "Wrong data type for " << DataSetName << "in HDF5 File" — no space before in, so the dataset name runs into the word (itkHDF5TransformIO.cxx:171, :217) |
|
| ⬜ Q94 |
the rank-1 check in both of those readers raises "Wrong # of dims for TransformType in HDF5 File" (:176, :222) — TransformType is the one dataset the check never covers |
|
| ⬜ Q95 |
HDF5TransformIO::Read loops i < transformGroup.getNumObjs() and then openGroup(GetTransformName(i)) (:297-302); getNumObjs counts every link, so one stray dataset beside the 0, 1, … subgroups makes the loop ask for a group N that was never written and the read dies inside libhdf5 rather than reporting an extra object |
|
| ⬜ Q96 |
HDF5TransformIO::CanReadFile is h5file.exists("/TransformGroup") (:59-66), i.e. H5Lexists — a dataset of that name passes, the factory hands the file over, and Read's openGroup (:295) throws an H5::Exception instead of the factory's clean "could not create Transform IO object" |
|
| ⬜ Q97 |
HDF5ImageIO stores three axis conventions in one group: Dimension/Origin/Spacing in ITK order, fastest-moving first (:1151-1161); only VoxelData's dataspace is reversed (:1170-1182); and Directions writes m_Direction[i] as its row i (:490-510) while that is column i of the direction matrix — so a 4×3 2-D image writes Dimension = [4, 3] and a VoxelData of shape [3, 4], and the Directions dataset is the matrix transpose |
|
| ⬜ Q98 |
HDF5ImageIO's ReadScalar (:410) and ReadVector (:479) both raise "Wrong # of dims for TransformType in HDF5 File" — the transform IO's message (Q94), verbatim, in a file that holds no transform. ReadDirections (:521) at least names the image directions |
|
| ⬜ Q99 |
VoxelType is written on every Write (:1163-1166) and read on no Read: m_ComponentType is derived from imageSet.getDataType() (:752-753). A file whose VoxelType reads FLOAT over int16 voxels loads as Int16; a file with no VoxelType at all loads fine. The class comment (itkHDF5ImageIO.h:68-71) says it exists to make the file "user friendly with respect to HDF5 viewers", so this is by design — worth stating, since it looks authoritative |
|
| ⬜ Q100 |
HDF5ImageIO::CanReadFile is h5file.exists("/ITKImage") (:647-654) — same H5Lexists shape as Q96, so a dataset named /ITKImage is claimed and ReadImageInformation's first openDataSet throws |
|
| ⬜ Q101 |
three of ReadImageInformation's meta-data branches are unreachable: WriteScalar(unsigned long) stores NATIVE_UINT tagged isUnsignedLong (:322-342), so no ITK-written dataset is ever NATIVE_INT + isUnsignedLong as :801-805 expects; and on every LP64 platform H5Tequal(NATIVE_LONG, NATIVE_LLONG) and H5Tequal(NATIVE_ULONG, NATIVE_ULLONG) are true, so the arms at :874-897 swallow every 64-bit integer and those at :898-905 never run (the isLLong/isULLong tags inside the surviving arms are what actually distinguish the two) |
|
| ⬜ Q102 |
a meta-data dataset of rank > 1 is skipped by continue (:785-789), and the if/else if chain over the fourteen NATIVE_* types ends in an else that tests only metaDataType == strType with no else of its own (:914-922) — so a compound, enum, array or variable-length-sequence dataset leaves no dictionary entry and raises nothing. A subgroup under MetaData, by contrast, is fatal: the loop opens every link with openDataSet (:776-782) |
|
| ⬜ Q103 |
HDF5ImageIO's constructor registers .hdf, .h4, .hdf4, .he4 — HDF4 container names — for both reading and writing alongside the HDF5 ones (:36-43), the same eight-name list already noted on the transform side (Q89). Here CanWriteFile is HasSupportedWriteExtension(name) with ignoreCase = true (itkImageIOBase.h:788), so IMAGE.H5 is claimed for writing where the transform IO's case-sensitive CanWriteFile declines it |
|
| ⬜ Q104 |
JPEGImageIO::Write throws "JPEG Writer can only write 2-dimensional images" before opening anything (:459-462), where PNGImageIO::WriteSlice silently writes only the first slice (B71) — the two sibling IOs disagree on whether a 3-D source is an error |
|
| ⬜ Q105 |
JPEGImageIO::CanReadFile is markedly stricter than PNG's: case-sensitive extension (:102, ignoreCase = false), then the 0xFFD8 magic (:117-130), then a full jpeg_read_header parse (:152) — where PNGImageIO::CanReadFile stops at the 8-byte signature |
|
| ⬜ Q106 |
JPEGImageIO::CanWriteFile passes ignoreCase = false (:449) against four registered spellings (:308-314) — .jpg, .JPG, .jpeg, .JPEG are claimed, .Jpg is not. Same shape as PNG's (Q91) |
|
| ⬜ Q107 |
m_UseCompression is assigned false in the constructor (:299) and never read: jpeg_set_quality is called unconditionally (:537), so only Quality/CompressionLevel has any effect and UseCompressionOn() is silently inert for this IO |
|
| ⬜ Q108 |
Two quirks of the TIFF resolution tags. (a) Spacing survives a round-trip only to float precision: WriteImageInformation stores 25.4 / m_Spacing[i] (:587, :794) into XRESOLUTION, which libtiff holds as a float (tif_dir.h:98; TIFF_RATIONAL/TIFF_SETGET_FLOAT, tif_dirinfo.c:90) and m_XResolution mirrors as float — so a spacing of 1.0 reads back as 1.0000000150…. (b) TIFFReaderInternal::Initialize reads XRESOLUTION with the non-defaulted TIFFGetField (:202) over Clean()'s seed of 1, while the read guard is only unit > 0 && m_XResolution > 0 (:375-385) — so a page with RESOLUTIONUNIT = 2 (inch) and no XRESOLUTION tag yields a spacing of 25.4 rather than the default 1.0. (An absent RESOLUTIONUNIT seeds unit = 1, which takes neither branch, so the plain case is safe.) |
|
| ⬜ Q109 |
FastChamferDistanceImageFilter |
the anti-raster sweep is for (it.GoToEnd(), --it; !it.IsAtBegin(); --it) (.hxx:194), so flat index 0 is never the center of the backward pass — harmless, because the pass writes to lower-index neighbours and flat index 1 still updates it. The 3-D weights are the float literals 0.92644, 1.34065, 1.65849 (.hxx:38, :41, :44) |
| ⬜ Q110 |
IsolatedConnectedImageFilter |
the bisection bounds and midpoint are typed NumericTraits<InputPixelType>::AccumulateType (.hxx:147, :178-180, :243-245), which for unsigned int is unsigned int — no widening (itkNumericTraits.h:1027). guess = (upper + lower) / 2 (.hxx:231, :296) can therefore overflow for thresholds near UINT_MAX |
| ⬜ Q111 |
STAPLEImageFilter |
contrary to the common description of the algorithm, there is no p = q = 0.99999 seeding: W is seeded from the rater mean (.hxx:116), the prior g_t is computed once before the loop (:123), per-rater convergence is (dp)² , (dq)² ≤ 1e-14 (:51, :215-220), and the foreground test is a two-sided ε-window with ε = 1e-10 (:94). Worth stating because implementations that copy the papers' seeding will not reproduce ITK |
| ⬜ Q112 |
BinaryMedianImageFilter |
the majority rule is strict > against neighborhoodSize / 2 (.hxx:121, :136-143), so a tie goes to background; and the output is always exactly ForegroundValue or BackgroundValue — a pixel whose value matches neither is not passed through |
| ⬜ Q113 |
ChangeLabelImageFilter |
the change map is std::map<InputPixelType, OutputPixelType> (.h:57), so keys are quantized through the pixel type and a repeated key is last-wins (.h:77); operator() performs a single lookup (.h:92-101), so a→b together with b→c is not transitive — a maps to b |
| ⬜ Q114 |
BSplineDecompositionImageFilter |
the causal-coefficient initialization switches between an accelerated truncated sum and the exact mirror-boundary sum on horizon < dataLength, where `horizon = ceil(log(tol)/log( |
| ⬜ Q115 |
PatchBasedDenoisingImageFilter noise-model fidelity |
on the first iteration the output equals the input, so GAUSSIAN's 2·(in − out) (.hxx:2015) and POISSON's (in − out)/(out + 1e-5) (:2049) are both identically zero. With SimpleITK's default NumberOfIterations = 1, neither noise model contributes anything; RICIAN's Bessel-ratio term (:2031-2032) is nonzero and is unaffected |
| ⬜ Q116 |
PatchBasedDenoisingImageFilter internals |
three of a kind: ResolveSigmaUpdate writes m_KernelBandwidthSigma (.hxx:1854, :1860) before returning, and the caller then compares |sigmaUpdate| against the already-updated sigma (:1427); m_MinProbability = 2.2e-306 (:43) is added to the entropy probability and to its first and second derivatives (:1734-1738); and on the skipped-query path the subsampler's offset keeps its zero initialization rather than being recomputed by ComputeOffset (itkSpatialNeighborSubsampler.hxx:140, :147-151) |
| ⬜ Q117 |
JoinSeriesImageFilter size mismatch |
asymmetric and undocumented. Each input's requested region is derived from the primary input's size (.hxx:209-216), so a subsequent input that is smaller throws InvalidRequestedRegionError from region propagation, while one that is larger is silently corner-cropped to [0, primary_size). (Physical congruency is checked separately, with coordinateTol = m_CoordinateTolerance · spacing[0] — the first dimension's spacing for every axis, itkImageToImageFilter.hxx:184-185) |
| ⬜ Q118 |
TransformToDisplacementFieldFilter linear fast path |
the scan-line endpoints are the continuous indices idx[0] and idx[0] + size[0] — one past the last pixel — with alpha = i / size[0] (.hxx:239, :245, :255-256). This is exact rather than off-by-one, because for a linear transform T(p) − p is affine, so interpolating between those two endpoints reproduces the displacement at every interior pixel. Recorded because it reads like an off-by-one and is not |
Following up on #6569: while porting SimpleITK's filter surface to another language, I kept a ledger of everything in the ITK sources that looked wrong or surprising. It has grown to 92 suspected bugs and 118 quirks (originally 30 and 24; follow-up batches B31–B92 and Q25–Q118 are merged into the tables below) — too many to file individually without flooding the tracker, so I am reporting them together here for triage. Happy to split any subset into standalone issues if that is more useful; I am not planning PRs for all of these, so please treat this as raw material.
Status at a glance (maintainer triage — updated 2026-07-17 by @hjmjohnson)
Every PR opened against this ledger has merged. The completed work is collapsed below; what follows is only what still needs someone.
⬅ Outstanding work
Bugs fixed on a branch, needing an ITK PR (6)
WarpImageFilterExponentialDisplacementFieldImageFilter1 << numiter— UB whennumiter >= 31; unreachable by default (cap 20, clamped), but an explicitSetMaximumNumberOfIterations(>= 31)makes it liveNrrdImageIO::Writemetadata dispatchNRRD_-prefixed keys are matched bystrncmpof the candidate's length —NRRD_space directionsis swallowed by thespacehandler, and unmatchedNRRD_keys (including the reader's ownNRRD_pixel_original_axis) are silently dropped — fix: branchbug-nrrdio-6575sizes/spacings/thicknesses/axis mins/axis maxsparsedim + 1values into aNRRD_DIM_MAX(16)-element stack array — a 16-D header with a 17th token is a stack buffer overflow — fix: teem branchbug-nrrd-parse-axis-overflow(pushed to sourceforge; regenerates into ITK NrrdIO) + ITK-sidebug-nrrdio-6575nrrdOriginCalculate(vendored)gotMinloop testsaxis[0]->minfor every axis (literal 0, not the loop variable) — origins computed from NaN mins, or usable mins ignored — fix: teem branchbug-nrrd-origin-calculate(pushed to sourceforge; regenerates into ITK NrrdIO) + ITK-sidebug-nrrdio-6575rnParse_kinds(vendored)nonetoken arm assigns the axis's centering, not its kind —kinds: none …clobbers a previously parsedcenters:value — fix: teem branchbug-nrrd-kinds-none-centering(pushed to sourceforge; regenerates into ITK NrrdIO) + ITK-sidebug-nrrdio-6575bug-displacement-field-batch2-6575bug-nrrdio-6575+ 4 teem branches on sourceforgeBugs unclaimed (5) — all vendored ThirdParty, better handled upstream
nifti_read_buffer(vendored)isfiniteis a macro (glibc: always) — NaN/±Inf pixels silently destroyedElementDataFile = LIST(vendored)m_DimSize[-1]/m_SubQuantity[-1]OOB reads;== NDims→ zero-length slice reads, still successM_ReadElements(vendored)LOCALcompressed header withoutCompressedDataSizeseeks to file start and inflates its own header text over the uninitialized pixel buffer — success, garbage pixelsMET_PerformUncompression(vendored)return true— truncated/corrupt/wrong-container streams and short output all report successvnl_matlab_readhdr::read_hdr(vendored)switchlists 7 of the 8 legaltype_tencodings —1010(big-endian, column-wise, single-precision), which vnl's own writer emits on a big-endian host, is byte-swapped as though foreignQuirks
102 of 118 quirks are untriaged; 16 (Q25–Q38, Q82, Q84) are fixed on the branches listed above. The full quirk table is collapsed in its own section at the bottom — none of it is completed work, so it is all outstanding.
✅ Completed — 76 merged bug fixes (table)
ProjectionImageFilterunsigned; axis 0 wraps to a ~2³¹·spacing shift — fix: #6594 (merged)N4BiasFieldCorrectionImageFilter::SharpenImage0.0/0.0 = NaN→ NaN→int cast (UB) → unchecked histogram index — fix: #6589 (merged)SLICImageFilter0/0NaN centroid → NaN→integer index cast (UB) — fix: #6589 (merged)WienerDeconvolutionImageFilterPf == Pn→ complex division by zero → NaN output (guard passes on inf) — fix: #6589 (merged)GridImageSourceMinMaxCurvatureFlowFunction(3-D)acostaken on a gradient rescaled to length r, not 1 — wrong polar angle for radius ≥ 2 — fixed: #6603 (merged)MinMaxCurvatureFlowFunctionAdaptiveHistogramEqualizationiscale == 0→ NaN everywhere — fix: #6589 (merged)MultiLabelSTAPLEImageFilterSTAPLEImageFilterp_denom == 0→ NaN, unguarded — fix: #6589 (merged)LabelOverlapMeasuresImageFilterfalse_positive_errorguard tests the wrong quantity — NaN reachable — fix: #6589 (merged)ThresholdMaximumConnectedComponentsImageFilter(upper−lower)/2; unsigned wrap when it lands belowlower— fixed: #6595 (merged)ScalarImageKmeansImageFilterUseNonContiguousLabelsinterval underflows to0xFFFFFFFFfor > 255 classes — fix: #6596 (merged)LabelVotingImageFiltermax_label+1wraps to 0 for a UInt8 image using all 256 labels — fix: #6597 (merged)MaskedFFTNormalizedCorrelationImageFilterIterativeDeconvolutionImageFilterPadInput/CropOutputread them —OutputRegionModesilently ignored — fixed: #6599 (merged)FastApproximateRankImageFilter::SetRankAttributeMorphologyBaseImageFilterstable_sortend iterator off by one — last raster pixel never sorted — fix: #6581 (merged)BoxSigmaImageFilter0.0/0.0→ NaN for every pixel — fix: #6589 (merged)LevelSetNeighborhoodExtractorStructureTensorImageFilter(AnisotropicDiffusionLBR)K_ρapplied along axis 0 only (single-direction filter,SetDirectionnever called) — fix: #6602 (merged)CoherenceEnhancingDiffusionImageFilterTransform::ApplyToImageMetadataGetInverseTransform()(singular or non-linear transform) dereferenced without a check — fix: #6583 (merged)PatchBasedDenoisingImageFilter(POISSON)static_cast<PixelValueType>(0.99999)is 0 for integer types — step size collapses to 1e-5 always — fix: #6633 (merged)PatchBasedDenoisingImageFilterUniformRandomSpatialNeighborSubsamplerSearchloops forever when the search box is exactly the query point (kernel-bandwidth path) — fix: #6605 (merged)GaussianRandomSpatialNeighborSubsamplerunsigned int— UB; rejection loop relies on the wrap — fix: #6589 (merged)RegionBasedLevelSetFunctionCurvatureWeight == 0— fix: #6606 (merged)IterativeInverseDisplacementFieldImageFiltersmallestErrorcarries over from the previous pixel when the mapped point starts outside the buffer — output depends on raster order — fix: #6576 (merged)InverseDisplacementFieldImageFilterVectorConfidenceConnectedImageFilterFastMarchingImageFilterBase::UpdateNeighborsFastMarchingImageFilterBase(NoHandles)NoHandlesdegenerates toStrict— fixed: #6613 (merged)FastMarchingImageFilterBase::SolveScalarToRGBColormapImageFilter/ColormapFunctionNumericTraits<float/double>::min()(smallest positive) used as a "most negative" seed in two places — wrong rescale for non-positive float images, and a ≈FLT_MAXdefault input range — fixed: #6615 (merged)MergeLabelMapFilter(KEEP)LabelObjectends up aliased under two label keys — fix: #6590 (merged)LabelMapContourOverlayImageFilter(SLICE_CONTOUR)m_SliceDimensionwhere it means the read index —SliceDimension == 0draws no contour at all — fix: #6590 (merged)LabelMapMaskImageFilter(Crop)AttributeUniqueLabelMapFilterwhile (it.IsAtEnd())— inverted, never executes — fix: #6590 (merged)LabelMap::PushLabelObjectSetLabel, and silently overwrites the entry at the object's own label instead of throwing "map is full" — fix: #6590 (merged)StatisticsLabelMapFilterratio > 0check — an indefinite weighted covariance yields NaN whereShapeLabelMapFilteryields 0 — fix: #6590 (merged)NormalizedCorrelationImageFilterTransformIOBaseTemplate::CorrectTransformPrecisionTypefind("float")result used unchecked — a transform-type string naming neither precision throwsstd::out_of_rangepast every ITK handler — fixed: #6608 (merged)NiftiImageIO::Readscl_slope/scl_inter— fix: #6625 (merged)NiftiImageIO::ReadRescaleFunctionis given the voxel count — only the firstnumEltsof a multi-component/complex buffer are rescaled — fix: #6625 (merged)NiftiImageIO::ReadGiplImageIO::WriteGiplImageIO::Read!bad(); a short pixel block setsfailbit/eofbit, neverbadbit— truncated file reads as success over an uninitialized tail — fixed: #6611 (merged)GiplImageIO::Read(compressed)p != nullptron the caller's always-non-null output buffer —gzread's byte count is never inspected — fixed: #6611 (merged)GiplImageIO::Writeimage_typeis derived from the component type alone while the byte count multiplies by components — a vector image writes a scalar header plus N× the bytes, reading back as garbage — fixed: #6611 (merged)VTKImageIO::ReadReadBufferAsBinary'sbool(false on short read) is discarded — truncated BINARY file reads as success — fixed: #6610 (merged)VTKImageIO::InternalReadImageInformationsscanfreturn is discarded over uninitialized locals — an under-filledDIMENSIONS/SPACING/ORIGINline sizes the image from indeterminate values (UB) — fixed: #6610 (merged)VTKImageIO::InternalReadImageInformationSCALARS vector_field floatmatches thevectorarm and is misparsed as a 3-component VECTORS line — fixed: #6610 (merged)VTKImageIOASCII writeprecision(16)is set on a throwaway header stream; pixel data is emitted through a second stream at the default 6 significant digits — silent precision loss — fixed: #6610 (merged)ImageIOBase::ReadBuffer(ASCII)5 99999 7 8asshortreads[5, 32767, 32767, 32767]) — fixed: #6609 (merged)VTKImageIO::CanReadFileGetNextLinecalls outside the try block — a.vtkshorter than four lines throws "Premature EOF" through the factory instead of returning false — fixed: #6610 (merged)PNGImageIO::WriteSlice"wb"— truncating it — before the component-type switch whosedefault:throws; writing anint16image destroys any existing.pngand leaves it zero-byte — fixed: #6614 (merged)PNGImageIO::WriteSlicePNG_COLOR_TYPE_RGB_ALPHA) while row pointers advance by the actual count — each row's trailing components are silently dropped — fixed: #6614 (merged)HDF5ImageIO::WriteImageInformationconstCstringObj->GetMetaDataObjectValue()under a guard that admitsconstCstringObj == nullptr— null dereference for aMetaDataObject<char *>— fix: #6626 (merged)HDF5ImageIO::ReadImageInformationspacing[i]/Dims[i]are indexed tonumDimsregardless of the file's actual dataset length, reading past the end of a heap vector before any pixel is touched — fix: #6626 (merged)JPEGImageIO::WriteSlicedensity_unit = 0("aspect ratio only") while storing real dots-per-cm values — the reader's owndensity_unit > 0guard then discards them, so the spacing is lost on a round-trip through ITK — fix: #6627 (merged)JPEGImageIO::ReadTIFFImageIO::ReadVolumem_SubFiles— one ignored or untagged page before aSUBFILETYPE == 0page and the last slice is written past the end of the buffer — fix: #6636 (merged)TIFFImageIO::ReadGenericImagewidth/SamplesPerPixel/BitsPerSamplestay pinned to directory 0 — a multi-page file whose later pages are narrower over-reads the buffer — fix: #6636 (merged)TIFFImageIO::ReadImageInformationdefault:, andCanReadnever constrainsSampleFormat— a 32-bitSAMPLEFORMAT_VOIDpage is read as whateverm_ComponentTypealready held — fix: #6636 (merged)TIFFImageIO::ReadGenericImagePHOTOMETRIC_MINISWHITEis read out un-inverted — the// check invertedcomment above the plain-copy grayscale arm never grew a body — fix: #6636 (merged)TIFFImageIO::GetFormatMINISBLACK/MINISWHITEmap toGRAYSCALEregardless ofSamplesPerPixel, so a 2-sample grey+alpha page is declared 1-component and each row keeps only its first half — fix: #6636 (merged)TIFFImageIO::InternalWritePHOTOMETRIC_RGBis written for every component count other than 1 — a 2-component image becomes an RGB-tagged file withSamplesPerPixel = 2and noEXTRASAMPLES— fix: #6636 (merged)MultiLabelSTAPLEImageFilterInputPixelTypebut bounded bym_TotalLabelCount— auint8input using all 256 labels wraps the counter 255 → 0 andInitializePriorProbabilities()never terminates (reproduced as a live hang, not source analysis) — fix: #6607 (merged)MultiLabelSTAPLEImageFilter::InitializeConfusionMatrixArrayFromVotingLabelVotingImageFilternever receives the outer filter'sSetLabelForUndecidedPixels(), so the seed pass always uses automatic label selection regardless of the caller's setting — fix: #6607 (merged)GiplImageIO::ReadImageInformation/CanReadFilegzread/istream::readpairs, none of whose byte counts is checked — a truncated or non-GIPL file populates dimensions, spacing and origin from uninitialized locals (same shape as B54/B55, but no success flag is computed at all) — fix: #6628 (merged)TileImageFilter0in any non-lastLayoutaxis is never validated: with a non-zero last axis it indexes an emptystd::vectorand runs a ~2³² iteration loop; with a zero last axis (the constructor default) it divides by zero — fix: #6630 (merged)GDCMImageIO::Read(SINGLEBIT)GDCMImageIO::InternalReadImageInformation(ultrasound/hardcopy arm)(0028,0030)leaves the secondElementslot never written — axis-0 spacing is read from indeterminate heap memory; theassert(GetLength() == 2)guard is vacuous and cannot fire even in a Debug build — fix: #6639 (merged), replacing the reverted #6629LabelSetErodeImageFilter/LabelSetDilateImageFilterGenerateDataonly callsAllocateOutputs()and every write is gated onm_Scale[d] > 0; underUseImageSpacinga zero radius on the last axis skips erosion's only output-writing pass — the filter returns uninitialized memory — fix: #6631 (merged)ImageSeriesReader::GenerateOutputInformationITK_ImageOriginoverride array shorter than the output dimension is silently shrunk byArray::operator=, and the next loop still indexes toImageDimension - 1— unchecked out-of-bounds read. No in-tree ImageIO writes that key, so it is live only for an out-of-tree/customImageIO— fix: #6632 (merged)✅ Completed — PR-by-PR triage record (49 settled rows)
#6629 (B88/B89, GDCM) was reverted by #6638 — its single-bit decode assumed row-padded 1-bit Pixel Data, but DICOM PS3.5 packs contiguously — and is now replaced by #6639, which decodes contiguously (verified pixel-for-pixel against pydicom on a real 13×12×8 highdicom SEG file) and validates PixelSpacing multiplicity via
gdcm::VM::GetNumberOfElementsFromArray.Every @physwkim PR carries a single commit; each regression test was verified locally to fail on the pre-fix code and pass on the fix.
❌ Withdrawn — 4 items investigated and found not to be defects (table)
CannySegmentationLevelSetFunctionNiftiImageIOdim[1]— a 2-D vector image one row tall reads back as 1-D — not a defect; withdrawn: the write side collapses trailing degenerate dimensions too, so a 2-D 5x1 vector image and a 1-D 5 vector image produce byte-identicaldim[]; the rank is lost at write time and no read-side change can recover itPNGImageIO::WriteSliceheightcomes fromGetDimensions(1)alone, no axis beyond it is consulted — a 3-D image writes only its first slice, with no error — not a defect; withdrawn: writing the first slice of an image of 3 or more dimensions is the documented, tested contract (Modules/IO/PNG/test/itkPNGImageIOTest.cxx:152-160)HDF5ImageIO::ReadH5Dread's memory type, making the conversion the identity — aVoxelDatastoredSTD_I16BEis copied without a byte swap and read back byte-swapped, silently — not a defect; withdrawn:ReadImageInformation()requires an exactH5Tequalmatch against aNATIVE_*predtype and throws otherwise, and it is what populatesm_VoxelDataSet— soRead()is unreachable with a foreign-endian on-disk type (verified: writingSTD_I16BEthrows duringReadImageInformation())How these were found, and what was checked before filing
Each filter was reimplemented from the
.hxxsources and the two implementations compared on synthetic inputs; every divergence was traced back to the ITK code path that caused it. Before filing, every item was re-checked againstmainate46eb723a5: the cited lines re-read in context, declared types and constructor defaults verified, and each claimed consequence re-derived from the quoted code. Items whose original analysis did not survive that re-check were corrected or dropped — some of my own earlier conclusions turned out to be wrong, so I would not be surprised if a few of the remaining ones are too. Corrections welcome.One item is flagged inline as source-analysis only (B28: non-termination identified from the loop structure, not run to a live hang).
Reproduced quirks and inconsistencies
These were verified the same way but may be judged intended, historical, or not worth changing — listed for the record, one line each, citations available on request (I have exact file:line and analysis for every row).
Verified quirks Q1–Q118 (table)
KernelZeroMagnitudeThresholdε is compared against |H| by Inverse but against the |H|²-carrying denominator by Tikhonov/Wiener — "regularization → 0 equals inverse" fails in the band ε ≤ |H| < √ε.DiscreteGaussianDerivativeImageFilterFlipAxes(), unlikeDerivativeImageFilter/GradientImageFilter(which do) — odd-order derivatives carry the opposite sign vs. its siblings. Also the inter-axis intermediate isImage<OutputPixelType>, so integer outputs truncate per axis pass (the local comments say "real to real").ThresholdSegmentationLevelSetImageFilter.h:63-65) states positive = inside — the opposite of the convention its own base class documents (itkSegmentationLevelSetImageFilter.h:82-84: negative inside).CurvatureSpeedis overridden toPropagationSpeedonly by GeodesicActiveContour and ShapeDetection; Threshold/Laplacian/Canny keep the base-class constant 1 — an undocumented asymmetry across siblings.BinaryContourImageFilter.hxx:164-179) — they are not normalized toBackgroundValue, so several distinct non-foreground input values all survive into the output.SimpleContourExtractorImageFilterInputForegroundValue == InputBackgroundValuemarks every foreground pixel as contour (the neighborhood scan includes the center pixel,.hxx:84).BinaryReconstructionByErosionImageFilterBinaryNot;BackgroundValueis consumed only as a self-consistent internal sentinel — the parameter has no effect on the output.AnisotropicDiffusionImageFilter.hxx:68).ThresholdMaximumConnectedComponentsImageFilterm_LowerBoundaryis written once in the constructor and read only byPrintSelf— dead configuration with no setter.MinMaxCurvatureFlowFunctionSetStencilRadiusclamps to ≥ 1 (.hxx:43), making theradius == 0early-outs (.hxx:217-220,276-279) unreachable dead code.ScalarImageKmeansImageFilterGenerateData(SetMaximumIteration(200), threshold 0.0,.hxx:91-92), invisibly to users; andKdTreeBasedKmeansEstimator's loop tests the limit pre-increment after the refinement pass (.hxx:327-347) —MaximumIteration = nruns n+1 passes whileGetCurrentIteration()reports n.SLICImageFilter.hxx:544-546); there is no final relabel/compaction pass; per-pixel distances accumulate infloat.BoxMean/BoxSigmaImageFilteritkBoxUtilities.h:322-324,499-501) — border statistics use fewer samples. AZeroFluxNeumannBoundaryConditionis declared (L215/389) but never used, suggesting replication was once intended.BinShrinkImageFilterShrinkImageFiltersilently clamps to 1 (.hxx:289-296vsitkShrinkImageFilter.hxx:265-267); integer outputs round half-up while float outputs truncate (RoundIfInteger,.h:135-149).AntiAliasBinaryImageFiltermax − (max−min)/2iso-value and the strict> 0sign test (.hxx:109; base.hxx:605) — the output is uniformly negative, i.e. "all inside".ReinitializeLevelSetImageFilter±NumericTraits<PixelType>::max()and only overwrites band points (.hxx:206-225) — far-field pixels keep ±max rather than a distance-like magnitude.FastMarchingUpwindGradientImageFilterm_TargetValueis overwritten at every accepted point in NoTargets mode; in Some/AllTargets modes the reached test is sticky, so every later accepted point — target or not — keeps advancing it; OneTarget reports the last reached target, not the first (.hxx:154-227).CollidingFrontsImageFilterApplyConnectivity's flood fill is seeded onSeedPoints1alone (.hxx:114-122), so the forced −1e-6 at everySeedPoints2seed (.hxx:92-97) is dropped from the output unless that seed is face-connected to front 1's region.itk::watershed::Segmenteroperator<(itkWatershedSegmentTable.h:73-82); a NaN height compares "equivalent" to everything — a strict-weak-ordering violation (formal UB; benign merge-equal in libstdc++).itkMath.h FloatAlmostEqual0.1 · epsilon) returns equal before the signbit check (itkMath.h:334-352) —0.0 == -0.0, and near-zero pairs millions of ULPs apart compare equal, partially defeating the documented ULP semantics.MultiphaseDenseFiniteDifferenceImageFiltertimeStep = 0.08; // FIXME !!! After all this work, assign a constant !!! Why ??(.hxx:153-154).UseImageSpacingm_ScaleCoefficientsthat nothing in the Chan-Vese chain reads; the PDE divides bym_InvSpacingfrom the feature image unconditionally while the re-distancing Maurer map inherits the level-set image's spacing — the flag's only live effect ismaurer->SetUseImageSpacing(.hxx:227). Two images with different spacings drive the PDE and the re-distancing with different metrics.ScalarChanAndVeseDenseLevelSetRMS/labelingm_ReinitializeCounterdefaults to 1, so the reinit branch runs every iteration, zeroes the RMS accumulator, and refills it with the re-distancing residual (.hxx:214-248) —MaximumRMSErrorhalting compares against the wrong quantity.CopyInputToOutputlabelsphi < 0strictly (.hxx:69), so exactly-zero boundary pixels are excluded from the label.ObjectMorphologyImageFilterBeforeThreadedGenerateData's fill-then-conditional-copy always reduces tooutput := input(the fill sentinel is constructed to differ fromObjectValue,.hxx:87-113);UseBoundaryConditionis dead configuration (installed but never enabled,.h:145even warns "Don't forget to set UseBoundaryCondition to true!"); boundary detection is a hard-coded 3^dim box decoupled from the kernel radius (.hxx:130-198) — dilation is unaffected, erosion diverges substantially from true erosion (the header caveats this).IterativeInverseDisplacementFieldImageFilterDisplacementFieldJacobianDeterminantFilterSetDerivativeWeightssilently flipsUseImageSpacingoff — precedence between the two is last-writer-wins, while the class doc describes only one orderingWarpImageFilterm_OutputSize[0] == 0alone keys "size unset"; output-grid defaults are unit/zero/identity from neither input; the field is sampled with edge replication, never out-of-domainVectorConnectedComponentImageFilterInputValyeTypeIsFloatingCheckMahalanobisDistanceMembershipFunctiondet < 0exception branch is unreachable;GetCovariance()never reflects the singular-case substituteFastSymmetricForcesDemonsRegistrationFilterESMDemonsRegistrationFunction; the identically-namedFastSymmetricForcesDemonsRegistrationFunctionclass is orphaned from its own filterExponentialDisplacementFieldImageFilter+ 1.0-then-truncate code (integral values overshoot by 1); a zero field still runs one squaring iterationWarpVectorImageFilter(as wired byDiffeomorphicDemonsRegistrationFilter)IsInsideBufferis always true andm_EdgePaddingValueis dead codeESMDemonsRegistrationFunctionWarpedMovingandMappedMovinggradient options coincide exactly at a zero displacement field (interior pixels, default linear interpolation, coincident grids)LevelSetMotionRegistrationFilter/Functiondt = 1/maxL1makes the applied step at the governing pixel exactlyAlpha-independent; thresholded pixels count toward the metric while out-of-buffer pixels don't;Haltadds an RMS==0 clause the base can never fireLevelSetMotionRegistrationFunctionGetRMSChange() == 0butGetMetric() == NumericTraits<double>::max()in the two filters that don't overrideGetRMSChangeLevelSetMotionRegistrationFunctionsmoothingu8in →u8smoothed) before the gradient; and any axis shorter than 4 pixels is a hard error for this family member onlyFastMarchingImageFilterBase::IsChangeWellComposed3DGetRequestedRegion()(empty, never set) where the 2-D sibling and the strict-topology check useGetBufferedRegion()— harmless (onlySetLocation+GetPixelare used) but inconsistent (.hxx:779vs:583/:619)LabelToRGBImageFilterm_BackgroundColorzero-filled at construction); the functor comment promising a "gray pixel matching the background intensity" describesLabelOverlayFunctor, not this class (itkLabelToRGBFunctor.h:93-98)LabelToRGB/LabelOverlaypaletteNumericTraits<output RGB component>::max()rather than a fixed 255 (itkLabelToRGBFunctor.h:110-117) — adoubleRGB output scales the palette into[0, ≈1.8e308]HessianToObjectnessMeasureImageFilterstd::sort(equal-magnitude order unspecified,.hxx:78-79);alpha == 0skips only the R_A factor whilebeta == 0(or a zero denominator) zeroes the whole measure (.hxx:111-151) — asymmetric parameter semanticsComplexToModulusImageFiltersqrt(re*re + im*im)in the component type (.h:46-50):ComplexFloat32overflows to inf above |component| ≈ 1.8e19 and flushes to 0 near denormals, wherestd::hypotwould notRelabelLabelMapFilterPushLabelObject; the inherited implementation usesSetLabel+AddLabelObjectagainst a counter from 0 (itkAttributeRelabelLabelMapFilter.hxx:64-79)LabelMap::HasLabelfind(label) != end(), which returns false for background (.hxx:151-153)StatisticsLabelMapFilterweighted momentsspacing²/12(the pixel's own second moment) to every central-moment diagonal (.hxx:224-228) whileShapeLabelMapFilterdoes not —WeightedPrincipalMomentsexceedPrincipalMomentsby 1/12 per eigenvalue even on a constant feature imageStatisticsLabelMapFilterextremum indicesv <= min,v >= max,.hxx:118-127) — on ties the recorded index is the last tie in raster orderitk::Statistics::Histogram::Initializefloat(itkHistogram.hxx:225-236) — aHistogram<double>gets float-quantized bin edges, whichStatisticsLabelMapFilter'sMedianinheritsImageBase::IsCongruentImageGeometrythis->GetSpacing()[0]only (itkImageBase.hxx:400-405) — the predicate is asymmetric when the two images differ in axis-0 spacing, and axes 1+ are judged against axis 0's scaleDivideImageFilter::DivNumericTraits<TOutput>::max()regardless of the numerator's sign or magnitude (itkArithmeticOpsFunctors.h:142-151); throughNormalizeToConstantImageFiltera zero-sum input sends the entire output to the type maximumVotingBinaryHoleFillingImageFilterit.Set(foregroundValue)for any center value other thanbackgroundValue(.hxx:138-141) — a third pixel value is silently promoted to foreground without counting towardGetNumberOfPixelsChanged(); the iterative sibling inherits thisExtractImageFilter.hxx:74-85,:162-179)CenteredVersorTransformInitializer::ComputeRotationR = M·Fᵀ(.hxx:47), which does not satisfy the covariance-aligning identityR·Cov_f·Rᵀ = Cov_m(that requiresMᵀ·F); with per-image independent eigenvector sign canonicalization the initializer can 180°-flip an axisCompositeTransformGet/SetParameters,Get/SetFixedParameters, andUpdateTransformParametersall flatten sub-transforms in reverse queue order (last-added first,.hxx:613,665-687,703,734,847) whileGetNthTransform(n)indexes forward — two orderings in one classFFTPadImageFilterSizeGreatestPrimeFactor == 1pads each axis to an even size (.hxx:64-68) while the class doc says "1 or less … disable the extra padding" (.h:90) — only 0 disablesMaskedFFTNormalizedCorrelationImageFilter.hxx:528-545) whileFFTConvolutionImageFilteruses the backend's greatest prime factor (11 under PocketFFT) — identical inputs get different padded sizes across the two FFT-based filtersRegistrationParameterScalesFromIndexShiftMaximumStepSizeInPhysicalUnitsdefaults to the minimum virtual spacing (mm) — the gradient-descent learning rate divides mm by voxels, dimensionally mixed on anisotropic spacing (.hxx:66-79;itkGradientDescentOptimizerv4.hxx:272)RegistrationParameterScalesEstimatorCentralRegionRadiusCentralRegionSampling, which is selected only for displacement-field/B-spline transforms (.hxx:346-367,:203-211); linear/affine transforms take Corner/Random sampling and never read it, and for displacement fields the scales estimate is radius-independentObjectToObjectOptimizerBaseweights1e-4of 1.0 are flagged identity and never multiplied into the gradient (strict>, so exactly1e-4is also discarded,itkObjectToObjectOptimizerBase.cxx:154-164); the sibling scales-identity test uses a 100× looser0.01StartOptimization, but the vnl-based LBFGS/LBFGSB and Amoeba optimizers then apply only scales and silently ignore well-sized weights (itkSingleValuedNonLinearVnlOptimizerv4.cxx:53-58);LBFGS2Optimizerv4, by contrast, does apply themNiftiImageIOITK_InputFilterNameis encapsulated (:1102) and then wiped six lines later bySetImageIOMetadataFromNIfTI, whose first act isthisDic.Clear()(:630)NiftiImageIOITK_sform_corrected(:632), looked up undernifti_sform_corrected(:2038)NiftiImageIOgetQFormCodeFromDictionary/getSFormCodeFromDictionaryresults are unconditionally overwritten withNIFTI_XFORM_SCANNER_ANAT(:2077-2078) — yet theirStringToInt32still throws on a non-numeric dictionary value that would have been discarded anywayNiftiImageIOIMG.NIIpassesCanWriteFile(extension check lowercases) butWriteImageInformationcompares the raw uppercase extension against lowercase spellings only and throws "Bad Nifti file name" (:1173-1202).niireportsnifti_type == 1:convert_nhdr2nimsets ANALYZE, thennifti_set_type_from_namesforces NIFTI1_1 wheneverfname == iname— true for every single-file case (nifti1_io.c:3730,:3453-3454)GiplImageIOCanReadFile;ReadImageInformationre-reads the same 4 bytes and discards them, so a file supplied viaSetImageIOgets no magic check (:566-584)GiplImageIONDimsis a population count of non-trivial dims — a 2-D image round-trips as 3-D[nx, ny, 1], and malformed dims like[4, 0, 5, 1]giveNDims == 2with the 5 silently dropped (:294-312)GiplImageIOimage_typeis byte-swapped only in the BigEndian arm andmin/maxare never swapped — unobservable, since the constructor pins BigEndian and all these header fields are read then discarded (:78,:325-328,:443-461)GiplImageIOAddSupported*Extension) and its real gate is a case-sensitive suffix test —IMAGE.GIPLis not recognized (:1079-1090)GiplImageIOgzopen/gzreadthroughout, which transparently pass through a non-gzip stream — a.gipl.gzcontaining a plain GIPL file is accepted and read verbatimVTKImageIO::GetNextLine:44-46)VTKImageIO::GetNextLineeof()before using the line, so a header whose final line lacks a trailing newline throws "Premature EOF" even thoughgetlinedelivered the content (:50-56)VTKImageIOdims[2] <= 1", soDIMENSIONS 4 1 3is a one-row-tall 3-D volume (:210-221)VTKImageIOComplexFloat32to a 2-componentVectorFloat32, and a 1-component vector image to a scalar — the legacy format cannot express either distinction (:701-709,:311-329)VTKImageIO// todo this should be asserted,:705) —COLOR_SCALARS c 7reads as a 7-componentVectorUInt8ImageIOBaseASCII readis >> tempwith an integerPrintType: a negative literal for an unsigned pixel type wraps rather than failing, and out-of-range 8-bit values narrow silently (−1 → 255, 300 → 44) (itkImageIOBase.cxx:804-811)MetaImageIOElementNumberOfChannels == 2is indistinguishable from a 2-vector, and the reader forcesVECTORfor any channels > 1 (itkMetaImageIO.cxx:241-244, in-source// BUG: 8732)MetaImage::CanReadre-tests the suffix case-sensitively against.mha/.mhdwhile the ITK factory andCanWriteFilematch case-insensitively —IMG.MHAis writable but not readable (metaImage.cxx:1184-1199)value[0] == 'T'|'t'|'1'—trashparses as true,yesas false (metaObject.cxx:1605-1642)M_Read's apply order, not file order:Origin>Position>Offset,TransformMatrix>Rotation>Orientation,BinaryDataByteOrderMSB>ElementByteOrderMSB— a trailingElementByteOrderMSB = Falseloses to an earlierBinaryDataByteOrderMSB = True(metaObject.cxx:1618-1707)inflateInit2(…, 47)) — the reader accepts a superset of what the writer produces (metaUtils.cxx:808,:862)gz_lookfall back to a byte-for-byte copy when the gzip magic is absent, while niftilib'snifti_is_gzfileis a pure name test — a genuine gzip stream named.niiis never inflated, and a.nii.gzholding raw bytes reads via the copy fallback (gzio.c:501-519;nifti1_io.c:2656-2668) — reviewed, no change (see below)NrrdImageIORAS/LAS/LPSare converted to LPS; every otherspacevalue (scanner-xyz, right-up, …) has its geometry used verbatim through an emptydefault:with no warning (itkNrrdImageIO.cxx:766-786) — fix: branchbug-nrrdio-6575data file: LISTreads filenames until EOF, so a blank line becomes an empty filename that later failsfopen("")— there is no in-band list terminator (parseNrrd.c:1347-1400) — reviewed, no change (see below)formatNRRD_writeemits comments and key/value pairs after thedata file:field, defeating the writer's own "data file must be last" ordering — latent header corruption forLIST-form writes with comments (formatNRRD.c:783-857; cf.write.c:724-728) — fix: teem branchbug-nrrd-datafile-last(pushed to sourceforge; regenerates into ITK NrrdIO) + ITK-sidebug-nrrdio-6575TxtTransformIOParameters:lines are parsed by vnlread_ascii, which stops at the first unparseable token and reports success —Parameters: 1 2 oops 4silently yields[1, 2](itkTxtTransformIO.cxx:183-186)TxtTransformIOtransformlocal is reassigned perTransform:line and buffered parameters apply only once bothParametersandFixedParametershave been seen — unpaired lines bind one transform's parameters to a different transform, silently (:168-232)TxtTransformIO#Insight Transform File V1.0header is swallowed by the#-comment skip and never validated;CanReadFilechecks only the.txt/.tfmextension (:148,:39-46)TxtTransformIOFixedParametersvector writes the lineFixedParameters:with a trailing space (:238-250,:303-305)HDF5TransformIOCanWriteFileclaims.hdf/.h4/.hdf4/.he4— HDF4 container names — thoughWritealways creates an HDF5 file, and the lowercase extension table rejects.H5/.HDF5(itkHDF5TransformIO.cxx:83-84,:424)PNGImageIO::ReadImageInformationis a barereturn;on a wrong PNG signature (:334-338) whereReadthrows"File is not png type: …"for the identicalpng_sig_cmp(:139-143) — a caller reaching it viaSetImageIO(bypassingCanReadFile) gets a fabricated success withm_Dimensionsunset. A missing file pointer returns silently the same way (:321-324)PNGImageIO::CanWriteFilepassesignoreCase = falsetoHasSupportedWriteExtension(:497), unlike the trait's own case-insensitive default;.pngand.PNGare both registered (:281-287), sofoo.PNGis claimed butfoo.PnGis notm_ExpandRGBPalette's in-class initializer isfalse(itkImageIOBase.h:853) butImageIOBase's constructor callsReset(false), which unconditionally sets ittrue(itkImageIOBase.cxx:28,45) —PNGImageIOnever touches the flag, so the header default is dead and every instance starts with palette expansion onHDF5TransformIO'sReadParameters/ReadFixedParametersemit"Wrong data type for " << DataSetName << "in HDF5 File"— no space beforein, so the dataset name runs into the word (itkHDF5TransformIO.cxx:171,:217)"Wrong # of dims for TransformType in HDF5 File"(:176,:222) —TransformTypeis the one dataset the check never coversHDF5TransformIO::Readloopsi < transformGroup.getNumObjs()and thenopenGroup(GetTransformName(i))(:297-302);getNumObjscounts every link, so one stray dataset beside the0,1, … subgroups makes the loop ask for a groupNthat was never written and the read dies inside libhdf5 rather than reporting an extra objectHDF5TransformIO::CanReadFileish5file.exists("/TransformGroup")(:59-66), i.e.H5Lexists— a dataset of that name passes, the factory hands the file over, andRead'sopenGroup(:295) throws anH5::Exceptioninstead of the factory's clean "could not create Transform IO object"HDF5ImageIOstores three axis conventions in one group:Dimension/Origin/Spacingin ITK order, fastest-moving first (:1151-1161); onlyVoxelData's dataspace is reversed (:1170-1182); andDirectionswritesm_Direction[i]as its rowi(:490-510) while that is columniof the direction matrix — so a 4×3 2-D image writesDimension = [4, 3]and aVoxelDataof shape[3, 4], and theDirectionsdataset is the matrix transposeHDF5ImageIO'sReadScalar(:410) andReadVector(:479) both raise"Wrong # of dims for TransformType in HDF5 File"— the transform IO's message (Q94), verbatim, in a file that holds no transform.ReadDirections(:521) at least names the image directionsVoxelTypeis written on everyWrite(:1163-1166) and read on noRead:m_ComponentTypeis derived fromimageSet.getDataType()(:752-753). A file whoseVoxelTypereadsFLOAToverint16voxels loads asInt16; a file with noVoxelTypeat all loads fine. The class comment (itkHDF5ImageIO.h:68-71) says it exists to make the file "user friendly with respect to HDF5 viewers", so this is by design — worth stating, since it looks authoritativeHDF5ImageIO::CanReadFileish5file.exists("/ITKImage")(:647-654) — sameH5Lexistsshape as Q96, so a dataset named/ITKImageis claimed andReadImageInformation's firstopenDataSetthrowsReadImageInformation's meta-data branches are unreachable:WriteScalar(unsigned long)storesNATIVE_UINTtaggedisUnsignedLong(:322-342), so no ITK-written dataset is everNATIVE_INT+isUnsignedLongas:801-805expects; and on every LP64 platformH5Tequal(NATIVE_LONG, NATIVE_LLONG)andH5Tequal(NATIVE_ULONG, NATIVE_ULLONG)are true, so the arms at:874-897swallow every 64-bit integer and those at:898-905never run (theisLLong/isULLongtags inside the surviving arms are what actually distinguish the two)continue(:785-789), and theif/else ifchain over the fourteenNATIVE_*types ends in anelsethat tests onlymetaDataType == strTypewith noelseof its own (:914-922) — so a compound, enum, array or variable-length-sequence dataset leaves no dictionary entry and raises nothing. A subgroup underMetaData, by contrast, is fatal: the loop opens every link withopenDataSet(:776-782)HDF5ImageIO's constructor registers.hdf,.h4,.hdf4,.he4— HDF4 container names — for both reading and writing alongside the HDF5 ones (:36-43), the same eight-name list already noted on the transform side (Q89). HereCanWriteFileisHasSupportedWriteExtension(name)withignoreCase = true(itkImageIOBase.h:788), soIMAGE.H5is claimed for writing where the transform IO's case-sensitiveCanWriteFiledeclines itJPEGImageIO::Writethrows"JPEG Writer can only write 2-dimensional images"before opening anything (:459-462), wherePNGImageIO::WriteSlicesilently writes only the first slice (B71) — the two sibling IOs disagree on whether a 3-D source is an errorJPEGImageIO::CanReadFileis markedly stricter than PNG's: case-sensitive extension (:102,ignoreCase = false), then the0xFFD8magic (:117-130), then a fulljpeg_read_headerparse (:152) — wherePNGImageIO::CanReadFilestops at the 8-byte signatureJPEGImageIO::CanWriteFilepassesignoreCase = false(:449) against four registered spellings (:308-314) —.jpg,.JPG,.jpeg,.JPEGare claimed,.Jpgis not. Same shape as PNG's (Q91)m_UseCompressionis assignedfalsein the constructor (:299) and never read:jpeg_set_qualityis called unconditionally (:537), so onlyQuality/CompressionLevelhas any effect andUseCompressionOn()is silently inert for this IOfloatprecision:WriteImageInformationstores25.4 / m_Spacing[i](:587,:794) intoXRESOLUTION, which libtiff holds as afloat(tif_dir.h:98;TIFF_RATIONAL/TIFF_SETGET_FLOAT,tif_dirinfo.c:90) andm_XResolutionmirrors asfloat— so a spacing of1.0reads back as1.0000000150…. (b)TIFFReaderInternal::InitializereadsXRESOLUTIONwith the non-defaultedTIFFGetField(:202) overClean()'s seed of1, while the read guard is onlyunit > 0 && m_XResolution > 0(:375-385) — so a page withRESOLUTIONUNIT = 2(inch) and noXRESOLUTIONtag yields a spacing of25.4rather than the default1.0. (An absentRESOLUTIONUNITseedsunit = 1, which takes neither branch, so the plain case is safe.)FastChamferDistanceImageFilterfor (it.GoToEnd(), --it; !it.IsAtBegin(); --it)(.hxx:194), so flat index 0 is never the center of the backward pass — harmless, because the pass writes to lower-index neighbours and flat index 1 still updates it. The 3-D weights are the float literals0.92644,1.34065,1.65849(.hxx:38,:41,:44)IsolatedConnectedImageFilterNumericTraits<InputPixelType>::AccumulateType(.hxx:147,:178-180,:243-245), which forunsigned intisunsigned int— no widening (itkNumericTraits.h:1027).guess = (upper + lower) / 2(.hxx:231,:296) can therefore overflow for thresholds nearUINT_MAXSTAPLEImageFilterp = q = 0.99999seeding:Wis seeded from the rater mean (.hxx:116), the priorg_tis computed once before the loop (:123), per-rater convergence is(dp)² , (dq)² ≤ 1e-14(:51,:215-220), and the foreground test is a two-sided ε-window withε = 1e-10(:94). Worth stating because implementations that copy the papers' seeding will not reproduce ITKBinaryMedianImageFilter>againstneighborhoodSize / 2(.hxx:121,:136-143), so a tie goes to background; and the output is always exactlyForegroundValueorBackgroundValue— a pixel whose value matches neither is not passed throughChangeLabelImageFilterstd::map<InputPixelType, OutputPixelType>(.h:57), so keys are quantized through the pixel type and a repeated key is last-wins (.h:77);operator()performs a single lookup (.h:92-101), soa→btogether withb→cis not transitive —amaps tobBSplineDecompositionImageFilterhorizon < dataLength, where `horizon = ceil(log(tol)/log(PatchBasedDenoisingImageFilternoise-model fidelity2·(in − out)(.hxx:2015) and POISSON's(in − out)/(out + 1e-5)(:2049) are both identically zero. With SimpleITK's defaultNumberOfIterations = 1, neither noise model contributes anything; RICIAN's Bessel-ratio term (:2031-2032) is nonzero and is unaffectedPatchBasedDenoisingImageFilterinternalsResolveSigmaUpdatewritesm_KernelBandwidthSigma(.hxx:1854,:1860) before returning, and the caller then compares|sigmaUpdate|against the already-updated sigma (:1427);m_MinProbability = 2.2e-306(:43) is added to the entropy probability and to its first and second derivatives (:1734-1738); and on the skipped-query path the subsampler'soffsetkeeps its zero initialization rather than being recomputed byComputeOffset(itkSpatialNeighborSubsampler.hxx:140,:147-151)JoinSeriesImageFiltersize mismatch.hxx:209-216), so a subsequent input that is smaller throwsInvalidRequestedRegionErrorfrom region propagation, while one that is larger is silently corner-cropped to[0, primary_size). (Physical congruency is checked separately, withcoordinateTol = m_CoordinateTolerance · spacing[0]— the first dimension's spacing for every axis,itkImageToImageFilter.hxx:184-185)TransformToDisplacementFieldFilterlinear fast pathidx[0]andidx[0] + size[0]— one past the last pixel — withalpha = i / size[0](.hxx:239,:245,:255-256). This is exact rather than off-by-one, because for a linear transformT(p) − pis affine, so interpolating between those two endpoints reproduces the displacement at every interior pixel. Recorded because it reads like an off-by-one and is notAlso:
LinearAnisotropicDiffusionLBRImageFilter's Selling decomposition bails out after 200 superbase flips by printing"Warning: Selling's algorithm not stabilized."tostd::cerrand continuing with a possibly non-obtuse superbase — which can yield negative stencil weights. The 2-D branch guards obtuseness with a debug-onlyassert; the 3-D branch has no check at all (.hxx:185-263).AI assistance
mainate46eb723a5, checking declared types and constructor defaults, re-deriving each consequence)e46eb723a5; behavioral claims cross-checked against the independent reimplementation's pinned tests. B28 is source analysis only (not run to a live hang) and is labeled as such inline.