Skip to content

[cli] Add save-images -i/--interval to base image count on scene length - #588

Open
Rober1208 wants to merge 1 commit into
Breakthrough:mainfrom
Rober1208:feat/save-images-interval
Open

Rober1208 wants to merge 1 commit into
Breakthrough:mainfrom
Rober1208:feat/save-images-interval

Conversation

@Rober1208

Copy link
Copy Markdown

Closes #280

Adds -i/--interval to save-images, as discussed in #280, so the number of images per scene depends on its length:

scenedetect -i video.mp4 save-images -i 2s          # one image per 2 seconds of each scene
scenedetect -i video.mp4 save-images -i 2s,3        # at least 3 images per scene
scenedetect -i video.mp4 save-images -i 2s,,10      # at most 10 images per scene
scenedetect -i video.mp4 save-images -i 2s,3,10     # between 3 and 10 images per scene

Each scene gets floor(scene length / DURATION) + 1 images, clamped to [MIN, MAX] (default 1 and no limit; an empty MIN or MAX uses its default). The images are chosen the same way as with -n, so the first and last frames are included and the frame margin applies. DURATION takes the same formats as -m/--frame-margin: frames (24), seconds (1.5s), or a timecode (00:00:01.500).

Design notes

  • Single argument: I went with your suggestion of one option that takes 1–3 values. Click options can't take a variable number of values, so MIN and MAX are separated by commas: -i 2s,1,10. The config file accepts the same format (spaces after the commas are fine): interval = 2s, 1, 10. If you'd rather have separate --min-images/--max-images options, that's an easy change.
  • -n and -i are mutually exclusive: setting both on the command line is an error, and so is setting both num-images and interval in the config file. An option given on the command line takes precedence over the config file: -n overrides interval, and -i overrides num-images.
  • API: save_images() gets interval, min_images and max_images arguments. To reject num_images and interval together, the default for num_images is now None, which means 3 unless interval is set. Existing calls behave the same. min_images and max_images are rejected without interval.
  • With varying counts, the $IMAGE_NUMBER padding and the progress bar total use the actual image counts. save-html already handles a different number of images per scene.
  • A very small interval can give more images than a scene has frames, so some frames repeat. Setting a large -n does the same, so I left it alone.
  • I left out computing the interval automatically from the shortest and longest scenes, as mentioned in Dynamically adjust the number of output images of the scene #280.

Tests

  • tests/test_output.py: image counts per scene for frame, second and timecode intervals, with and without MIN/MAX, on both the threaded and non-threaded paths; invalid arguments raise ValueError.
  • tests/test_cli.py: parsing of DURATION[,MIN[,MAX]] and its error messages; -i end to end with each format and with MIN/MAX; -n with -i and invalid values fail with the expected message; the interval config option, including invalid values, the num-images conflict, and precedence between the command line and the config file. The new error checks run in-process.

docs/cli.rst matches the output of docs/generate_cli_docs.py for save-images, and scenedetect.cfg and the changelog are updated.

🤖 Generated with Claude Code

With `-i DURATION[,MIN[,MAX]]`, each scene gets floor(length / DURATION) + 1
images instead of a fixed `-n`, clamped to [MIN, MAX] (default 1, no limit;
an empty MIN or MAX uses the default, e.g. `-i 2s,,10`). The images are
chosen the same way as with `-n`, including the frame margin. `-n` and `-i`
are mutually exclusive, on the command line and in the config file.

The API gets `interval`, `min_images` and `max_images` arguments on
`save_images()`. `num_images` now defaults to None (3 unless `interval` is
set) so that setting both can be rejected, and `min_images`/`max_images`
are rejected without `interval`.

Closes Breakthrough#280

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Copilot AI balanced review requested due to automatic review settings October 22, 2026 01:51

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Breakthrough Breakthrough left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this looks great! Appreciate you taking the time to kick this off.

I left a few comments - would like to understand the tradeoffs a bit better between the different CLI approaches we already discussed. I would be okay landing this as-is, so feel free to push back if you think this is the right way to go.

If possible, can you look into how other projects handle cases like this and if there's a standardized/recommended way to specify options such as this on a command line tool? Hopefully the comments and concerns I added are useful, let me know if you have any questions.

metavar="DURATION[,MIN[,MAX]]",
default=None,
type=click.STRING,
help="Generate one image per DURATION of each scene instead of a fixed number, so longer scenes get more images. Each scene gets floor(length / DURATION) + 1 images, chosen the same way as with -n/--num-images. MIN and MAX limit the number of images per scene (default: 1, no limit). Leave MIN empty to only set MAX (-i 2s,,10). DURATION can be specified in frames (-i 24), in seconds with `s` suffix (-i 1.5s), or timecode (-i 00:00:01.500). Cannot be used with -n/--num-images.{}".format(

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to support leaving MIN empty? Can we require it be declared? I'm assuming if we don't specify it just defaults to 1. I'm also curious, can we use nargs here or does that not work because of the subcommands?

E.g. it would be cool if we made -i take 1/2/3 args conditionally using spaces as separators rather than requiring commas. Not sure how much effort that would be though, so happy to punt on this if it becomes a rabbit hole - just curious as to your thoughts.

Context: It's possible in some locales timecodes use commas, so eventually we might need to support that. The key word being eventually though, which is why for today I'd say this is optional, but I do want to explore what options we have and the tradeoffs for each before shipping something.

duration_secs = max((end - start).seconds, 0.0)
# The epsilon keeps a scene that is a whole number of intervals long from getting one
# image less due to floating point error.
scene_images = max(math.floor(duration_secs / interval_secs + 1e-9) + 1, min_images)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use a named constant for this (we might have an epsilon value declared elsewhere as well).

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dynamically adjust the number of output images of the scene

3 participants