164 lines
9.0 KiB
Markdown
164 lines
9.0 KiB
Markdown
# 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](Create-Clip.md) with the scene's frame range, which in turn calls [Clone-Video.ps1](Clone-Video.md).
|
|
|
|
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](#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](#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](Clone-Video.md) 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](#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:
|
|
|
|
```text
|
|
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.
|
|
|
|
## Automatic Level Search
|
|
|
|
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:**
|
|
|
|
```powershell
|
|
.\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:**
|
|
|
|
```powershell
|
|
.\Create-Scenes.ps1 -Video P01.mp4 -Level 0.4 -List
|
|
```
|
|
|
|
**Create one clip per scene:**
|
|
|
|
```powershell
|
|
.\Create-Scenes.ps1 -Video P01.mp4
|
|
# Creates: P01-Scene-01.mp4, P01-Scene-02.mp4, ...
|
|
```
|
|
|
|
**Scale and remove audio from every clip:**
|
|
|
|
```powershell
|
|
.\Create-Scenes.ps1 -Video P01 -Level 0.3 -Size 720p -NoAudio
|
|
```
|
|
|
|
**Custom suffix:**
|
|
|
|
```powershell
|
|
.\Create-Scenes.ps1 -Video P01.mp4 -Suffix "-Shot-%03d"
|
|
# Creates: P01-Shot-001.mp4, P01-Shot-002.mp4, ...
|
|
```
|
|
|
|
**Source with a `.skip` marker:**
|
|
|
|
```powershell
|
|
.\Create-Scenes.ps1 -Video P01.skip.mp4
|
|
# Creates: P01-Scene-01.skip.mp4, P01-Scene-02.skip.mp4, ...
|
|
```
|
|
|
|
**Higher-quality clips:**
|
|
|
|
```powershell
|
|
.\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.
|
|
|
|
```powershell
|
|
.\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.
|