Skip to content

BUG: Findings collected while porting ~60 filters — reported together in one issue #6575

Description

@physwkim

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) — too many to file individually without flooding the tracker, so I am reporting them together here for triage. The quirks now live in #6684; this issue tracks the 92 suspected bugs only. 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-23 by @hjmjohnson)

Bucket Count
✅ Bugs fixed and merged 82 of 92
🚀 Bugs fixed, PR open 3 (#6681, #6682, #6683)
🔄 Bugs fixed on a branch, no ITK PR yet 0
⬜ Bugs unclaimed (all vendored ThirdParty) 1 (B64 MetaIO LIST is in upstream PR)
❌ Bugs withdrawn — investigated, not defects 4
Quirks (moved to #6684) 16 of 118 dispositioned

Every ledger PR that has completed review has merged (latest: #6672 (B34) and #6673 (B35), split from superseded draft #6669). Three are open now: #6681 (B63/Q82), #6682 (B91), #6683 (B52). The completed work is collapsed below; what follows is only what still needs someone.

⬅ Outstanding work

Bugs fixed, PR open (3)

# Site One-line symptom
🚀 B52 niftilib nifti_read_buffer (vendored) maps every non-finite float/double/complex component to 0 on read wherever isfinite is a macro — always, since it is a C99 <math.h> macro everywhere — so NaN/±Inf pixels are silently destroyed; confirmed empirically (1.5 nan inf -inf 2.5 reads back 1.5 0 0 0 2.5) — fix: #6683 (open), via ISC/nifti_clib f24a6078 + UpdateFromUpstream.sh
🚀 B91 vnl vnl_matlab_readhdr::read_hdr (vendored) The no-swap switch lists 7 of the 8 legal type_t encodings — 1010 (big-endian, column-wise, single-precision), which vnl's own writer emits on a big-endian host, is byte-swapped as though foreign — fix: #6682 (open), vendored-only by maintainer decision
🚀 B63 NrrdImageIO::Write metadata dispatch NRRD_-prefixed keys are matched by strncmp of the candidate's length — NRRD_space directions is swallowed by the space handler, and unmatched NRRD_ keys (including the reader's own NRRD_pixel_original_axis) are silently dropped — fix: #6681 (open)
Ledger item PR Author State
B52 (niftilib non-finite pixel zeroing) #6683 @hjmjohnson PR open — fixed in ISC/nifti_clib (f24a6078, both nifti1 and nifti2) and imported via UpdateFromUpstream.sh; opt-in fix_floats option keeps the old default for other consumers; 49/49 NIfTI tests
B91 (vnl matlab reader native 1010 code) #6682 @hjmjohnson PR open — endian-agnostic fail-before/pass-after GTests; vendored-only fix, no upstream vxl submission planned
B63, Q82 (NrrdImageIO, non-vendored) #6681 @hjmjohnson PR open — rebased on the merged #6674 snapshot; also adds ITK-side regression GTests for B67–B69

Vendored MetaIO — upstream PRs open at Kitware/MetaIO (3)

All three are fixed in draft PRs against Kitware/MetaIO, the repo Modules/ThirdParty/MetaIO/UpdateFromUpstream.sh imports from. ITK picks them up by re-running that script once they merge; no ITK-side change is needed. Each carries a new MetaIO regression test verified fail-before/pass-after.

# Site One-line symptom
🚀 B64 MetaIO ElementDataFile = LIST (vendored) out-of-range file dimension (LIST -1 indexes m_DimSize[-1]/m_SubQuantity[-1]; LIST NDims reads nothing) and a short slice list both report success — fix: Kitware/MetaIO#141
🚀 B65 MetaIO M_ReadElements (vendored) a compressed image whose header omits CompressedDataSize measures from the start of the file, so a LOCAL image inflates its own ASCII header — fix: Kitware/MetaIO#142
🚀 B66 MetaIO MET_PerformUncompression (vendored) unconditionally return true; also its dest_pos accounting skipped the final inflate call, so no byte-count check was possible — both fixed, and both call sites now honour the result — fix: Kitware/MetaIO#142

Quirks — moved to #6684

All 118 quirks were split into #6684 so this issue can be driven to completion. They are lower priority and deliberately deferred until the bug ledger closes. Of them, 16 are already dispositioned (Q25–Q38 in open PR #6671, Q82 in open PR #6681, Q84 merged via #6674); the remaining 102 are untriaged and tracked there.

✅ Completed — 82 merged bug fixes (table)
# Site One-line symptom
✅ B1 ProjectionImageFilter Collapsed-axis origin shift uses the axis index, unsigned; axis 0 wraps to a ~2³¹·spacing shift — fix: #6594 (merged)
✅ B2 N4BiasFieldCorrectionImageFilter::SharpenImage Degenerate bin range → 0.0/0.0 = NaN → NaN→int cast (UB) → unchecked histogram index — fix: #6589 (merged)
✅ B3 SLICImageFilter Empty cluster → 0/0 NaN centroid → NaN→integer index cast (UB) — fix: #6589 (merged)
✅ B4 WienerDeconvolutionImageFilter Pf == Pn → complex division by zero → NaN output (guard passes on inf) — fix: #6589 (merged)
✅ B6 GridImageSource Kernel loop walks only the first line per axis; origin cancels out of the pattern — fix: #6600 (merged)
✅ B7 MinMaxCurvatureFlowFunction (3-D) acos taken on a gradient rescaled to length r, not 1 — wrong polar angle for radius ≥ 2 — fixed: #6603 (merged)
✅ B8 MinMaxCurvatureFlowFunction Neighborhood scales divide by the stencil radius but only ±1 neighbors are sampled — update r² too small — fix: #6604 (merged)
✅ B9 AdaptiveHistogramEqualization Constant input → iscale == 0 → NaN everywhere — fix: #6589 (merged)
✅ B10 MultiLabelSTAPLEImageFilter Confusion-matrix increment walks into the next row on any voting tie — fix: #6579 (merged)
✅ B11 STAPLEImageFilter All-background inputs → p_denom == 0 → NaN, unguarded — fix: #6589 (merged)
✅ B12 LabelOverlapMeasuresImageFilter false_positive_error guard tests the wrong quantity — NaN reachable — fix: #6589 (merged)
✅ B13 ThresholdMaximumConnectedComponentsImageFilter Initial bisection midpoint is (upper−lower)/2; unsigned wrap when it lands below lowerfixed: #6595 (merged)
✅ B14 ScalarImageKmeansImageFilter UseNonContiguousLabels interval underflows to 0xFFFFFFFF for > 255 classes — fix: #6596 (merged)
✅ B15 LabelVotingImageFilter Undecided label max_label+1 wraps to 0 for a UInt8 image using all 256 labels — fix: #6597 (merged)
✅ B16 MaskedFFTNormalizedCorrelationImageFilter Pixel squaring runs in the input integer type — int32 overflow before the cast to real — fix: #6598 (merged)
✅ B17 IterativeDeconvolutionImageFilter Output regions clobbered before PadInput/CropOutput read them — OutputRegionMode silently ignored — fixed: #6599 (merged)
✅ B18 FastApproximateRankImageFilter::SetRank Rank forwarded to only the first Dim−1 per-axis filters — last axis always median — fix: #6580 (merged)
✅ B19 AttributeMorphologyBaseImageFilter stable_sort end iterator off by one — last raster pixel never sorted — fix: #6581 (merged)
✅ B20 BoxSigmaImageFilter Radius 0 → 0.0/0.0 → NaN for every pixel — fix: #6589 (merged)
✅ B21 LevelSetNeighborhoodExtractor Strict sign tests: a pixel exactly on the contour neutralizes its neighborhood; grid-aligned contour starves the outward march — fixed: #6601 (merged)
✅ B22 StructureTensorImageFilter (AnisotropicDiffusionLBR) Tensor smoothing K_ρ applied along axis 0 only (single-direction filter, SetDirection never called) — fix: #6602 (merged)
✅ B23 CoherenceEnhancingDiffusionImageFilter Constant input → ∞ rescale → NaN tensors → negative step count via UB cast → zero iterations by accident — fix: #6589 (merged)
✅ B24 FastMarchingUpwindGradientImageFilter AllTargets stop condition counts arrival events, not distinct targets — a duplicated target point ends the march early — fix: #6658 (merged)
✅ B25 Transform::ApplyToImageMetadata Null GetInverseTransform() (singular or non-linear transform) dereferenced without a check — fix: #6583 (merged)
✅ B26 PatchBasedDenoisingImageFilter (POISSON) static_cast<PixelValueType>(0.99999) is 0 for integer types — step size collapses to 1e-5 always — fix: #6633 (merged)
✅ B27 PatchBasedDenoisingImageFilter Update write-back casts an unclamped negative float to an unsigned pixel type — UB — fix: #6589 (merged)
✅ B28 UniformRandomSpatialNeighborSubsampler Search loops forever when the search box is exactly the query point (kernel-bandwidth path) — fix: #6605 (merged)
✅ B29 GaussianRandomSpatialNeighborSubsampler Negative Gaussian variate cast to unsigned int — UB; rejection loop relies on the wrap — fix: #6589 (merged)
✅ B30 RegionBasedLevelSetFunction Reinitialization smoothing term silently degenerates to a bare Laplacian when CurvatureWeight == 0fix: #6606 (merged)
✅ B31 IterativeInverseDisplacementFieldImageFilter smallestError carries over from the previous pixel when the mapped point starts outside the buffer — output depends on raster order — fix: #6576 (merged)
✅ B32 InverseDisplacementFieldImageFilter the field-subsampling resampler never gets the input's direction — with non-identity direction cosines the spline landmarks are not on the input lattice at all — fix: #6577 (merged)
✅ B33 VectorConfidenceConnectedImageFilter a re-estimation pass that visits zero pixels produces NaN statistics, which invert the Mahalanobis guard — the next flood includes every pixel in the image — fix: #6578 (merged)
✅ B34 WarpImageFilter the same-information fast-path test omits size, so a valid larger-output configuration dies in an iterator containment check instead of taking the general path — fix: #6672 (merged)
✅ B35 ExponentialDisplacementFieldImageFilter the field is divided by 1 << numiter — UB when numiter >= 31; unreachable by default (cap 20, clamped), but an explicit SetMaximumNumberOfIterations(>= 31) makes it live — fix: #6673 (merged)
✅ B36 FastMarchingImageFilterBase::UpdateNeighbors the neighbor loop gates on the center being strictly interior, so a border seed never propagates along the boundary-normal axis — a face seed confines the march to that face, a corner seed marches nowhere — fixed: #6613 (merged)
✅ B37 FastMarchingImageFilterBase (NoHandles) marched-alive voxels never receive a connected-component label, so the facing-neighbor label test always passes and NoHandles degenerates to Strictfixed: #6613 (merged)
✅ B38 FastMarchingImageFilterBase::Solve the zero-speed guard substitutes a finite coefficient, giving zero-speed pixels an arrival time ≈ 2⁵² instead of the old filter's ∞ — speed barriers leak — fixed: #6613 (merged)
✅ B39 ScalarToRGBColormapImageFilter / ColormapFunction NumericTraits<float/double>::min() (smallest positive) used as a "most negative" seed in two places — wrong rescale for non-positive float images, and a ≈FLT_MAX default input range — fixed: #6615 (merged)
✅ B40 MergeLabelMapFilter (KEEP) the deferred-collision deque is never cleared between inputs — with ≥ 3 inputs one LabelObject ends up aliased under two label keys — fix: #6590 (merged)
✅ B41 LabelMapContourOverlayImageFilter (SLICE_CONTOUR) the slice-radius loop tests the write cursor against m_SliceDimension where it means the read index — SliceDimension == 0 draws no contour at allfix: #6590 (merged)
✅ B42 LabelMapMaskImageFilter (Crop) an empty/zero-line object set leaves the bounding-box seeds untouched — signed overflow (UB), then a 2×2 region at index 2⁶³−1 — fix: #6590 (merged)
✅ B43 AttributeUniqueLabelMapFilter the "remove objects without lines" pass is while (it.IsAtEnd()) — inverted, never executes — fix: #6590 (merged)
✅ B44 LabelMap::PushLabelObject on a completely full label range the gap scan finds nothing, skips SetLabel, and silently overwrites the entry at the object's own label instead of throwing "map is full" — fix: #6590 (merged)
✅ B45 StatisticsLabelMapFilter weighted elongation/flatness: no eigenvalue clamp, one shared guard, no ratio > 0 check — an indefinite weighted covariance yields NaN where ShapeLabelMapFilter yields 0 — fix: #6590 (merged)
✅ B46 NormalizedCorrelationImageFilter both divisions unguarded — a constant template or a locally-constant neighborhood produces NaN — fixed: #6612 (merged)
✅ B47 TransformIOBaseTemplate::CorrectTransformPrecisionType find("float") result used unchecked — a transform-type string naming neither precision throws std::out_of_range past every ITK handler — fixed: #6608 (merged)
✅ B48 NiftiImageIO::Read rescale cast buffer sized per voxel, consumed per component — heap over-read for any multi-component image with scl_slope/scl_interfix: #6625 (merged)
✅ B49 NiftiImageIO::Read RescaleFunction is given the voxel count — only the first numElts of a multi-component/complex buffer are rescaled — fix: #6625 (merged)
✅ B50 NiftiImageIO::Read RAS↔LPS conversion never checks the 3-component requirement its own exception text names — 2/4-component vectors get a stride-3 sign flip across voxel boundaries — fix: #6625 (merged)
✅ B53 GiplImageIO::Write INT/UINT images throw "Pixel Type Unknown" only after the target file has been truncated and the header written — destroys existing data, leaves a 256-byte stub — fixed: #6611 (merged)
✅ B54 GiplImageIO::Read success test is !bad(); a short pixel block sets failbit/eofbit, never badbit — truncated file reads as success over an uninitialized tail — fixed: #6611 (merged)
✅ B55 GiplImageIO::Read (compressed) success flag is p != nullptr on the caller's always-non-null output buffer — gzread's byte count is never inspected — fixed: #6611 (merged)
✅ B56 GiplImageIO::Write header image_type is 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)
✅ B57 VTKImageIO::Read ReadBufferAsBinary's bool (false on short read) is discarded — truncated BINARY file reads as success — fixed: #6610 (merged)
✅ B58 VTKImageIO::InternalReadImageInformation every sscanf return is discarded over uninitialized locals — an under-filled DIMENSIONS/SPACING/ORIGIN line sizes the image from indeterminate values (UB) — fixed: #6610 (merged)
✅ B59 VTKImageIO::InternalReadImageInformation attribute dispatch is an ordered substring test — SCALARS vector_field float matches the vector arm and is misparsed as a 3-component VECTORS line — fixed: #6610 (merged)
✅ B60 VTKImageIO ASCII write precision(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)
✅ B61 ImageIOBase::ReadBuffer (ASCII) extraction variable declared outside the loop, stream state never checked — one bad/overflowing field latches into every later component (5 99999 7 8 as short reads [5, 32767, 32767, 32767]) — fixed: #6609 (merged)
✅ B62 VTKImageIO::CanReadFile four GetNextLine calls outside the try block — a .vtk shorter than four lines throws "Premature EOF" through the factory instead of returning false — fixed: #6610 (merged)
✅ B67 NrrdIO per-axis field parsers (vendored) sizes/spacings/thicknesses/axis mins/axis maxs parse dim + 1 values into a NRRD_DIM_MAX(16)-element stack array — a 16-D header with a 17th token is a stack buffer overflow — fixed: #6674 (merged), upstream teem branch bug-nrrd-parse-axis-overflow on sourceforge
✅ B68 NrrdIO nrrdOriginCalculate (vendored) the gotMin loop tests axis[0]->min for every axis (literal 0, not the loop variable) — origins computed from NaN mins, or usable mins ignored — fixed: #6674 (merged), upstream teem branch bug-nrrd-origin-calculate on sourceforge
✅ B69 NrrdIO rnParse_kinds (vendored) the none token arm assigns the axis's centering, not its kindkinds: none … clobbers a previously parsed centers: value — fixed: #6674 (merged), upstream teem branch bug-nrrd-kinds-none-centering on sourceforge
✅ B70 PNGImageIO::WriteSlice the file is opened "wb" — truncating it — before the component-type switch whose default: throws; writing an int16 image destroys any existing .png and leaves it zero-byte — fixed: #6614 (merged)
✅ B72 PNGImageIO::WriteSlice any component count > 4 declares 4 channels (PNG_COLOR_TYPE_RGB_ALPHA) while row pointers advance by the actual count — each row's trailing components are silently dropped — fixed: #6614 (merged)
✅ B73 HDF5ImageIO::WriteImageInformation the C-string meta-data branch calls constCstringObj->GetMetaDataObjectValue() under a guard that admits constCstringObj == nullptr — null dereference for a MetaDataObject<char *>fix: #6626 (merged)
✅ B74 HDF5ImageIO::ReadImageInformation every geometry dataset's length is taken on trust — spacing[i] / Dims[i] are indexed to numDims regardless of the file's actual dataset length, reading past the end of a heap vector before any pixel is touched — fix: #6626 (merged)
✅ B76 JPEGImageIO::WriteSlice the "prefer centimetres" density branch tags the JFIF header density_unit = 0 ("aspect ratio only") while storing real dots-per-cm values — the reader's own density_unit > 0 guard then discards them, so the spacing is lost on a round-trip through ITK — fix: #6627 (merged)
✅ B77 JPEGImageIO::Read the CMYK→RGB conversion assumes all four channels are inverted; that holds for plain CMYK, but for a YCCK source libjpeg un-inverts C/M/Y and passes K through — a white YCCK pixel decodes to black — fix: #6627 (merged)
✅ B78 TIFFImageIO::ReadVolume each kept page is written at its raw directory index while the volume depth is sized from m_SubFiles — one ignored or untagged page before a SUBFILETYPE == 0 page and the last slice is written past the end of the buffer — fix: #6636 (merged)
✅ B79 TIFFImageIO::ReadGenericImage the scanline buffer is sized from the current directory, but width/SamplesPerPixel/BitsPerSample stay pinned to directory 0 — a multi-page file whose later pages are narrower over-reads the buffer — fix: #6636 (merged)
✅ B80 TIFFImageIO::ReadImageInformation the 32-bit arm of the component-type ladder has no default:, and CanRead never constrains SampleFormat — a 32-bit SAMPLEFORMAT_VOID page is read as whatever m_ComponentType already held — fix: #6636 (merged)
✅ B81 TIFFImageIO::ReadGenericImage PHOTOMETRIC_MINISWHITE is read out un-inverted — the // check inverted comment above the plain-copy grayscale arm never grew a body — fix: #6636 (merged)
✅ B82 TIFFImageIO::GetFormat MINISBLACK/MINISWHITE map to GRAYSCALE regardless of SamplesPerPixel, so a 2-sample grey+alpha page is declared 1-component and each row keeps only its first half — fix: #6636 (merged)
✅ B83 TIFFImageIO::InternalWrite PHOTOMETRIC_RGB is written for every component count other than 1 — a 2-component image becomes an RGB-tagged file with SamplesPerPixel = 2 and no EXTRASAMPLESfix: #6636 (merged)
✅ B84 MultiLabelSTAPLEImageFilter label loop counters are typed InputPixelType but bounded by m_TotalLabelCount — a uint8 input using all 256 labels wraps the counter 255 → 0 and InitializePriorProbabilities() never terminates (reproduced as a live hang, not source analysis) — fix: #6607 (merged)
✅ B85 MultiLabelSTAPLEImageFilter::InitializeConfusionMatrixArrayFromVoting the internal seeding LabelVotingImageFilter never receives the outer filter's SetLabelForUndecidedPixels(), so the seed pass always uses automatic label selection regardless of the caller's setting — fix: #6607 (merged)
✅ B86 GiplImageIO::ReadImageInformation / CanReadFile the 256-byte header is consumed as 15 separate gzread/istream::read pairs, 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)
✅ B87 TileImageFilter A 0 in any non-last Layout axis is never validated: with a non-zero last axis it indexes an empty std::vector and runs a ~2³² iteration loop; with a zero last axis (the constructor default) it divides by zero — fix: #6630 (merged)
✅ B88 GDCMImageIO::Read (SINGLEBIT) The 1-bit expansion walks the buffer as an unpadded bitstream, but GDCM pads each row to a byte boundary — every row after the first is decoded from the wrong bits when the width is not a multiple of 8 — fix: #6639 (merged), replacing the reverted #6629
✅ B89 GDCMImageIO::InternalReadImageInformation (ultrasound/hardcopy arm) A single-valued (0028,0030) leaves the second Element slot never written — axis-0 spacing is read from indeterminate heap memory; the assert(GetLength() == 2) guard is vacuous and cannot fire even in a Debug build — fix: #6639 (merged), replacing the reverted #6629
✅ B90 LabelSetErodeImageFilter / LabelSetDilateImageFilter GenerateData only calls AllocateOutputs() and every write is gated on m_Scale[d] > 0; under UseImageSpacing a zero radius on the last axis skips erosion's only output-writing pass — the filter returns uninitialized memory — fix: #6631 (merged)
✅ B92 ImageSeriesReader::GenerateOutputInformation An ITK_ImageOrigin override array shorter than the output dimension is silently shrunk by Array::operator=, and the next loop still indexes to ImageDimension - 1 — unchecked out-of-bounds read. No in-tree ImageIO writes that key, so it is live only for an out-of-tree/custom ImageIOfix: #6632 (merged)
✅ Completed — PR-by-PR triage record (51 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.

Ledger item PR Author State
B10 (MultiLabelSTAPLE tie increment) #6579 @physwkim Merged
B24 (FastMarchingUpwindGradient AllTargets duplicates) #6658 @hjmjohnson Merged
B67–B69, Q84 (vendored NrrdIO via ISC teem fork snapshot) #6674 @hjmjohnson Merged
B18 (FastApproximateRank last axis) #6580 @physwkim Merged
B19 (AttributeMorphologyBase sort range) #6581 @physwkim Merged
B25 (ApplyToImageMetadata null inverse) #6583 @dzenanz Merged
B31 (IterativeInverseDisplacementField stale error bar) #6576 @physwkim Merged
B32 (InverseDisplacementField subsampler direction) #6577 @physwkim Merged; adjacent geometry gaps split to #6582, closed by #6588
B32 follow-up (lattice centering, output-direction API, subsampling guards) #6588 @hjmjohnson Merged — closed #6582
B2, B3, B4, B9, B11, B12, B20, B23, B27, B29 (NaN / UB-cast guards) #6589 @hjmjohnson Merged
B40–B45 (LabelMap / label-statistics correctness) #6590 @hjmjohnson Merged
B1 (ProjectionImageFilter collapsed-axis origin) #6594 @physwkim Merged — baselines uploaded
B13 (ThresholdMaximumConnectedComponents bisection seed) #6595 @physwkim Merged
B14 (ScalarImageKmeans non-contiguous label interval) #6596 @physwkim Merged
B15 (LabelVoting / MultiLabelSTAPLE undecided-label wrap) #6597 @physwkim Merged
B16 (MaskedFFTNormalizedCorrelation integer squaring) #6598 @physwkim Merged
B17 (IterativeDeconvolution ignores OutputRegionMode) #6599 @physwkim Merged
B6 (GridImageSource ignores the image origin) #6600 @physwkim Merged
B7 (MinMaxCurvatureFlow 3-D polar angle) #6603 @physwkim Merged
B8 (CurvatureFlowFunction derivative scale vs radius) #6604 @physwkim Merged
B21 (LevelSetNeighborhoodExtractor zero contour) #6601 @physwkim Merged
B22 (StructureTensor single-axis smoothing) #6602 @physwkim Merged — the 16 AnisotropicDiffusionLBR baselines were regenerated and uploaded
B28 (UniformRandomSpatialNeighborSubsampler infinite Search) #6605 @physwkim Merged
B30 (RegionBasedLevelSetFunction reinitialization term) #6606 @physwkim Merged
B36, B37, B38 (FastMarchingImageFilterBase boundary / topology / zero speed) #6613 @physwkim Merged
B39 (colormap NumericTraits::min as a most-negative seed) #6615 @physwkim Merged
B46 (NormalizedCorrelationImageFilter unguarded divisions) #6612 @physwkim Merged
B47 (TransformIOBase CorrectTransformPrecisionType) #6608 @physwkim Merged
B53–B56 (GiplImageIO write truncation, short-read detection, vector header) #6611 @physwkim Merged
B57–B60, B62 (VTKImageIO short read, sscanf, attribute dispatch, ASCII precision, CanReadFile) #6610 @physwkim Merged
B61 (ImageIOBase::ReadBuffer ASCII stream state) #6609 @physwkim Merged
B70, B72 (PNGImageIO truncates the file before validating) #6614 @physwkim Merged
B71 (PNGImageIO writes only the first slice) @physwkim Withdrawn — not a defect, the contract is documented and tested
B84, B85 (MultiLabelSTAPLE counter wrap, undecided label not propagated) #6607 @physwkim Merged
B48–B50 (NIfTI multi-component rescale over-read) #6625 @physwkim Merged
B73, B74 (HDF5 null metadata cast, unchecked geometry lengths) #6626 @physwkim Merged
B76, B77 (JPEG centimetre density tag, YCCK conversion) #6627 @physwkim Merged
B86 (GIPL header read in one checked call) #6628 @physwkim Merged
B88, B89 (GDCM single-bit packing, single-valued PixelSpacing) #6629 (reverted by #6638), replaced by #6639 @physwkim, @hjmjohnson Merged#6639 decodes 1-bit Pixel Data contiguously per PS3.5 (including the trailing partial byte, which the pre-#6629 code dropped) and counts PixelSpacing values with GDCM's own multiplicity counter, closing the trailing-backslash gap. Verified against pydicom on a real multi-frame SEG file.
B87 (TileImageFilter zero Layout axis) #6630 @physwkim Merged
B90 (LabelSet erode/dilate uninitialized output) #6631 @physwkim Merged
B92 (ImageSeriesReader origin-override out-of-bounds) #6632 @physwkim Merged
B26 (PatchBasedDenoising POISSON step collapse) #6633 @physwkim Merged
B78–B83 (TIFF volume packing, per-page geometry, SampleFormat, MINISWHITE, grey+alpha, 2-component write) #6636 @physwkim Merged
B39 family (NumericTraits::min as a most-negative seed, 5 further sites) #6634 @physwkim Merged
B39 family (entropy threshold calculators) #6635 @physwkim Merged — expected constants and baselines updated
B51 (NIfTI 2-D vector rank), B71 (PNG first slice), B75 (HDF5 memory type) @physwkim Withdrawn — not defects, see the bug table
B5 (CannySegmentationLevelSetFunction speed image) @physwkim Withdrawn — not a defect, see comment below
B33 (ConfidenceConnected empty re-estimation) #6578 @physwkim Merged
Precursors #6569 (JHMI marginals + derivative) @physwkim Merged
#6572 (ScaleLogarithmicTransform Jacobian) @physwkim Merged
❌ Withdrawn — 4 items investigated and found not to be defects (table)
# Site One-line symptom
❌ B5 CannySegmentationLevelSetFunction Propagation weight 0 → advection generated from a never-allocated speed image — not a defect; withdrawn (UpdateOutputInformation resets the empty requested region)
❌ B51 NiftiImageIO spatial-dimension inference for intent-vector images never consults dim[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-identical dim[]; the rank is lost at write time and no read-side change can recover it
❌ B71 PNGImageIO::WriteSlice height comes from GetDimensions(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)
❌ B75 HDF5ImageIO::Read the file datatype is passed as H5Dread's memory type, making the conversion the identity — a VoxelData stored STD_I16BE is copied without a byte swap and read back byte-swapped, silently — not a defect; withdrawn: ReadImageInformation() requires an exact H5Tequal match against a NATIVE_* predtype and throws otherwise, and it is what populates m_VoxelDataSet — so Read() is unreachable with a foreign-endian on-disk type (verified: writing STD_I16BE throws during ReadImageInformation())
How these were found, and what was checked before filing

Each filter was reimplemented from the .hxx sources 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 against main at e46eb723a5: 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).

AI assistance
  • Tool: Claude Code (claude-opus-4-8)
  • Role: found the defects while cross-checking an independent reimplementation of these filters against this source; performed the pre-filing re-verification pass (re-reading every cited line on main at e46eb723a5, checking declared types and constructor defaults, re-deriving each consequence)
  • Contribution: the inventory above
  • Testing: source-level verification against 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.
  • Items whose original analysis did not survive re-verification were corrected or dropped before filing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions