7.0 KiB
Create-Scenes.ps1
Detects scene changes in a video with ffmpeg and extracts each scene as a separate clip file. Each scene is created by calling Create-Clip.ps1 with the scene's frame range, which in turn calls Clone-Video.ps1.
The clips are saved next to the source video, named from the source filename and the scene number: P01.mp4 produces P01-Scene-01.mp4, P01-Scene-02.mp4, and so on. A .skip marker stays last: P01.skip.mp4 produces P01-Scene-01.skip.mp4.
Parameters
-Video(required): Path to the input video file. Can be absolute or relative to current directory. If the file does not exist, the script tries appending.mp4,.webmand.mkvin turn, soP01resolves toP01.mp4when that file exists.-Scene(optional): Scene-change threshold between0and1.0. Lower values are more sensitive and produce more scenes. Default:0.3.-Suffix(optional): Suffix for the clip filenames. Must contain one printf-style number placeholder, which is replaced by the scene number starting at 1. Default:-Scene-%02d. See Suffix Placeholders.-List(optional): Only displays the scene list. No clip is created and no existing file is deleted.- Extra parameters: Any parameter accepted by
Create-Clip.ps1andClone-Video.ps1(e.g.-Size,-FPS,-NoAudio,-Audio,-CRF,-Metadata,-Reverse,-LTX) is forwarded to every clip. The parameters-Video,-Frames,-Suffixand-Targetare managed by this script and must not be repeated.
Refer to Clone-Video.ps1 for a full description of the encoding parameters.
Requirements
ffmpegandffprobemust be in thePATH.Create-Clip.ps1must be in the same directory asCreate-Scenes.ps1.
How It Works
- Detection: ffmpeg runs the
scenescore filter with the-Scenethreshold and reports the timestamp of each detected change. - Frame conversion: each timestamp becomes a 0-based frame index using the video's frame rate and start time. Indices at or before frame 0, or at or after the last frame, are ignored.
- Scene list: scene 1 starts at frame 0, and each detected change starts a new scene. Each scene ends one frame before the next change, and the last scene ends at the last frame. With no detected change, the whole video is one scene.
- Scene list display: the list is always printed, including with
-List,-WhatIfand-Confirm. - Cleanup: unless
-Listis given, files from a previous run are deleted (see Replacing Existing Clips). - Clip creation: one
Create-Clip.ps1call per scene, with framesstart-endand the scene suffix.
Example scene list for a video with two hard cuts at frames 30 and 60:
Index StartFrame EndFrame Frames StartTime EndTime Suffix
----- ---------- -------- ------ --------- ------- ------
1 0 29 30 0.00 0.97 -Scene-01
2 30 59 30 1.00 1.97 -Scene-02
3 60 89 30 2.00 2.97 -Scene-03
StartTime and EndTime are in seconds, computed as frame index divided by frame rate.
Scene Threshold
-Scene is passed to ffmpeg's scene score. The score measures how much a frame differs from the previous one, from 0 to 1:
0.3(default): a reasonable starting point for ordinary footage.- Lower values (e.g.
0.1) detect gradual changes, such as slow camera moves, and produce more scenes. - Higher values (e.g.
0.6) only detect hard cuts. 1.0: detects nothing, so the whole video becomes one scene.
Suffix Placeholders
The first printf-style placeholder in -Suffix is replaced by the scene number. The number is zero-padded to the width given in the placeholder:
| Placeholder | Scene 1 | Scene 12 |
|---|---|---|
%d |
1 |
12 |
%02d |
01 |
12 |
%003d |
001 |
012 |
%03d |
001 |
012 |
Other text in the suffix is used as written, so -Suffix "-Shot-%03d" produces P01-Shot-001.mp4. A suffix without a placeholder, such as -Suffix "-Scene", is rejected. A literal %% is not supported.
Replacing Existing Clips
Before the new clips are created, the script deletes earlier clips of the same source. A file is deleted when all of these match:
- It is in the same directory as the source video.
- Its name is the source's base name, then the suffix pattern (any number in place of the placeholder), then
.skiponly if the source had one, then the source's extension. - It is not the source video itself.
Matching is case-insensitive. Files that do not match are never touched. Existing clips are always replaced, so you do not need to delete them first.
| Source | Matches | Does not match |
|---|---|---|
P01.mp4 |
P01-Scene-01.mp4, P01-Scene-12.mp4 |
P01-Scene-01.skip.mp4, P01-Other.mp4 |
P01.skip.mp4 |
P01-Scene-01.skip.mp4 |
P01-Scene-01.mp4 |
Because the deletion happens before the first clip is created, a second run with a higher -Scene value removes the extra clips from the first run.
Examples
List the scenes without creating anything:
.\Create-Scenes.ps1 -Video P01.mp4 -Scene 0.4 -List
Create one clip per scene:
.\Create-Scenes.ps1 -Video P01.mp4
# Creates: P01-Scene-01.mp4, P01-Scene-02.mp4, ...
Scale and remove audio from every clip:
.\Create-Scenes.ps1 -Video P01 -Scene 0.3 -Size 720p -NoAudio
Custom suffix:
.\Create-Scenes.ps1 -Video P01.mp4 -Suffix "-Shot-%03d"
# Creates: P01-Shot-001.mp4, P01-Shot-002.mp4, ...
Source with a .skip marker:
.\Create-Scenes.ps1 -Video P01.skip.mp4
# Creates: P01-Scene-01.skip.mp4, P01-Scene-02.skip.mp4, ...
Higher-quality clips:
.\Create-Scenes.ps1 -Video P01.mp4 -CRF 18
Safe Execution
This script supports -WhatIf and -Confirm. Both apply to the deletion of old clips and to the creation of new ones, and are forwarded to Create-Clip.ps1.
-WhatIf: Shows the scene list, the files that would be deleted, and the clips that would be created. Nothing is changed on disk.-Confirm: Prompts before each deletion and each clip creation.
.\Create-Scenes.ps1 -Video P01.mp4 -WhatIf
Notes
- Frame indices are computed as
timestamp * frame rate. This is exact for constant-frame-rate videos. With variable frame rate, a scene boundary can be off by one frame. - Clips are encoded by
Clone-Video.ps1, so each clip keeps the source's resolution, frame rate and audio unless extra parameters change them. - Clips are written next to the source video, not to the current directory.
- Scene detection is based on visual change only. A cut between two very similar shots may not be detected, and a fast camera move may be detected as a cut. Adjust
-Sceneto suit the footage. - Each clip is created with
Create-Clip.ps1, so a failed clip is reported as a warning and the other scenes are still created. At the end, the script reports how many scenes failed.