YT-Frame-Extractor: frames from YouTube videos, without the dark ones
A small Python script that downloads a video from a list of YouTube links and pulls evenly spread random frames, skipping dark ones and the first and last te…
On this page
YT-Frame-Extractor: frames from YouTube videos, without the dark ones
TL;DR
- It is one Python file,
extractor.py, that reads a list of YouTube links fromvideos.txt, downloads each video, and pulls a set number of frames as PNGs into per-video folders, then zips the result. - Two choices make the output usable for a dataset: frames are drawn across the whole video (the first and last tenth are skipped) and a frame is rejected when its mean luminance is below 20% of 255, so black frames do not end up in the set.
- Defaults are hardcoded in
main(): 200 frames per video,framesas the output root, andvideos.txtin the working directory. - The repository's README is a single title line — everything below comes from reading
extractor.py, so treat this as a description of the code, not of documentation.
What it is and who it is for
YT-Frame-Extractor is the kind of script people write once and then keep: a way to build an image dataset from video without sitting through the video with a screenshot key. It depends on pytube, OpenCV and NumPy, and it is small enough to read in one sitting.
It is for the person who has a folder of YouTube links and wants frames for a LoRA or a checkpoint, and for the one who already tried a frame extractor and got three hundred near-identical frames of a title card.
What the code does
- Reads URLs from
videos.txt, one per line. - Downloads each video with
pytube, keeping the progressive MP4 at the highest available resolution, with five attempts and a five-second wait between them. - Divides the video into equal segments and draws one random frame per segment, so coverage is spread across the whole runtime instead of clustered at the start.
- Skips the first and last 10% of frames, which is what keeps an intro sequence, a logo and end credits out of the dataset.
- Checks each candidate's mean luminance in grayscale and retries the draw when the frame is darker than 20% of 255 — the concrete answer to "half my dataset is black frames".
- Writes
frame_{i}.pngwith PNG compression level 0, one thread per frame, then zips the wholeframesdirectory.
How to use it
git clone https://github.com/artificialguybr/YT-Frame-Extractor.git
cd YT-Frame-Extractor
pip install pytube opencv-python numpy
Then create videos.txt with one URL per line and run the script:
python extractor.py
The output lands in frames/<video-name>/frame_0.png and onward, and a ZIP of the whole frames directory is created at the end. num_frames and output_root are set inside main() — 200 and frames — so changing the count means editing the file rather than passing an argument.
What to expect, honestly
There is no command-line interface, no configuration file and no progress reporting beyond a print per video. The number of attempts and the timeout constant live in the code, and a video that fails all five downloads is skipped with a message rather than stopping the run.
Because the frame order follows the segment index, the numbering is a spread over the video rather than a timeline — frame_0.png is not the first frame of the video. That is fine for a training set and confusing for anything else.
The luminance filter is a single mean value, so a frame that is dark but interesting ("a night scene") is rejected while a bright unusable frame can pass. It reduces the worst of the problem, and it does not solve it.
When to use it, when not to
Use it when you need hundreds of frames from a long video for a LoRA dataset, when the video has credits and intros you do not want, or when you want a script small enough to modify for your own filtering rule.
Do not use it for a video you need to sample precisely — the frames are random within segments, so a specific second in the video is not recoverable by index. Do not use it when pytube has broken against a YouTube change either: that dependency is outside the script, and the 2024 code reflects the API of its time.
Sources
- YT-Frame-Extractor — repository — the repository has a README with a title only, so this article reads
extractor.pydirectly for behavior, defaults and dependencies. - extractor.py — download retries, segment sampling, luminance threshold, PNG output, zip step.
Project page: YT-Frame-Extractor
For what to do with the frames afterwards, see automated dataset creation for LoRA training and building a training dataset.
FAQ
Does it need a YouTube API key? No. It uses pytube to download the video, with five attempts and a five-second wait between them.
Why are some frames skipped? The script redraws a frame when its mean grayscale luminance is below 20% of 255, which keeps black and near-black frames out of the dataset.
Which part of the video is used? Segments cover the whole runtime, but the first and last 10% are excluded so intros and credits stay out.
Can I choose how many frames? Only in code: num_frames defaults to 200 and is set inside main(), since the script has no command-line options.