refactor(cv): read thresholds from runtime tuning + load from NVS on boot

cv_process and helpers (frame_diff, extract_blob, find_centroids) now read
diff_thresh, min_blob_px, max_move, max_missed, and line_offset from
state.tuning instead of file-scope static const constants. The four
thresholds are promoted to file-local constexpr defaults in cv.cpp
(CV_DEFAULT_*) and are no longer part of the public cv.h API — external
code can't depend on them.

cv_process signature drops the line_pct parameter; callers use
state.tuning.line_offset instead. This eliminates the drift hazard of
having two sources of truth (DeviceConfig.line_offset vs
CVTuning.line_offset); the former is deleted.

main.cpp now calls config_load_tuning(g_cv.tuning) after cv_init on boot
so previously persisted tuning survives reboot; logs whether tuning came
from NVS or defaults.

The legacy NVS key "line_offset" is intentionally left alone — harmless
and flash_device.py may still write it during provisioning. Migration is
out of scope.

Tests: 12/12 passing (11 existing + 1 new
test_cv_process_respects_runtime_min_blob proving the runtime-read path).
Flash: 1,414,069 bytes (89.9%).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-16 15:55:35 -07:00
parent a992bfe391
commit 94d74e425c
6 changed files with 93 additions and 51 deletions

View File

@@ -7,11 +7,6 @@ static const int CV_W = 96;
static const int CV_H = 96;
static const int CV_PIXELS = CV_W * CV_H;
static const uint8_t CV_DIFF_THRESH = 30;
static const int CV_MIN_BLOB_PX = 64;
static const float CV_MAX_MOVE = 15.0f;
static const int CV_MAX_MISSED = 10;
struct CVTrack {
int id;
float x, y;
@@ -46,5 +41,5 @@ struct CVResult {
};
void cv_init(CVState& state);
CVResult cv_process(CVState& state, const uint8_t* frame, uint8_t line_pct);
CVResult cv_process(CVState& state, const uint8_t* frame);
void cv_reset_counts(CVState& state);