Skip to content

Numerical setImage / operator() tests for each feature evaluator#25

Merged
vladiant merged 1 commit into
mainfrom
add_setimage_tests
Apr 29, 2026
Merged

Numerical setImage / operator() tests for each feature evaluator#25
vladiant merged 1 commit into
mainfrom
add_setimage_tests

Conversation

@vladiant
Copy link
Copy Markdown
Owner

Summary

The existing feature tests verified construction, init and feature
counts, but never evaluated a feature on a real image. This MR adds
8 numerical tests that drive the full pipeline of every concrete
evaluator (CvHaarEvaluator, CvLBPEvaluator, CvHOGEvaluator):

init() → setImage() → operator()(featureIdx, sampleIdx)

The tests use synthetic images (constant patches and step edges) and
assert closed-form invariants that hold regardless of which feature
index corresponds to which geometric layout, so they remain meaningful
without hard-coding implementation-specific feature ordering.

Library line coverage rises from 42.4% → 44.4%, driven primarily by
HOGfeatures.cpp going from 28% → 77% (the entire integral
histogram + feature evaluation path was previously untested).

Changes

traincascade/test/test_features.cpp

8 new test cases appended, all following the existing Arrange / Act /
Assert
pattern with comments per step:

Evaluator Invariant exploited Tests added
Haar Flat image → variance = 0 → normfactor = 0operator() short-circuits to 0.0f for every feature constant returns 0 (BASIC), vertical edge produces ≥1 nonzero, ALL mode flat → 0 (also exercises the tilted-integral branch in setImage)
LBP Flat image → all 8 block sums equal cval → all 8 >= cval comparisons true → result 0xFF = 255 constant returns 255, horizontal edge produces ≥1 value <255, two-sample isolation by index
HOG Flat image → all gradients 0 → bin sums fail > 0.001f guard → 0.0f for every variable constant returns 0 across all numFeatures × N_BINS × N_CELLS vars, vertical edge produces ≥1 nonzero

Inner loops use a fail-fast accumulator into a single CHECK so the
doctest assertion counter stays sane (291 total, not 170k+).

Why these invariants?

Each evaluator has a property that holds for any uniform input:

  • Haar uses a normalization factor derived from the image variance.
    On a constant patch the variance is zero, the implementation
    short-circuits, and every feature returns exactly 0. Any non-zero
    output on a textured image therefore proves setImage actually
    populated the integral image and operator() is using it.
  • LBP compares each surrounding 3×3 block sum to the center block.
    Equal sums make every comparison true, producing the maximum bit
    pattern 0xFF. A non-0xFF output on a textured image proves the
    pipeline is working end-to-end.
  • HOG computes per-bin gradient histograms; on a flat image every
    bin is zero and the implementation's res > 0.001f guard returns
    0.0f everywhere. A non-zero output on an edge image proves
    integralHistogram, normalization and Feature::calc are wired up.

Using closed-form invariants instead of golden values keeps the tests
robust against benign reordering of generated features.

Test results

[doctest] test cases:  88 |  88 passed | 0 failed | 0 skipped
[doctest] assertions: 291 | 291 passed | 0 failed |
[doctest] Status: SUCCESS!

ctest: 100% tests passed, 0 tests failed out of 1

Coverage impact (traincascade/lib/)

Metric Before After
Lines 42.4% (1984 / 4676) 44.4% (2076 / 4676)
Functions 77.5% (200 / 258) 79.1% (204 / 258)
Branches 27.7% (1388 / 5006) 28.8% (1442 / 5006)

Per-file gains:

File Before → After
HOGfeatures.cpp 28% → 77%
HOGfeatures.h (inline operator() / Feature::calc) 7% → 100%
haarfeatures.cpp 73% → 75%
lbpfeatures.h (inline Feature::calc) already 100%, now exercised on real data

The HOG jump is the key win: previously no test ever called
CvHOGEvaluator::setImage, so the entire integralHistogram,
gradient-bin accumulation and normalization paths were dark. They are
now covered on both the zero-gradient short-circuit path and the normal
evaluation path.

Risks / Notes

  • All tests use in-memory cv::Mat images — no filesystem I/O, no test
    fixtures.
  • Runtime contribution is well under one second.
  • No changes to library code.

Checklist

  • All 88 tests pass locally (ctest --output-on-failure)
  • AAA pattern with comments in every test
  • Happy paths and degenerate inputs covered (constant images, edges)
  • No new resource files committed
  • No changes to library code

@vladiant vladiant merged commit 973125b into main Apr 29, 2026
7 checks passed
@vladiant vladiant deleted the add_setimage_tests branch April 29, 2026 21:08
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