Populations and denominators
These names recur across every endpoint, and getting them wrong is the easiest way to compute a confident wrong answer.
On the cleanliness response these reconcile exactly, and it is worth asserting:
clips_analyzed and clips_measured are different populations, and the second
is not a rounding error away from the first. Anything computed over one must
not be divided by the other.
clips_analyzed is also per endpoint, because each names the run that
answered it. Vocabulary and cleanliness resolve their runs independently, so
their clips_analyzed are two facts about two runs and need not agree. Compare
them only when the echoed run tokens say they came from the same measurement.
segments_unassessed and segments_unusable are published rather than
absorbed because they are the honest edge of the answer. A corpus with a large
count in either is one where the shares describe a smaller slice of the footage
than the clip count suggests.
Vocabulary
The corpus described in three sections:environments (where the footage was
shot), objects (what is handled), actions (what is done).
Terms
A term is a published concept, not a raw string. Independent passes over the footage produce wordings that mean the same thing; those are merged, andspellings lists every variant that folded into the published term. This is
why term matching in GET /api/quality/clips is evidence-based: any spelling
that was merged finds the term, and a spelling nothing ever produced finds
nothing.
kind is the coarser category the term groups under, absent when the term is
its own kind. It is what makes distinct_kinds auditable rather than asserted
— you can re-derive the kind count from the term table.
clip_count and occurrences both report how many distinct clips carry the
term, counted once however many passes agreed on it.
How many independent passes had to agree before a term was published is not
on the wire. That describes the filter, and the filter is ours. What you get
is the surviving vocabulary and the evidence behind each of its members.
Counting a vocabulary
Four counts, and they answer four different questions.distinct_terms — published terms that carry a tier. The plain count of
what is in the corpus. It rises with how much footage was read, so it is not
comparable across datasets.
effective_distinct_terms — the repetition-corrected count. Formally
exp(H) over the occurrence distribution, where H is Shannon entropy: a
Hill number of order one, the “effective number of species” of ecology, the
perplexity of the distribution.
Read it as a count, because it is measured in terms and behaves like one. Ten
evenly-used terms give back exactly 10.0. Two hundred kitchen clips plus one
glimpse of a garage give back 1.06, not 2. A corpus naming 134 objects
where four of them account for nearly every clip is not as rich as one naming
134 evenly, and this is the number that can tell them apart.
rarefied_distinct_terms — distinct_terms interpolated down to a fixed
common depth, stamped in comparable_depth_clips. This is the only count
here that may be set beside another dataset’s. A raw term count keeps rising
with how much footage was read and that curve never flattens, so comparing two
raw counts compares sampling effort as much as content. It is null when the
corpus is thinner than the common depth, because rarefaction interpolates and
must not extrapolate — an estimate for a depth never reached would be a guess
wearing a number’s clothes.
distinct_kinds — how many coarser categories the published terms fall
into. Answers “how varied is this corpus” where the term count answers “what is
in it”.
Tiers
Each section’s terms are partitioned into tiers. For objects and actions these are ordinal — they rank how much dexterity handling or performing the term demands, so a higher tier is a harder thing and not merely a different one. For environments they are not: the tiers there are collection settings, which are categories with no order between them. One wire shape, two meanings, and only the first can be summed into anything resembling a difficulty. The response enumerates every tier, including ones no term landed in, with its stable wire name, its display label, its share of the section’s occurrences, and its most frequent members. Reading the labels and the examples from the response is the intended way to learn the scheme; it means the tier set cannot drift away from this page.untiered_terms lists published terms that could not be placed. They are in
neither distinct_terms nor the shares, which is why the term table can be
longer than the counts imply. Every published term is either counted in
distinct_terms or named in untiered_terms — that identity is worth
asserting in your own pipeline.
Cleanliness
An exhaustive partition of the decoded frames into three categories, plus the denominators behind it. The endpoint decodes nothing and calls no model; it reduces measurements already stored.usable_frames + distorted_frames + jerky_frames == frames_measured, exactly.
The three _share fields are derived from that same denominator and are absent
— never three zeros — when nothing was measured.
Two derivations that change how the counts read:
- Distortion takes precedence. A frame that is both unreadable and abrupt
is counted
distorted, neverjerky, so the two categories never double-count andjerkymeans “legible but abrupt”. - A frame that could not be measured is
distorted, notusable. A frame with no measurable focus or brightness carries no image information, and the conservative direction is to flag it rather than let it inflate the usable share. The first frame of a clip has no predecessor and so is neverjerky, though it is still judged on its own terms.
classification_version is an opaque token naming the rule that produced
the partition. Two partitions carrying the same token were produced by the same
rule and can be compared directly. Two carrying different tokens cannot, and a
corpus should be re-read rather than re-interpreted when the token moves.
The token exists precisely so that comparability is available without the
cutoffs being published — which is the only thing the cutoffs would have told
you, and publishing them would let a reader re-partition a corpus into whatever
answer suited them.
Hand activity
Footage-weighted shares overmeasured_seconds. The measure is defined in
terms of the camera wearer, so it says something only about egocentric footage.
“Camera-wearer” is load-bearing. Another person’s hands in frame are not the
wearer’s and do not count, nothing is inferred from a hand that is out of frame
or behind an object, and any amount of visibility counts — fingertips included.
Visibility is presence, not use.
two_hands_visible is a strict subset of hands_visible: one-hand and
two-hand stretches are recorded as disjoint spans and summed. Manipulation is
observed independently of visibility, so no containment holds between them in
either direction, and you should not expect
active_manipulation ≤ hands_visible to be true of every corpus.
Every share is null rather than 0.0 when measured_seconds is zero.
Clips
One row per clip: counts and seconds throughout, never shares. A share commits you to our denominator; counts let you re-aggregate over any subset — one operator, one site, one week — which is the reason this endpoint exists beside the corpus-level ones.clip is the clip’s identity: the source file and the span within it. No
renumbering changes it, so it is the join key to use between pages, between
endpoints, and against your own records.
analysed_secs is a duration of footage assessed, not an offset into the
source.
episode_index is the position the clip had when analysed, and is
absent when two passes disagreed about it. A corpus renumbered between
passes means either number would address somebody else’s footage, so neither is
published. Join on clip.
frame_quality and hand_activity are opt-in, via
frame_quality=true and hand_activity=true. They are booleans rather than
addresses because they add columns to rows the vocabulary has already chosen.
Reading a run token
Every metric response echoes the run that answered it —vocabulary,
analysis, frame_quality. You need not supply one: name a dataset and you
get its most recent measurements.
The tokens are opaque and exist for exactly one purpose: telling two answers
apart. The same token in two responses a week apart means the corpus was not
re-measured between them and the numbers are directly comparable. A different
token means it was, and any drift you see is real change plus re-measurement,
not one or the other.
Do not pin a token. It addresses one stored run, and a re-measurement will
leave a pinned integration reading an answer that no longer reflects the
corpus.