Skip to main content

Sharp Frame Extractor PyPI

Sharp Frame Extractor is a command line utility for sampling videos into still images using sharpness scoring. It processes the input in short time windows and writes the highest scoring frame from each window to disk, which is useful for photogrammetry, volumetric capture, and similar pipelines.

Version 2 focuses on:

  • A simpler command line interface
  • Better sharpness scoring
  • Two-pass architecture for memory-efficient analysis and faster processing

Example

frames-all One hundred sharp frames extracted from an iPhone video.

from-to-trees Reconstruction from the extracted frames, compared with the original cherry tree.

More context for this project is on Behance.

Quick start

Install:

pip install sharp-frame-extractor

Extract about 300 sharp frames from a video:

sharp-frame-extractor input.mp4 --count 300

Extract one sharp frame every 0.25 seconds:

sharp-frame-extractor input.mp4 --every 0.25

How it works

The extractor processes the video in consecutive blocks. You choose how blocks are defined:

  • --every SECONDS defines blocks by time (one block per N seconds)
  • --count N defines blocks by targeting a total of about N blocks across the video

For each block, it evaluates frames with a sharpness score and writes only the sharpest frame of that block to disk. Sharpness scoring is based on the Tenengrad focus measure (Sobel gradient energy), a common approach for focus and blur evaluation.

Notes:

  • If an entire block is blurry (fast motion, heavy compression, defocus), the best frame in that block can still be blurry.
  • With --count, the final number is approximate because it depends on video duration and block sizing.

Usage

The command accepts one or more input videos. Choose exactly one sampling mode: --count or --every.

Extract a target number of frames

sharp-frame-extractor input.mp4 --count 300

Use this when you want a roughly fixed number of frames per clip, regardless of duration.

Extract one frame every N seconds

sharp-frame-extractor input.mp4 --every 0.25

Use this when you want a consistent sampling interval across clips.

Multiple videos

sharp-frame-extractor a.mp4 b.mp4 c.mp4 --count 100

Output directory

If you omit --output, results are written next to each input video:

  • ./video.mp4 becomes ./video/frame-00000.png, ./video/frame-00001.png, ...

To write everything into a single base directory (still grouped per input video):

sharp-frame-extractor a.mp4 b.mp4 -o frames --every 2

Outputs:

  • frames/a/frame-00000.png
  • frames/b/frame-00000.png

Performance tuning

By default, the extractor automatically chooses performance settings based on the workload and the available hardware. The options below let you override those defaults when you want more direct control.

There are three main tuning knobs, with two layers of parallelism:

  • -j/--jobs (max_video_jobs) = how many videos are processed at the same time. Each job mainly acts as an orchestrator: it drives frame decoding and hands blocks to the analysis stage.
  • -w/--workers (max_workers) = how many analysis workers run in parallel. Workers are separate processes that perform the CPU intensive sharpness scoring and are shared across all jobs.
  • -m/--memory-limit (memory_limit_mb) = the total memory budget for frame buffers. This limit is split across active jobs, so increasing --jobs reduces the buffer size available per video.

How the pipeline behaves:

  • A job processes a video block by block.
  • Each block needs an available worker to be analyzed.
  • If no worker is available, the job waits and does not keep decoding more blocks.
  • Frame buffering is bounded by the global memory limit, preventing unbounded memory growth when many jobs are active.

Practical guidance:

  • Processing a single video: keep --jobs 1 and tune --workers. This usually controls total throughput.
  • Processing many videos: pick a sensible --workers value first, often close to your CPU core count, then increase --jobs until the workers stay busy. If the CPU is already fully utilized, increasing --jobs will mostly add overhead without speeding things up.
  • If you run many jobs at once and see increased waiting or reduced throughput, consider raising the memory limit so each job has enough buffering.

Example:

sharp-frame-extractor a.mp4 b.mp4 --count 200 -j 2 --workers 6

