Repository navigation
BUG: Stop iterating images that have no pixel buffer - #7041
hjmjohnson wants to merge 7 commits into
Conversation
dzenanz
left a comment
There was a problem hiding this comment.
This partly duplicates code. I guess it is unavoidable.
3d537d2 to
bb6cb8e
Compare
|
Two force-pushes: |
|
@dzenanz The duplication is gone: the scanline offsets now come from |
[Medium impact] The changes reach registration metrics, optimizers, and image-label filtering. The PR appears safe to merge, with a non-blocking improvement to random-sampling tests. Findings
|
bb6cb8e to
e383983
Compare
e383983 to
2ea9e4c
Compare
|
Two force-pushes: |
…ed image SetupLineOffsets built a ConstShapedNeighborhoodIterator over a PretendImage that is never allocated, only to read the active neighbor offsets. Constructing it offsets the null buffer, undefined behavior that UBSan reports from ConnectedComponentImageFilter and the other scanline label filters. Generate the offsets with ConnectedImageNeighborhoodShape, which needs no image and yields them in the order setConnectivity() and setConnectivityPrevious() produce; the "previous" neighbors are the first half of the symmetric set. Addresses InsightSoftwareConsortium#7039.
…l image ImageToImageMetricv4 never allocates its virtual image, yet the dense GetValueAndDerivative threader, the joint-histogram PDF threader, and REGULAR metric sampling in ImageRegistrationMethodv4 walked it with ImageRegionConstIteratorWithIndex, which computes pixel pointers from the null buffer: undefined behavior that UBSan reports as pointer overflow. All three read only the index, so iterate ImageRegionIndexRange over the same region, which visits the same indices in the same order. Addresses InsightSoftwareConsortium#7039.
…elper Code that samples a region at random without an allocated image, such as the v4 registration virtual domain, has to reproduce the iterator's index draw exactly to keep its samples, and so registration results, unchanged. Move the draw into detail::RandomIndexInRegion, which takes only a generator and a region, and call it from RandomJump. The arithmetic and the generator calls are unchanged. Addresses InsightSoftwareConsortium#7039.
RANDOM metric sampling in ImageRegistrationMethodv4 walked the virtual image with ImageRandomConstIteratorWithIndex, whose every jump offsets the null buffer pointer. Draw the same indices with detail::RandomIndexInRegion, the iterator's own draw, from a MersenneTwisterRandomVariateGenerator created and seeded at the same point as the iterator's. The sampled points, and so registration results, are unchanged. Addresses InsightSoftwareConsortium#7039.
The ANTS neighborhood-correlation threader scanned the virtual image with a ConstNeighborhoodIterator, in both its dense and sparse paths. The metric never allocates that image, so constructing and advancing the iterator offsets a null buffer pointer. The scan reads only indices: the center index, each neighbor's index, whether a neighbor lies inside the buffered region, the window size, and the region start. A small nested iterator provides exactly those from the region and a Neighborhood of the same radius, in the same order, so the correlation sums and the metric are unchanged. Addresses InsightSoftwareConsortium#7039.
… image RegistrationParameterScalesEstimator sampled the metric's virtual image, which is never allocated, with ImageRegionConstIteratorWithIndex for central-region sampling and ImageRandomConstIteratorWithIndex for random sampling. Both offset the null buffer pointer. The estimator runs from ImageRegistrationMethodv4 whenever a scales estimator is set. Iterate ImageRegionIndexRange for the region, and draw random indices with detail::RandomIndexInRegion from a generator created at the same point as the iterator's. The sample points, and so the estimated scales, are unchanged. Addresses InsightSoftwareConsortium#7039.
2ea9e4c to
e964468
Compare
|
Force-push
TestingEvery commit builds with 0 warnings; 999 tests (ITKCommon plus the six affected modules) pass; ANTs' |
Run every code path that reads the never-allocated virtual image of an ImageToImageMetricv4: the MeanSquares, JointHistogramMutualInformation, and ANTS neighborhood-correlation metrics, ImageRegistrationMethodv4 with NONE, REGULAR, and RANDOM sampling, and the PhysicalShift scales estimator with central-region and random sampling. Each checks that the virtual image has no buffer and that the results are finite. Built with -fsanitize=undefined -fno-sanitize-recover=undefined, the test fails if one of these paths iterates the null buffer again. Adds the module's first GoogleTest, so ITKGoogleTest joins its TEST_DEPENDS. Addresses InsightSoftwareConsortium#7039.
e964468 to
e87c410
Compare
Stop building image iterators over images that have no pixel buffer, which offset a null pointer (undefined behavior that UBSan reports as pointer overflow). Each replacement visits the same indices, or draws the same random indices, in the same order, so results are unchanged. Closes #7039.
BUG: Compute scanline neighbor offsets …ScanlineFilterCommon::SetupLineOffsets(connected-component and label filters)BUG: Iterate virtual-domain indices …ImageRegistrationMethodv4ENH: Draw ImageRandomConstIteratorWithIndex positions …detail::RandomIndexInRegion, the iterator's index draw, usable without an imageBUG: Draw RANDOM metric samples …ImageRegistrationMethodv4BUG: Scan the ANTS correlation window …BUG: Sample the scales-estimator virtual domain …RegistrationParameterScalesEstimatorregion and random samplingENH: Test v4 registration paths …What changed
ConnectedImageNeighborhoodShape, which generates the face- or fully-connected offsets without an image, in the same ascending ordersetConnectivity()/setConnectivityPrevious()produce; the "previous" neighbors are the first half of the symmetric set.ImageRegionIndexRangeover the same region.detail::RandomIndexInRegion, whichImageRandomConstIteratorWithIndex::RandomJumpnow also calls, with aMersenneTwisterRandomVariateGeneratorcreated and seeded at the same point as the iterator's, so the drawn indices are the same.ConstNeighborhoodIterator. A small protected nested iterator provides what the scan reads (center and neighbor indices, whether a neighbor is inside the buffered region, the window size, the region start) from the region and aNeighborhoodof the same radius.!ITK_LEGACY_REMOVE && !ITK_FUTURE_LEGACY_REMOVE, because downstream code relies on them transitively (ANTs'antsAI.cxxuses both region iterators without including them).Testing
-fsanitize=undefinedagainst one ITK build: 16 reports at 13 sites withmain's headers, 0 with this branch's, and identical printed values (metric values, derivatives, registration results).antsAI.cxxcompiles against these headers withITK_LEGACY_REMOVE; the new GoogleTest also compiles withITK_LEGACY_REMOVE;pre-commit run --all-filespasses.main): ANTs builds and passes 179/179, BRAINSTools builds and passes 458/458. An earlier run caught ANTs relying on the iterator includes, which is why they stay.AI assistance (Claude Code) found the defect with a UBSan build of BRAINSTools, wrote the reproducer and the changes, and ran the tests; the author reviewed every line.