Files

9.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, .webm and .mkv in turn, so P01 resolves to P01.mp4 when that file exists.
  • -Level (optional): Scene-change threshold between 0 and 1.0, rounded to 2 decimals. Lower values are more sensitive and produce more scenes. Default: 0.3.
  • -Scenes (optional): Target number of scenes, a positive integer. The level is searched automatically, starting from -Level, until exactly this many scenes are detected. Every attempt is displayed. If the number cannot be reached, the script stops with exit code 1 before any file is deleted or created. See Automatic Level Search.
  • -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.ps1 and Clone-Video.ps1 (e.g. -Size, -FPS, -NoAudio, -Audio, -CRF, -Metadata, -Reverse, -LTX) is forwarded to every clip. The parameters -Video, -Frames, -Suffix, -Target, -Level and -Scenes are managed by this script and must not be repeated. -Level and -Scenes are never forwarded to Create-Clip.ps1.

Refer to Clone-Video.ps1 for a full description of the encoding parameters.

Requirements

  • ffmpeg and ffprobe must be in the PATH.
  • Create-Clip.ps1 must be in the same directory as Create-Scenes.ps1.

How It Works

  1. Detection: ffmpeg runs the scene score filter with the -Level threshold and reports the timestamp of each detected change.
  2. 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.
  3. 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.
  4. Scene list display: the list is always printed, including with -List, -WhatIf and -Confirm.
  5. Cleanup: unless -List is given, files from a previous run are deleted (see Replacing Existing Clips).
  6. Clip creation: one Create-Clip.ps1 call per scene, with frames start-end and the scene suffix.

Example scene list for a video with two hard cuts at frames 30 and 60:

Index StartFrame EndFrame Frames StartTime EndTime TotalTime Suffix
----- ---------- -------- ------ --------- ------- --------- ------
    1          0       29     30      0.00    0.97      1.00 -Scene-01
    2         30       59     30      1.00    1.97      1.00 -Scene-02
    3         60       89     30      2.00    2.97      1.00 -Scene-03

StartTime and EndTime are in seconds, computed as frame index divided by frame rate. EndTime is the time of the last frame of the scene, so it is one frame before the next scene starts. TotalTime is the scene's duration in seconds: Frames divided by frame rate.

After the list, the script prints Scene level used: <level>, the threshold that produced the list.

Scene Threshold

-Level 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.

With -Scenes, the script searches for a level that gives exactly the requested number of scenes:

  1. It starts at -Level (rounded to 2 decimals) with a step of 0.1.
  2. It detects the scenes and displays them, printed as Pass N: level X -> M scene(s) followed by the list.
  3. If the count matches -Scenes, the search stops.
  4. Too few scenes: the level moves down. Too many scenes: the level moves up. When the direction changes, the step is halved. Each new level is rounded to 2 decimals; the step is not rounded.
  5. The search stops with exit code 1 when the step falls below 0.01, or when the level would leave the range 0 to 1.0. Nothing is deleted or created in that case.

There is no limit on the number of passes. Each pass runs a full detection on the video, so long videos take longer. A count can be unreachable for a video: raising the level can skip a scene and jump past the target, so the search fails with exit code 1.

The level used for the final list is shown on the last line, Scene level used: <level>.

List exactly 4 scenes, starting at level 0.3:

.\Create-Scenes.ps1 -Video P01.mp4 -Scenes 4 -List

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 .skip only 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 -Level value removes the extra clips from the first run.

Examples

List the scenes without creating anything:

.\Create-Scenes.ps1 -Video P01.mp4 -Level 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 -Level 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 -Level to suit the footage, or use -Scenes to search for a level that gives a chosen number of scenes.
  • 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.