Spaces:
Running
Running
File size: 3,205 Bytes
518343a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | /**
* Perception envelope — uniform per-frame structured result across detector heads.
*
* Re-expressed from Human.js's single-pipeline-multi-head architecture (see
* docs/research/perception-bio-synthesis-2026.md §1). The envelope is the
* source-of-truth for receipt class `perception.envelope.v1`. Absence of a
* detection in a head that ran ≠ absence of a head that ran at all — the
* `ranHeads` / `skippedHeads` split makes that distinction observable.
*/
export type HeadName =
| 'face'
| 'body'
| 'hand'
| 'gesture'
| 'iris'
| 'emotion'
| 'object'
| 'person';
/** Normalised bounding box `[x, y, w, h]` in image-relative coords (each ∈ [0, 1]). */
export type NormalisedBox = readonly [number, number, number, number];
export interface Keypoint {
/** Semantic name (e.g. `'left-eye'`); pipeline-defined namespace. */
readonly name: string;
/** Image-relative `[x, y]`. */
readonly position: readonly [number, number];
/** Per-keypoint confidence ∈ [0, 1]. */
readonly score: number;
}
export interface Detection {
/** Per-detection confidence ∈ [0, 1]. */
readonly score: number;
/** Normalised box (image-relative). */
readonly box: NormalisedBox;
/** Optional keypoints (head-dependent). */
readonly keypoints?: readonly Keypoint[];
/** Detector head's model version (e.g. `'blazeface@1.0.4'`). */
readonly modelVersion: string;
/** Optional class label (e.g. emotion name). */
readonly label?: string;
}
export interface LivenessSummary {
/** Aggregate liveness confidence ∈ [0, 1]. Higher = more real. */
readonly livenessConfidence: number;
/** Human-readable reasons for the score (e.g. `'blink-detected'`). */
readonly livenessReasons: readonly string[];
/** Window of frames considered (millis). */
readonly windowMs: number;
}
export interface DetectionsSummary {
readonly counts: Readonly<Record<HeadName, number>>;
}
export interface PerceptionEnvelope {
/** Stable hash of the frame this envelope was computed from. */
readonly frameHash: string;
/** Heads that actually executed on this frame. */
readonly ranHeads: readonly HeadName[];
/** Heads that were intentionally skipped (budget, opt-out). */
readonly skippedHeads: readonly HeadName[];
readonly face: readonly Detection[];
readonly body: readonly Detection[];
readonly hand: readonly Detection[];
readonly gesture: readonly Detection[];
readonly object: readonly Detection[];
readonly person: readonly Detection[];
readonly liveness: LivenessSummary;
readonly detectionsSummary: DetectionsSummary;
/** Wall-clock budget consumed for this envelope (millis). */
readonly budgetMs: number;
/** Artifact that consumed the envelope (for receipt provenance). */
readonly consumerArtifact: string;
}
export function summariseDetections(envelope: Omit<PerceptionEnvelope, 'detectionsSummary'>): DetectionsSummary {
return {
counts: {
face: envelope.face.length,
body: envelope.body.length,
hand: envelope.hand.length,
gesture: envelope.gesture.length,
iris: 0,
emotion: 0,
object: envelope.object.length,
person: envelope.person.length,
},
};
}
|