Output

The extractor writes one image per processed block:

  • frame-00000.png
  • frame-00001.png
  • ...

The index is the block index, not the original frame number.

Help

sharp-frame-extractor --help
Usage: sharp-frame-extractor [-h] [-o DIR] (--count N | --every SECONDS) [-j N] [-w N]
                             [-m MEMORY_MB]
                             VIDEO [VIDEO ...]

Extract sharp frames from a video by scoring frames within blocks. Choose exactly one
sampling mode: --count or --every.

Positional Arguments:
  VIDEO                 One or more input video files.

Options:
  -h, --help            show this help message and exit
  -o, --output DIR      Base output directory. If omitted, outputs are written to
                        "<video_parent>/<video_stem>/". If set, outputs are written to
                        "<DIR>/<video_stem>/". (default: None)
  --count N             Target number of frames to extract per input video. (default: None)
  --every SECONDS       Extract one sharp frame every N seconds. Supports decimals, for example 0.25. (default: None)
  -j, --jobs N          Max number of videos processed in parallel (video jobs). (default: 4)
  -w, --workers N       Total analysis worker processes shared across all video jobs. (default: 8)
  -m, --memory-limit MEMORY_MB
                        Global memory limit for frame buffers in MB (shared across jobs). (default: 52428)
                        
Examples:
  Extract frames by target count:
    sharp-frame-extractor input.mp4 --count 300

  Extract one sharp frame every 0.25 seconds:
    sharp-frame-extractor input.mp4 --every 0.25

  Process multiple videos, outputs next to each input:
    sharp-frame-extractor a.mp4 b.mp4 --count 100

  Write outputs into a single base folder (per input subfolder):
    sharp-frame-extractor a.mp4 b.mp4 -o out --every 2

Migrating from version 1 to version 2

Version 1 remains available on the version-1 branch.

Changes in version 2:

  • New command name and argument layout
  • Updated sharpness scoring
  • Better performance via improved parallelism and worker pool design
# v1
sfextract --window 300 test.mov

# v2
sharp-frame-extractor test.mov --every 0.3
# v1
sfextract --frame-count 30 test.mov

# v2
sharp-frame-extractor test.mov --count 30

If you relied on v1 options such as method selection, cropping, preview, or debug output, keep using version 1 for now.

Development

This project uses uv for environment management and ruff for formatting and linting.

Setup:

uv sync --dev

Autoformat:

make autoformat

About

Copyright (c) 2026 Florian Bruggisser
Released under the MIT License. See LICENSE for details.

Metadata

Release files for sharp-frame-extractor 2.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sharp-frame-extractor 2.0.0
File Size Uploaded
sharp_frame_extractor-2.0.0.tar.gz 27.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sharp-frame-extractor 2.0.0
File Interpreter ABI Platform
sharp_frame_extractor-2.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 58.9 kB

Release files / sharp_frame_extractor-2.0.0.tar.gz

Download URL sharp_frame_extractor-2.0.0.tar.gz
Size 27.0 kB
Tags Source
SHA-256 checksum
How to use checksums
5d9bf3e38da75c88981bf53c97d6687d37a129a78f7e47f352f7ec9ced84a75c
BLAKE2b-256 checksum
How to use checksums
3b8df4561ceb496c4c3455d0cd0d7a149bad82cfa76d6f6a8fefe4903331991a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.7

Release files / sharp_frame_extractor-2.0.0-py3-none-any.whl

Download URL sharp_frame_extractor-2.0.0-py3-none-any.whl
Size 31.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
26be79e4bb240f1f57392b4bf54aacc4a12a72826086624db84f0105c0b6d7e5
BLAKE2b-256 checksum
How to use checksums
c7d2cc85c61c4f78c10bb8df8010b49cbb5a3b7f2b8b8cda93bc7b7a46900bca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.7

Release history Release notifications | RSS feed

This release

2.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page