The method
How one recording becomes usable clips
This page uses “segmentation” in exactly one sense: the cut of one continuous upload into task-bounded Clips, made at QA time. Two other things in our own records carry the same word, and the first section separates all three.
Three things carry this word. This page means one.
The cut described here takes one Recording — a single continuous upload from a Capture Partner — and produces the Clips inside it, each bounded to one task. It runs at QA time, after redaction and before anything reaches a Commissioning lab. Everything below describes that cut and nothing else.
The other two senses are real parts of our system, and confusing them with this one would misread what we have built. We name them here so that a reader never has to guess which is meant.
The distinction matters to a buyer for a plain reason: the first sense decides how many Clips a Recording yields and how many of its seconds are recorded as accepted work, the second decides what the annotation says happened inside a Clip, and the third does not exist here at all.
- The cut of one Recording into Clips. This page. It writes time boundaries and mints new Clips, each one carrying the seconds it came from.
- Temporal action segmentation inside a single Clip. Verb-noun spans in the exported annotation. On our published reference Clip those spans come from a template rather than from a model reading the video, and the artifact labels its own caption source as a stub. We record it as a defect and publish it beside the data.
- Per-pixel masks — a label on every pixel of every frame. We have not built this. Nothing in our exporter emits one, and no page of ours offers it.
The cut is classical, and the output says so
One decode pass reads the redacted copy of the Recording. Each sampled frame becomes greyscale, shrinks to 160×90, and is compared with the frame sampled before it; the mean absolute difference between the two, normalised to a 0–1 scale, is that moment's activity score. Sampling runs at four frames a second by default, which is an analysis rate rather than the frame rate of anything we deliver.
Two further measurements come off the same pass, read from the full-size greyscale frame. Mean luminance records how much light reached the sensor. The variance of the Laplacian is a standard sharpness measure, and it falls when a frame smears under motion. Both are stored per span, as numbers, beside the verdict they informed.
cv2 and numpy are the only third-party libraries this module imports; everything else it reaches for is our own code and the standard library. We run no cloud model on this footage, and no model of any kind is wired into this path today. Every result carries the string opencv_motion_v1 in a method field, so any span we ever show can be traced back to the thing that produced it.
A hook is reserved for a small local model that could confirm or overrule the classical spans. Nothing is plugged into it. If something ever is, the method string on the result changes to say so, which is why the string exists — it is one string per pass, describing what actually ran.
We chose arithmetic over a model here for a reason that is written down as a decision rather than a preference. D61 refused sending this footage to a cloud video service on two grounds: it would put unconditional external spend in front of the point where the rest of the pipeline deliberately stops, and it would hand partner footage to a processor our privacy notice does not name. Our own rule is that any processor holding partner footage is named in that notice before the first byte reaches it, and the sharing clause today names buyers, payment processors and our email provider. No model provider is on it.
The spans it writes, and where they are recorded
A span is a run of activity with a start second, an end second, a duration, a usable flag, a reason, and the three measurements behind it. Runs are joined across a quiet gap shorter than 1.5 seconds by default, so a Capture Partner who pauses to reposition their hands has one task counted once rather than twice.
A span ends at its last active sample, never at the end of the tolerated gap. That is a correction, not a design: the first live run of this stage found spans running on past the last activity, because the whole tolerated gap was being carried into the usable seconds at the end of every run. Accepted span seconds are the quantity our pay record is keyed on — each minted Clip's record carries its own span's duration, not the parent Recording's — so seconds of quiet counted as work would have gone into somebody's hours in the wrong direction. What those seconds are worth is a separate question our records leave open, and the record refuses to state an amount until it is answered.
Dead time between runs is written as its own span, with its own reason, rather than dropped. The summary the pass writes therefore accounts for the whole Recording, including where we judged that nothing was happening — which is a different statement from silence.
Every usable span becomes a Clip. Each Clip is written to the origin record at the moment it is minted, carrying the parent recording id, its position in the sequence, and the exact start and end seconds it was cut from. That table refuses UPDATE and DELETE by database trigger, so a later stage — or a contributor who has not read the file — cannot quietly re-point a Clip at different seconds.
Each Clip inherits the Recording's consent receipt. Consent is granted over the upload, and a withdrawal covers all of it, so a Clip that did not carry the parent's receipt would be footage our deletion path could not find. Older rows in that table hold no parent id, and that is a fact rather than a gap: it means the upload was the Clip, which was true before we cut anything.
The cut re-encodes rather than copying the stream. A stream copy is faster and can only land on a keyframe, which would start a delivered Clip up to a group of frames before the boundary we measured. Re-encoding costs processor time and buys an exact edge, and every downstream number is derived from that edge. The writer carries no audio track, and every cut file is checked for one before it is returned — a bystander conversation in someone's kitchen is personal data exactly as a face is, and this is the point in the pipeline where that track reliably stops existing.
Minting a Clip does not start the rest of the pipeline. Each new Clip job is written already stopped, before hand tracking, and stays there until a person releases that cohort by name. Hand tracking, captioning and export are deferred work under D49, and deferring them is what stops us spending on footage nobody has commissioned.
max(fps × 4, 60) window over a five-verb round-robin, and QA is nowhere in the call path (FS-4.3). Drawn from reference-run.json.frame 0frame 1842
| # | Frames | Seconds | Hand | Verb |
|---|---|---|---|---|
| 1 | 0–119 | 0.0–4.0s | left | grasp_object |
| 2 | 120–239 | 4.0–8.0s | left | organize_items |
| 3 | 240–359 | 8.0–12.0s | both | wipe_surface |
| 4 | 360–479 | 12.0–16.0s | both | inspect_component |
| 5 | 480–599 | 16.0–20.0s | both | place_item |
| 6 | 600–719 | 20.0–24.0s | left | grasp_object |
| 7 | 720–839 | 24.0–28.0s | left | organize_items |
| 8 | 840–959 | 28.0–32.0s | both | wipe_surface |
| 9 | 960–1079 | 32.0–36.0s | left | inspect_component |
| 10 | 1080–1199 | 36.0–40.0s | both | place_item |
| 11 | 1200–1319 | 40.0–44.0s | both | grasp_object |
| 12 | 1320–1439 | 44.0–48.0s | left | organize_items |
| 13 | 1440–1559 | 48.0–52.0s | both | wipe_surface |
| 14 | 1560–1679 | 52.0–56.0s | both | inspect_component |
| 15 | 1680–1799 | 56.0–60.0s | both | place_item |
| 16 | 1800–1842 | 60.0–61.4s | both | grasp_object |
What is not automatic
Nothing here reports zero when it has not measured. Four conditions make the stage raise and retry instead: OpenCV not loading, the file not opening, no frame rate reported, and no readable frames decoded. A fifth path, a missing file, raises before any of that. A crashed analysis and a genuinely empty Recording would otherwise look identical from the outside, and only one of them belongs to the Capture Partner.
A different thing happens when the pass completes and finds no usable span at all: the Recording is held for a person to look at. It is neither rejected nor failed, and the hold is deliberately not an ending — the job records that it is waiting, and no Clip is minted, so nothing from that Recording enters the record accepted work is counted in. We built it this way because a segmenter fault and an unusable hour of footage are indistinguishable from here, and settling one of them at zero on a machine's word would take somebody's day from them.
The thresholds are ours, and we say so rather than presenting them as properties of the footage. Three seconds of minimum span length, 1.5 seconds of gap tolerance, an activity threshold of 0.012, a minimum mean brightness of 30 and a minimum Laplacian variance of 80 are values we chose. Each span therefore stores its raw scores alongside the verdict, so a threshold argued differently next month can be re-applied to footage we have already processed without decoding it again.
Darkness is judged before length, and that ordering came out of a mistake we made. Footage shot in poor light produces a small frame-to-frame difference whatever the person is doing, so on a deliberately dark fixture the activity threshold called a genuinely busy Recording empty from end to end. The measurement was correct and the reason was wrong. Those two reasons ask for opposite things back — one says the work did not count, the other says add a lamp — and a reason is only worth recording if it is the one somebody could act on. Brightness is now decided first, on the evidence that actually detects it.
What we have not measured
Nobody has cut a set of real recordings by hand and compared our boundaries against theirs. Until that comparison exists, the usable percentage this stage emits is a threshold applied to a measurement, and we label it derived rather than measured everywhere it appears. It is not an acceptance rate and must not be read as one. The three underlying scores — activity, luminance, sharpness — are measured, and they are published alongside the verdict for that reason.
We have not timed this pass on its own. The nearest cost figure our records carry benchmarks a heavier classical stack that includes a hand landmarker this module does not import, at a sampling rate this module does not use, so it is not a measurement of what runs here and we do not publish it as one.
The pipeline that runs this stage — ordering, retries, the stop before hand tracking, and the child jobs SEGMENT mints — is covered by a suite of 70 automated checks, re-run on 05-09-2026. That suite substitutes a fake stage for the frame analysis, and the frame analysis itself has no committed test suite of its own: it has been driven end to end by hand, once, on a 30-second recording with known boundaries, and that run is recorded in our functional spec rather than in anything that re-runs. We would rather write that down than let a passing orchestration suite be read as a validated segmenter.
The reason attached to an unusable span is stored, not shown. No Capture Partner screen renders it today, and a span that is not usable never becomes a Clip, so it does not appear in the list of clips a partner sees. That gap is ours to close, and naming it here is the only honest thing to do with it while it is open.
No commissioned brief has been recorded against yet, so we hold no distribution of span lengths, no reject-reason breakdown across real cohort footage, and no figure for how often a Recording is held for review. None of those numbers exists, and none of them is on this page.