Shot Detection
Shot detection is the automatic identification of the points in a video where one continuous run of camera footage ends and another begins. A shot is the unbroken span of frames recorded between a camera starting and stopping, or lying between two splices in an already-edited video; shot detection returns those spans as a list of start and end times. Almost every implementation works the same way underneath: score how much each frame differs from the frame before it — in colour distribution, in luminance, or in a learned representation — and declare a boundary where that score crosses a threshold. That makes hard cuts easy to find and gradual transitions such as dissolves, fades and wipes considerably harder, because a dissolve spreads the change over dozens of frames so that no single pair of them differs by much. Despite the name most tools print, it is shot detection rather than scene detection: it finds camera changes, not narrative units.
Shot, Scene, Take: The Words Are Not Interchangeable
A shot is a mechanical unit — one unbroken run of frames from one camera, bounded by the camera starting and stopping or by two splices in an edit. A scene is a narrative unit: the shots that share a place and a time. A take is one attempt at a shot, most of which never reach the edit.
Nearly every tool advertising "scene detection" detects shots. PySceneDetect returns a list it calls scenes; they are shots. The naming is harmless until you act on the output. "Put a transition at each scene change" is a reasonable instruction; applied to a shot list on a two-camera interview it puts a whip pan in the middle of every sentence. Grouping shots into scenes is a genuinely different problem, because it needs an understanding of content rather than a measurement of difference.
How the Measurement Works
The classical algorithm is three steps and has barely changed since the 1990s. Reduce each frame to something comparable. Score the difference between consecutive frames. Declare a boundary where the score crosses a threshold.
1. Reduce the frame
Comparing raw RGB pixels is the naive version (sum of absolute differences), and it fires on every camera move. Better representations throw away what changes for uninteresting reasons and keep what changes at a cut. A colour histogram discards position entirely, so panning across a room barely moves it. HSV separates hue from brightness, so a cloud passing the window is mostly a luma event. The edge change ratio compares the outlines that enter and leave the frame, which survives lighting changes well. A perceptual hash compresses the frame to a few hundred bits via a DCT. All of them are lossy on purpose.
2. Score the difference
PySceneDetect's ContentDetector averages the absolute per-pixel delta in each HSV channel and combines them with weights that default to hue 1.0, saturation 1.0, luma 1.0 and edges 0.0. The result is a number on a 0–255 scale, and the default threshold is 27.0 — roughly a 10% average frame change. Edges are off by default because computing them costs real time on long files.
3. Suppress boundaries that are too close together
Every detector carries a minimum shot length, and PySceneDetect's Python API defaults it to 15 frames. That is a duration expressed in frames, so the same default means 0.25 s at 60 fps, 0.5 s at 30 fps and 0.625 s at 24 fps. Feed the same footage in at two frame rates and the same settings return two different shot lists. The CLI papers over this by expressing the default as 0.6s instead.
Here are the real defaults, across the implementations most tools are built on:
| Detector | What it measures | Default threshold | Min shot |
|---|---|---|---|
| PySceneDetect ContentDetector | HSV frame-to-frame delta (hue + saturation + luma, edges off) | 27.0 on a 0–255 scale | 15 frames |
| PySceneDetect AdaptiveDetector | The same score, compared to a rolling average of its neighbours | ratio 3.0, floor 15.0 | 15 frames |
| PySceneDetect ThresholdDetector | Average RGB intensity — fades to and from black only | 12 | 15 frames |
| PySceneDetect HistogramDetector | YUV luma-histogram correlation, 256 bins | 0.05 | 15 frames |
| PySceneDetect HashDetector | Perceptual-hash Hamming distance (DCT, 16×16) | 0.395 | 15 frames |
| ffmpeg scdet | Its own change score | 10 on a 0–100 scale | none |
| ffmpeg select='gt(scene,X)' | The same idea, different units | user-set, 0.3–0.4 typical, 0–1 scale | none |
| TransNet V2 (neural) | Learned, over a 100-frame window at 48×27 px | a probability, not a delta | learned |
Two rows of that table are the same idea on incompatible scales, and it is the commonest misconfiguration in the field. ffmpeg's scdet filter takes a threshold from 0 to 100 and defaults to 10. The scene variable inside the select filter runs from 0 to 1, and the expression everyone copies is select='gt(scene,0.4)'. Paste that 0.4 into scdet and you have asked for a threshold forty times more sensitive than intended — which returns a boundary every few frames and reads as a broken filter rather than a unit error.
Cuts Are Solved. Dissolves Are Not.
A hard cut is the easy case by construction: one frame belongs to the old shot, the next belongs to the new one, and the difference score spikes in a single step. A gradual transition — dissolve, fade, wipe, iris — is engineered to be the opposite. A one-second dissolve at 30 fps spreads the entire change across 30 frames, so each consecutive pair differs by about 3% of it. That is far below any threshold set high enough to survive an ordinary camera pan, so the detector returns nothing and the two shots are reported as one.
You cannot fix this by lowering the threshold, because you are already between two failure modes. Below roughly 20 on ContentDetector's scale you start firing on handheld motion, on a subject crossing the lens, and on sensor noise in dark footage. Above roughly 35 you start missing real cuts between two shots that happen to look alike — which is exactly the two-camera interview, the most common footage anyone owns.
The field's partial answers are all about looking at more than two frames. PySceneDetect ships a separate ThresholdDetector that watches average frame brightness against a default of 12, so it catches fades to and from black — but not a dissolve between two shots, where brightness never dips. Its AdaptiveDetector compares each frame's score to a rolling average of its neighbours and fires when the ratio exceeds 3.0, with a floor of 15.0 so noise cannot produce a large ratio out of two small numbers; that reframes the question usefully — a cut is a spike relative to its own neighbourhood, a pan is a plateau — but a dissolve is a plateau too.
Neural detectors close most of the remaining gap by seeing the whole event at once. TransNet V2 runs a dilated 3D convolutional network over a sliding window of 100 frames downscaled to 48×27 pixels — 1,296 pixels per frame, which is a deliberate statement that boundary detection is about global change and not about detail — and emits a per-frame boundary probability. Its published F1 scores tell you exactly how much of the problem remains: 96.2 on BBC Planet Earth and 93.9 on RAI, both broadcast material, against 77.9 on ClipShots, which is web video. The eighteen-point gap is what messy real-world footage costs. Shot boundary detection was a formally evaluated task at TRECVID from 2001 to 2007, where gradual transitions were scored with their own metrics — frame-recall and frame-precision — for the reason above: a dissolve has no single correct frame to be right about.
What a Shot List Gives an Editing Agent
A shot list is four numbers per shot at most, and it is disproportionately useful — not because it describes the video, but because it is the only thing that says where the picture is continuous.
- It separates scene changes from jump cuts. After a silence pass, a talking-head video has a junction per removed pause and nearly all of them sit inside one continuous shot. Those are jump cuts and they work by being invisible. Without a shot list, "add transitions" means a full-screen effect every couple of seconds through footage that never changed scene.
- It bounds anything with duration. A zoom, a speed ramp, a text overlay or a punch-in that crosses a cut reads as a mistake, because the viewer sees the effect survive a change it should not have survived. Shot boundaries are the safe start and end points.
- It is the unit for framing and colour. A crop that follows a subject has to reset when the camera changes, and a grade is matched shot by shot. Both are per-shot decisions in every manual workflow, for the same reason.
- It classifies the footage before anything is edited. One shot plus dense speech is a talking head. Forty shots plus almost no speech is montage material that needs a voiceover or music to carry it. Those two want opposite edits, and the shot count is half of what distinguishes them.
- It makes long video navigable. On a 40-minute recording the shot list is a table of contents for the picture, in the same way that word-level timestamps are one for the speech.
What a shot list does not give is the picture. It carries timings, not content — it can say that the camera changed at 41.2 s and cannot say what it changed to. Anything that needs to know what is on screen has to look at frames.
A Scenario Where It Shows
You record a 25-minute interview with two cameras, cut between them in a rough assembly, and export a single file with a half-second dissolve on every camera change. Then you hand that file to a tool and ask for a highlight reel.
The detector reports far fewer shots than you cut. Every dissolve was spread over 15 frames, none of the consecutive pairs crossed the threshold, and adjacent camera angles were merged into one long shot. Meanwhile it reports boundaries you never made: the moment your guest gestured across the frame, the moment the ring light flickered, and a run of six "shots" during a slow zoom, because the score sat just above the threshold for two seconds.
Every downstream decision then inherits both errors. Clip boundaries land mid-dissolve, so a clip opens on a half-faded frame. Transitions are placed at the flicker and skipped at the real angle change. A per-shot grade applies one look across two cameras with different white balance. None of that is a threshold problem; the threshold that would catch the dissolves fires on the gesture. It is the shape of the frame-difference method, and the two real answers are a detector that sees more than two frames at a time, or — far better — detecting shots on the footage before the dissolves were baked into it.
Common Misconceptions
"Scene detection detects scenes."
It detects shots. A scene is a narrative unit spanning many shots, and no frame-difference method has any access to narrative. The label on the output is a naming convention, not a claim about what was measured.
"A missed cut means the threshold is too high."
Sometimes. But if the cut was a dissolve, no threshold finds it — the change was distributed so that no two consecutive frames differ much, and lowering the threshold reaches the camera motion long before it reaches the transition. Missed gradual transitions are a method problem, not a tuning problem.
"Keyframes and I-frames are shot boundaries."
They are not, and this shortcut is tempting because it is nearly free to read them. Encoders place I-frames on a fixed GOP cadence — every 2 to 10 seconds regardless of content — so most of them sit in the middle of a shot. Encoders also place one at a detected scene change when scenecut detection is enabled, which is why the shortcut appears to work on some files and produces nonsense on others. A file encoded with a closed 2-second GOP has an I-frame every 2 seconds and no shot information in it at all.
"More shots detected means better detection."
Recall and precision move in opposite directions here. A detector returning 300 boundaries on a video with 40 cuts has not found more; it has found the same 40 plus 260 camera moves, and every downstream per-shot decision now fires 300 times. Both error directions are expensive, which is why the field reports F1 rather than either alone.
"Minimum shot length is a taste setting."
It is a frame-rate-dependent duration wearing a fixed number. A default of 15 frames is 0.25 s on 60 fps footage and 0.625 s on 24 fps footage, so the same setting is silently more aggressive on cinema-rate material. If a tool ever gives you two different shot lists for what you thought was the same video, check the frame rate before you check the threshold.
"A shot list tells you what is in the video."
It tells you when the camera changed. It contains no description, no subject, no framing and no on-screen text. Treating a list of timestamps as visual understanding is how a tool ends up placing a title card over someone's face — the boundary was right and nothing ever looked at the frame.
How This Works in Valmera
Valmera is an agentic video editor, so shot detection is part of the index the agent edits from rather than a panel you open. Every upload is analysed once and the result is reused by every later edit.
- PySceneDetect ContentDetector at a threshold of 27.0, run at index time on the 540p proxy rather than the master. The downscale averages out sensor noise and is far faster; the boundary timings are the same either way, because a cut is a cut at any resolution.
- The shot list is cut geometry, not the picture. What the agent looks at is a set of labeled frame tiles covering the whole video, refreshed into its context on every message, plus the ability to request frames at exact times. The shot list answers when the camera changed; the frames answer what is there. Keeping those separate is deliberate — the failure it prevents is aiming a zoom or placing text from a timestamp nobody looked at.
- Transitions are bound to real shot changes. The default scope for transitions keeps only the junctions where the two sides come from different indexed shots, or where a clip has been spliced in. If every junction in the edit is a jump cut inside one continuous shot — the normal outcome after a silence pass — the agent does not apply the transition at all, and says why. Setting a transition on every cut is available, but you have to ask for it.
- Shot count feeds how the footage is classified. Dense speech across very few shots is treated as a talking head; many shots with little speech is treated as montage material. That classification changes what the agent proposes before you have asked for anything.
- Failure is visible, not silent. If detection cannot run on a file, the whole video is treated as one shot and the degradation is recorded as a warning rather than presented as a result. One shot is the honest answer when nothing could be measured; it is also the correct answer for a locked-off talking-head recording, and the two look identical unless the failure is reported.
- Readable directly.
get_shotsreturns the boundaries for any time range, in the studio and over the MCP connector, so Claude can ask the same question the in-house agent asks.
The honest limits. Valmera runs content detection only. Gradual transitions already baked into an uploaded file — dissolves, fades, wipes from a previous edit — are not reliably found, for the reason described above, and the shot list carries no label saying whether a boundary was a cut or something softer. The threshold is not exposed as a per-project setting. Shot boundaries are also not editable by hand: if the detector merges two similar camera angles, you cut at the right moment by describing it or by using the timeline, not by correcting the shot list. And there is no shot-list export in an interchange format such as CMX 3600 or FCPXML — the boundaries are readable in the product and over MCP, and the deliverable is a rendered MP4. Valmera also has no true crossfade of its own; its transitions are duration-preserving junction effects, so it neither creates nor detects a real dissolve.
Related Terms
Other entries in the Valmera video editing glossary that touch this one:
- Edit decision list — Where a detected boundary becomes an actual cut — a range in a keep list rather than a change to the file.
- Jump cut — What you get when you cut inside a shot instead of between two. Shot detection is how a tool tells the two apart.
- B-roll — Splicing a clip in creates a new shot boundary that no detector had to find — the edit made it.
- J-cut and L-cut — Deliberately putting the picture boundary and the audio boundary at different times.
- Silence detection — The audio-side sibling: the same threshold-and-minimum-duration shape, applied to level instead of pixels.
- Proxy editing — Why detection normally runs on a small proxy — a downscale removes sensor noise and is an order of magnitude faster.
- Auto-reframe — Reframing is decided per shot, because a crop that follows a subject has to reset when the camera changes.
- Color grading — Grades are matched shot by shot; the shot list is the unit a colourist works in.
Frequently Asked Questions
Let the Agent Read the Shot Structure for You
Upload real footage and describe the edit. Shot boundaries, transcript and frames are indexed once, and transitions land where the picture actually changes. 50 free credits, no card.
Start free →