BUG: Fix POISSON step-size collapse and Riemannian max seeds in PatchBasedDenoising#6633
BUG: Fix POISSON step-size collapse and Riemannian max seeds in PatchBasedDenoising#6633physwkim wants to merge 3 commits into
Conversation
static_cast<PixelValueType>(0.99999) truncates to 0 for integer pixel types, collapsing the step size to a constant 1e-5 regardless of the output value. Addresses item B26 of InsightSoftwareConsortium#6575.
NumericTraits<T>::min() is the smallest positive value for floating-point T, not the most negative one, so an all-zero or all-negative norm never updates the running maximum. Addresses InsightSoftwareConsortium#6575.
|
| Filename | Overview |
|---|---|
| Modules/Filtering/Denoising/include/itkPatchBasedDenoisingImageFilter.hxx | Updates the POISSON step-size calculation and Riemannian max seeds with type-consistent numeric traits usage. |
| Modules/Filtering/Denoising/itk-module.cmake | Adds the GoogleTest test dependency needed by the new test driver. |
| Modules/Filtering/Denoising/test/CMakeLists.txt | Adds the Denoising GoogleTest source list and driver registration. |
| Modules/Filtering/Denoising/test/itkPatchBasedDenoisingImageFilterGTest.cxx | Adds regression tests for integer POISSON fidelity and constant tensor-image rejection. |
Reviews (1): Last reviewed commit: "BUG: Seed Riemannian max trackers with N..." | Re-trigger Greptile
GAUSSIAN/RICIAN/POISSON read inVal/outVal as PixelValueType, so an unsigned pixel type wraps on subtraction/multiplication before ever reaching RealValueType; this PR's own POISSON fix amplifies the wrapped value instead of masking it. Read as RealValueType instead. Also reseed the Riemannian max trackers with 0, not NonpositiveMin(): squared geodesic differences are non-negative by construction, and a negative seed left sqrt() reachable on a negative value whenever every comparison in the loop was itself a NaN comparison.
|
Force-push adds a follow-up commit fixing integer-domain arithmetic and a NaN path found in review of this PR's own two fixes. What's fixedUnsigned wraparound amplified by this PR's own POISSON fix.
Local validation
I attempted an end-to-end regression test reproducing the NaN with an all-zero tensor background, and confirmed the NaN persists even with this fix — but traced it to a separate, deeper issue: computing the matrix logarithm of a genuinely singular/zero tensor is mathematically undefined ( |
static_cast<PixelValueType>(0.99999)truncates to 0 for any integer pixel type, collapsing the POISSON fidelity step size to 1e-5 for every integer-pixel image; and two running-maximum accumulators are seeded withNumericTraits<T>::min(), the smallest positive float. Addresses B26 of #6575.Root cause
POISSON step cap: the cap is compared against the output value after being cast to the pixel storage type. POISSON constrains pixel values to be non-negative, so for an integer type
std::min(outVal, 0)is always 0 and the step size is a constant regardless of the actual value — the fidelity term is silently defeated. The cap is now applied in the real-valued domain.Riemannian max seeds:
maxNormandm_ImageMaxtrack a running maximum but start atNumericTraits<T>::min(), which for floating-pointTis the smallest positive normal, not the range minimum. An all-zero or all-negative norm never beats the seed, so the tracked maximum sticks at the epsilon.NonpositiveMin()is the correct seed.Local validation
New GoogleTest
itkPatchBasedDenoisingImageFilterGTest.cxxin a new driver.PoissonFidelityAffectsIntegerPixelOutput— integer-pixel image, POISSON, comparingNoiseModelFidelityWeight0 against 1. Fail-before: the two outputs barely differ (sumAbsDiff = 98), because the fidelity term is inert. Pass-after:sumAbsDiff = 1152.ConstantTensorImageRejectedAsNonconstant— an all-identity-tensor image driven through the multithreaded Riemannian min/max pipeline. Fail-before: the filter runs a full denoising iteration on a genuinely constant image, because the epsilon seed keeps max != min. Pass-after: the pre-existing "each image component must be nonconstant" guard fires as intended.ctest -R Denoising: no baseline flipped. The legacy Poisson baseline usesfloatpixels and the legacy tensor baseline is not the degenerate all-identity case, so neither was exposed.AI assistance