Skip to content

fix(imq): correct FOCUS_SCORE overflow + vet 4 image-quality features vs analytic oracle - #394

Open
darkclad wants to merge 2 commits into
PolusAI:mainfrom
darkclad:main-imq
Open

fix(imq): correct FOCUS_SCORE overflow + vet 4 image-quality features vs analytic oracle#394
darkclad wants to merge 2 commits into
PolusAI:mainfrom
darkclad:main-imq

Conversation

@darkclad

Copy link
Copy Markdown

FOCUS_SCORE and LOCAL_FOCUS_SCORE were dominated by an unsigned-integer overflow: focus_score.cpp's laplacian() multiplied PixIntens (unsigned) pixels by the negative Laplacian kernel weights, so e.g. -4 wrapped to ~4.29e9 and FOCUS_SCORE reached 2.81e18. Cast the pixel to double before the multiply -> FOCUS_SCORE 12.109, LOCAL_FOCUS_SCORE 2.681.

Vet 4 of the 6 imq features against an independent numpy/scipy reimplementation of their documented algorithms (tests/vetting/oracles/gen_imq_analytic.py; reproduces every value exactly):

  • FOCUS_SCORE = variance of the Laplacian magnitude (scipy convolution) = 12.109
  • LOCAL_FOCUS_SCORE = same on the top-left h/2 x w/2 tile / 4 = 2.681
  • MIN_SATURATION = fraction of pixels at the image minimum = 18/96 = 0.1875
  • MAX_SATURATION = fraction of pixels at the image maximum = 16/96 = 0.16667
    imq 0/6 -> 4/6.

The other two stay regression with documented reasons (oracle_coverage.csv notes):

  • POWER_SPECTRUM_SLOPE: 0.0 on this fixture (rps() early-returns when min(h,w)/8 < 3), and its radial binning uses the FFT value as the radius index instead of the frequency radius -- a real bug; needs a rewrite + a >=24 px fixture.
  • SHARPNESS: correct (all-double DOM sharpness port, no bug); needs a reference-DOM oracle.

gtest 717/717; pytest 72 passed (7 arrow fails are the tiff-only local build; the pytest imq tests check the ImageQuality class runs, not feature values). Coverage 604 -> 608 / 758.

… vs analytic oracle

FOCUS_SCORE and LOCAL_FOCUS_SCORE were dominated by an unsigned-integer overflow: focus_score.cpp's
laplacian() multiplied PixIntens (unsigned) pixels by the negative Laplacian kernel weights, so e.g.
-4 wrapped to ~4.29e9 and FOCUS_SCORE reached 2.81e18. Cast the pixel to double before the multiply
-> FOCUS_SCORE 12.109, LOCAL_FOCUS_SCORE 2.681.

Vet 4 of the 6 imq features against an independent numpy/scipy reimplementation of their documented
algorithms (tests/vetting/oracles/gen_imq_analytic.py; reproduces every value exactly):
  - FOCUS_SCORE       = variance of the Laplacian magnitude (scipy convolution) = 12.109
  - LOCAL_FOCUS_SCORE = same on the top-left h/2 x w/2 tile / 4 = 2.681
  - MIN_SATURATION    = fraction of pixels at the image minimum = 18/96 = 0.1875
  - MAX_SATURATION    = fraction of pixels at the image maximum = 16/96 = 0.16667
imq 0/6 -> 4/6.

The other two stay regression with documented reasons (oracle_coverage.csv notes):
  - POWER_SPECTRUM_SLOPE: 0.0 on this fixture (rps() early-returns when min(h,w)/8 < 3), and its
    radial binning uses the FFT value as the radius index instead of the frequency radius -- a real
    bug; needs a rewrite + a >=24 px fixture.
  - SHARPNESS: correct (all-double DOM sharpness port, no bug); needs a reference-DOM oracle.

gtest 717/717; pytest 72 passed (7 arrow fails are the tiff-only local build; the pytest imq tests
check the ImageQuality class runs, not feature values). Coverage 604 -> 608 / 758.
Comment thread src/nyx/features/focus_score.cpp Outdated
// Cast the pixel to double before multiplying: image is PixIntens (unsigned),
// so the negative kernel weights (e.g. -4) were converted to a huge unsigned
// value, wrapping the Laplacian (focus score reached ~1e18 from the overflow).
out[i* n_image + j] += (double)image[ii * n_image + jj] * kernel[ikFlip * n_kernel + jkFlip];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use static_cast. If not now then open a PR where we port ALL C style casts to C++ safe casts.

// FOCUS_SCORE = variance of the Laplacian magnitude, vetted vs scipy (gen_imq_analytic.py).
// Was 2.810e18: focus_score.cpp multiplied PixIntens (unsigned) by the negative kernel weights,
// wrapping the Laplacian; now cast to double.
double truth_value = 12.109375;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this the oracle value or regression value? In either case why did it change?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's scipy oracle. This is resolved for me.

@vjaganat90 vjaganat90 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. With the view that we will have a PR which fixes all the C-style casting redundant keywords in the codebase.

Address review on PR 394: replace C-style cast with static_cast<double>,
and tag oracle references as 'Oracle: <tool>' in test comments and the
coverage CSV so they read unambiguously. Also restores upstream's CRLF
line endings for oracle_coverage.csv rows outside the IMQ family, which
had been flattened to LF, inflating the diff.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants