Skip to content

fix: use eigh instead of eig for blob orientation/ellipse moments - #25

Open
petercorke wants to merge 2 commits into
mainfrom
fix/blobs-orientation-eigh
Open

fix: use eigh instead of eig for blob orientation/ellipse moments#25
petercorke wants to merge 2 commits into
mainfrom
fix/blobs-orientation-eigh

Conversation

@petercorke

Copy link
Copy Markdown
Owner

Summary

  • Image.blobs() computed the blob's second-moment matrix eigendecomposition with np.linalg.eig, the general non-symmetric solver. That matrix is always real-symmetric, but for degenerate cases (a square blob, or any near-circular blob where both eigenvalues are equal — e.g. Image.Squares(...)) eig can return complex128 output purely from floating-point noise. np.arctan2() then raised TypeError, crashing blobs() outright for these shapes.
  • Switched to np.linalg.eigh, the correct solver for symmetric matrices, which always returns real output.
  • Added a regression test (test_orientation_square_blob_degenerate_moments) documenting the failure mode. The existing TestBlobScalarProperties::test_orientation also already exercised this via the Image.Squares(1, ...) fixture and was failing before this fix on this machine (Apple Silicon / Accelerate BLAS) — may not have surfaced on CI depending on the LAPACK backend there.
  • Found via unrelated docs work: Image.blobs() had no generated docs page before a separate fix restored it, so its live doctest example had never actually executed during a docs build, and this crash went unnoticed.

Test plan

  • pytest tests/ — 816 passed, 0 failed, 15 skipped (pre-existing skips)
  • Reproduced the crash directly against main before the fix, confirmed it's gone after

Blob.orientation (and a/b, the equivalent-ellipse axes) computed the
eigendecomposition of the second-moment matrix with np.linalg.eig. That
matrix is always real-symmetric by construction, but eig is the general
non-symmetric solver: for near-degenerate cases (e.g. a square or
near-circular blob, where both eigenvalues are equal) it can return
complex128 eigenvalues/eigenvectors purely from floating-point noise,
even though the true eigenvalues are real. That then made
np.arctan2(x[1], x[0]) raise TypeError, crashing Image.blobs() outright.

np.linalg.eigh is the correct solver for symmetric matrices and always
returns real output, so it doesn't have this failure mode.

Found via the existing image_class.rst sidebar fix: Image.blobs() had no
docs page before, so its live example never executed during a docs
build and this crash went unnoticed.
@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 complexity · 0 duplication

Metric Results
Complexity 0
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Pull in #26's ci.yml fix so this PR's own workflow file (loaded from
this branch's head, not main, for pull_request-triggered runs) actually
tests against opencv4 instead of the unpinned conda-forge opencv5 drift.
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.

1 participant