Give Hermes recognitions, not just photos: faces(), tracking ids, discovery
Hermes asked for structured detections instead of a bare JPEG. faces() returns the
faces in view with no photo: a persistent track id, bbox, detection confidence, how
long the person has been in view, and identity {name, similarity} or null, never a
made-up name. look() returns that same JSON as text plus the photo (send_image can
skip it); list_people() gives names and counts; robot://status and two prompts make
the server discoverable, where list_resources and list_prompts were empty. The
detector now tracks faces across frames (FaceTracker) and publishes the track id and
the identity as a second hypothesis.
Deliberately not built: embeddings in responses, age/gender attributes, auto-updating
face data, and a remote enroll tool (a prompt could make the robot call anyone "Kyle").
Checked end to end: a real Hermes turn on the local ornith model called faces() (0.03 s,
no image) and answered that Kyle was there at 0.78 similarity. Also: the camera is
calibrated (18 of 24 views, 0.296 px, outlier rejection added) and the calibration is
kept in pi/calibration and restored by the installer.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AFN8nRRCiDiMN7ChBpHJhs
This commit is contained in:
@@ -2,7 +2,8 @@ import cv2
|
||||
import numpy as np
|
||||
import yaml
|
||||
|
||||
from hermes_vision.calibrate import board_points, camera_info_yaml, novel_enough, solve, view_signature
|
||||
from hermes_vision.calibrate import (board_points, camera_info_yaml, coverage, novel_enough, solve, solve_robust,
|
||||
view_signature)
|
||||
|
||||
SIZE = (1296, 972)
|
||||
K_TRUE = np.array([[1100.0, 0, 650.0], [0, 1090.0, 480.0], [0, 0, 1]])
|
||||
@@ -72,3 +73,43 @@ def test_find_board_detects_a_real_rendered_checkerboard_and_not_a_blank_frame()
|
||||
corners = find_board(bgr, 9, 6)
|
||||
assert corners is not None and corners.reshape(-1, 2).shape == (54, 2)
|
||||
assert find_board(np.full_like(bgr, 128), 9, 6) is None
|
||||
|
||||
|
||||
def test_k3_is_held_at_zero_by_default_but_can_be_fitted():
|
||||
obj, views = synthetic_views()
|
||||
_, _, d = solve([obj] * len(views), views, SIZE)
|
||||
assert d[4] == 0.0
|
||||
_, _, d_full = solve([obj] * len(views), views, SIZE, fix_k3=False)
|
||||
assert d_full[4] != 0.0
|
||||
|
||||
|
||||
def test_coverage_names_the_regions_the_board_never_reached():
|
||||
w, h = SIZE
|
||||
centre_only = [np.random.default_rng(1).uniform([w * 0.4, h * 0.4], [w * 0.6, h * 0.6], (54, 2)).astype(np.float32)]
|
||||
frac, empty = coverage(centre_only, SIZE)
|
||||
assert frac < 0.2 and 'top-left' in empty and 'bottom-right' in empty and 'centre' not in empty
|
||||
rng = np.random.default_rng(2)
|
||||
everywhere = [rng.uniform([0, 0], [w, h], (200, 2)).astype(np.float32)]
|
||||
assert coverage(everywhere, SIZE) == (1.0, [])
|
||||
|
||||
|
||||
def test_blurred_outlier_views_are_dropped_and_the_result_stays_accurate():
|
||||
obj, views = synthetic_views(n=40, seed=9)
|
||||
rng = np.random.default_rng(5)
|
||||
noisy = [v + rng.normal(0, 0.15, v.shape).astype(np.float32) for v in views] # ordinary jitter
|
||||
bad = [v + rng.normal(0, 2.0, v.shape).astype(np.float32) for v in views[:4]] # four blurred views
|
||||
allv = noisy + bad
|
||||
rms_all, k_all, _ = solve([obj] * len(allv), allv, SIZE)
|
||||
rms, k, d, keep = solve_robust([obj] * len(allv), allv, SIZE)
|
||||
assert set(range(len(noisy), len(allv))) <= set(range(len(allv))) - set(keep) # the four bad ones are gone
|
||||
assert len(keep) >= len(noisy) - 2 # the ordinary ones are kept
|
||||
assert rms < rms_all and rms < 0.3
|
||||
assert abs(k[0, 0] - 1100) / 1100 < 0.02
|
||||
|
||||
|
||||
def test_it_never_drops_below_the_minimum_number_of_views():
|
||||
obj, views = synthetic_views(n=14, seed=4)
|
||||
rng = np.random.default_rng(1)
|
||||
allv = [v + rng.normal(0, 1.0, v.shape).astype(np.float32) * (i % 2) for i, v in enumerate(views)]
|
||||
_, _, _, keep = solve_robust([obj] * len(allv), allv, SIZE, min_views=len(allv) - 1)
|
||||
assert len(keep) >= len(allv) - 1
|
||||
|
||||
Reference in New Issue
Block a user