BUG: Reset smallestError per pixel in IterativeInverseDisplacementField#6576
Merged
hjmjohnson merged 1 commit intoJul 10, 2026
Conversation
Contributor
hjmjohnson
marked this pull request as draft
July 10, 2026 11:49
IterativeInverseDisplacementFieldImageFilter refines each pixel of the inverse field by coordinate descent, keeping the best probe found so far in "smallestError". That variable was declared once outside the per-pixel loop and only (re)assigned inside the branch that evaluates the error of a pixel's initial guess, which runs only when the pixel's initial mapped point lies inside the input buffer. For a pixel whose initial mapped point falls outside the buffer, that branch is skipped, so smallestError retained the residual left over from the previous pixel. The coordinate-descent probes are individually buffer-guarded, so a probe that lands back inside the buffer was compared against that stale bar and accepted or rejected based on how well an unrelated earlier pixel converged. The inverse field's values at such pixels therefore depended on raster order. Move the declaration inside the per-pixel loop and initialize it to NumericTraits<double>::max(). When the initial guess is not evaluable the error is unknown, so the bar is the worst possible value and the first evaluable probe is accepted rather than measured against a stale error. When the initial guess is evaluable, the existing inside-buffer branch still resets the bar to that guess's error, so behavior there is unchanged. The early-stop test against StopValue is likewise unaffected (default StopValue is 0.0). NumericTraits is already reachable through the itkMath.h include. Add a deterministic regression test that builds a field whose late-raster pixel maps just outside the buffer while a probe steps back inside, and a preceding pixel that converges to a zero residual. With the defect the refined output equals the zero-iteration first guess; with the fix it moves. The test asserts the two differ and that a well-behaved pixel stays finite and near zero. Addresses item B31 of InsightSoftwareConsortium#6575
hjmjohnson
force-pushed
the
bug-iterative-inverse-stale-smallest-error
branch
from
July 10, 2026 11:59
5528d27 to
503e774
Compare
hjmjohnson
approved these changes
Jul 10, 2026
hjmjohnson
marked this pull request as ready for review
July 10, 2026 13:30
physwkim
added a commit
to physwkim/sitk-rs
that referenced
this pull request
Jul 12, 2026
… pixel double smallestError = 0 is declared outside the per-pixel loop and only reassigned inside the IsInsideBuffer branch, so a pixel whose initial mapped point falls outside the buffer inherits the previous pixel's residual as the bar its probes must beat -- the output depends on raster order. Reset it to f64::MAX at the top of every pixel (upstream fix PR InsightSoftwareConsortium/ITK#6576), so an outside-start pixel searches from a neutral bar. Filed as #6575 comment item B31.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Addresses item B31 of #6575.
IterativeInverseDisplacementFieldImageFilterdeclares the coordinate-descent residual barsmallestErroroutside the per-pixel loop and reassigns it only when a pixel's initial mapped point lies inside the input buffer. A pixel whose initial mapped point falls outside the buffer therefore inherits the previous pixel's residual as the value its probes must beat: probes that land back inside the buffer are accepted or rejected based on how well the previous pixel converged, so the inverse field's values at such pixels depend on raster order.The fix moves the declaration inside the per-pixel loop, initialized to
NumericTraits<double>::max(): the bar a probe must beat is the error of this pixel's current guess when that guess is evaluable (the existing inside-buffer branch still sets it), and "unknown — accept any evaluable probe" otherwise. Behavior for pixels whose initial guess is evaluable is unchanged.The new regression test builds a 24×24 field where the pixel before the affected one in raster order converges to a zero residual (poisoning the stale bar) and the affected pixel's first guess maps just outside the interpolation buffer, with an in-buffer probe available one step away. Unfixed, the probe loses to the stale zero bar and the pixel never moves off its zero-iteration first guess; fixed, the descent accepts the probe and refines the estimate.
Verified locally (Linux, GCC 13, Release): the new test fails on unmodified
main(moved by 0) and passes with the fix; all 24 ITKDisplacementField and 12 ITKRegionGrowing tests pass with the fix applied.PR Checklist
AI assistance