// AR-002 — minimum face size, in original video resolution. // // TRACES: AR-002 | SR-002 | UT-002 // // Tier T1 here, T2 in test_replay_fixtures.cpp. The requirement is arithmetic on // bounding boxes, so the two edge cases that matter — a face sitting exactly on // the threshold, and the same face seen through a downscaled decode — are // reachable without a detector, a model or a GPU. What the fixture check adds is // that the rule was actually applied on the way to a dump; what this adds is that // it is applied *correctly*, which no real dump can demonstrate because real // footage does not contain a 39.999 px face on demand. // // FaceDetectorFunc is never constructed: its constructor loads SCRFD. Only the // static rule is called, so make_face_detector() is never odr-used and nothing // here needs a backend. #include #include "nodes/face_detector_node.hpp" #include "types.hpp" #include namespace { DetectedFace box(float w, float h) { DetectedFace f; f.bbox = cv::Rect2f(10.f, 10.f, w, h); f.confidence = 0.9f; return f; } // Sizes the rule kept, in the order given. std::vector surviving_widths(std::vector faces, float min_face_px, float bbox_upscale) { FaceDetectorFunc::drop_undersized(faces, min_face_px, bbox_upscale); std::vector out; for (const auto& f : faces) out.push_back(f.bbox.width); return out; } constexpr float kMin = 40.f; // Config::min_face_px default, and AR-002's number } // namespace // ── Exactly at the threshold ───────────────────────────────────────────────── // The boundary case is the whole content of a minimum: "40x40" has to mean 40 is // admissible, or the requirement says 41. TEST_CASE("a face exactly at the minimum is kept", "[detector][AR-002]") { CHECK(surviving_widths({box(kMin, kMin)}, kMin, 1.f).size() == 1); } TEST_CASE("a face one tenth of a pixel under the minimum is dropped", "[detector][AR-002]") { CHECK(surviving_widths({box(39.9f, 100.f)}, kMin, 1.f).empty()); CHECK(surviving_widths({box(100.f, 39.9f)}, kMin, 1.f).empty()); } TEST_CASE("both sides must clear the minimum, not the larger one", "[detector][AR-002]") { // A wide, short box has enough pixels and is still unusable: ArcFace // alignment needs both dimensions. Area would admit this; the rule must not. CHECK(surviving_widths({box(400.f, 20.f)}, kMin, 1.f).empty()); } TEST_CASE("the filter is a filter, not a reordering", "[detector][AR-002]") { auto kept = surviving_widths( {box(80.f, 80.f), box(10.f, 10.f), box(60.f, 60.f), box(39.f, 39.f)}, kMin, 1.f); REQUIRE(kept.size() == 2); // Order is load-bearing downstream (AR-003's largest-first sort tie-breaks on // it, and the Hungarian solver tie-breaks on index) — erase-remove must not // shuffle the survivors. CHECK(kept[0] == 80.f); CHECK(kept[1] == 60.f); } // ── The dense_scale interaction the requirement exists for ─────────────────── // dense_scale 0.5 halves the decoded frame, so the detector reports a 40 px face // as 20 px. If the threshold were applied to those numbers, turning on a // throughput knob would silently double the minimum face size the pipeline // accepts — a recall change with no line in the config to explain it. AR-002 // pins the minimum to the ORIGINAL resolution instead. TEST_CASE("at dense_scale 0.5 the cutoff stays 40 px of original footage", "[detector][AR-002]") { constexpr float kUpscale = 2.f; // frame_source_node: 1 / dense_scale // 20 px in downscaled space is exactly 40 px of original footage: kept. CHECK(surviving_widths({box(20.f, 20.f)}, kMin, kUpscale).size() == 1); // 19.9 px downscaled is 39.8 px original: dropped. CHECK(surviving_widths({box(19.9f, 19.9f)}, kMin, kUpscale).empty()); // And the interaction stated as one claim: a face of a given original size is // admitted or refused identically whether or not the decode was downscaled. for (float original : {30.f, 39.f, 40.f, 41.f, 80.f}) { INFO("original size " << original); const bool full = !surviving_widths({box(original, original)}, kMin, 1.f).empty(); const bool dense = !surviving_widths({box(original / kUpscale, original / kUpscale)}, kMin, kUpscale).empty(); CHECK(full == dense); CHECK(full == (original >= kMin)); } } TEST_CASE("an absent or degenerate upscale falls back to the raw threshold", "[detector][AR-002]") { // bbox_upscale is 1 on every non-dense frame; 0 would mean the frame source // never set it. Dividing by that would reject every face in the film, which // is a failure worth not having. CHECK(surviving_widths({box(kMin, kMin)}, kMin, 0.f).size() == 1); CHECK(surviving_widths({box(39.f, 39.f)}, kMin, 0.f).empty()); }