Video Frame Extractor for AI โ€” No Blur, No Duplicates avatar

Video Frame Extractor for AI โ€” No Blur, No Duplicates

Pricing

from $0.70 / 1,000 frame extracteds

Go to Apify Store
Video Frame Extractor for AI โ€” No Blur, No Duplicates

Video Frame Extractor for AI โ€” No Blur, No Duplicates

Turn videos into clean image datasets for AI: frames every N seconds, keyframes or one per scene. Skips blurry and near-duplicate frames automatically. Timestamps + sharpness manifest, JPEG/PNG/WebP, ZIP. Pay per frame kept.

Pricing

from $0.70 / 1,000 frame extracteds

Rating

0.0

(0)

Developer

Leandro Zanatta

Leandro Zanatta

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

14 hours ago

Last modified

Categories

Share

Video Frame Extractor for AI โ€” Clean Image Datasets (No Blur, No Duplicates)

Turn videos into clean image datasets for annotation, model training and computer vision pipelines. Extract frames every N seconds, only keyframes or one per scene, and the Actor automatically skips blurry frames and near-duplicates (moments where nothing moved), so you don't pay to store, label or train on the same picture hundreds of times.

Every frame comes with its timestamp, size and a sharpness score, ready for Label Studio, CVAT, Roboflow, a vector database or your own training code.

25 sampled frames: 15 kept, 3 skipped as blurry, 7 skipped as duplicates; scene mode finds 3 shots

Real output of this Actor on a 12-second test clip with a motion-blurred stretch and a frozen ending. Footage: Qviri and Minh Nguyen, CC BY-SA 4.0, Wikimedia Commons.

Why use it

  • ๐Ÿงน Cleaner datasets, less labeling: plain "one frame per second" exports are full of blurry and identical images. Annotators waste time on them and models overfit to them. This Actor filters both out before you ever see them.
  • ๐Ÿ’ธ You only pay for useful frames: skipped frames are free, so cleaning the dataset also lowers the bill.
  • ๐ŸŽฏ Keeps the motion that matters: the duplicate filter measures how much of the picture changed, so a car crossing a static street scene is kept while the empty street is not.
  • ๐ŸŽž๏ธ Three sampling strategies: fixed interval, keyframes (fastest) or scene change (one image per shot).
  • ๐Ÿงพ Training-ready manifest: one row per frame with image link, timestamp, width, height and sharpness. Export as JSON, CSV or Excel.
  • ๐Ÿงฉ No FFmpeg or OpenCV setup: send video URLs, get image links and an optional ZIP.

Who uses it

SectorTypical use
AI / ML teamsBuild object-detection, segmentation and classification datasets from raw video
Data labeling companiesPre-filter frames before sending them to annotators (Label Studio, CVAT, Roboflow)
Security & smart citiesSample CCTV and traffic footage for detector training and evaluation
Autonomous driving, drones, roboticsDashcam and drone flights into image sets for perception models
Retail & manufacturingShelf, conveyor and inspection footage into images for quality-control models
Media & researchThumbnails, shot lists and visual indexes of long videos; frames for multimodal LLMs and vector search
Sports & fitnessKey moments from training videos for pose estimation and analysis

How it works

  1. Each video is downloaded and decoded inside the run.
  2. Candidate frames are picked by the sampling mode.
  3. Each candidate gets a sharpness score (variance of the Laplacian). Frames below the blur threshold are skipped.
  4. Each remaining frame is compared with the last kept one; if less than Minimum change (%) of the picture changed, it is skipped as a near-duplicate.
  5. Kept frames are optionally resized, encoded as JPEG / PNG / WebP and stored; one dataset row is written per frame.

Sampling modes

ModeWhat you getBest for
Every N seconds (interval)One candidate every N seconds (0.04 to any value; e.g. 0.5, 1, 10)Datasets with even time coverage
Keyframes only (keyframes)Only the encoder's keyframes (I-frames), usually every 2โ€“10 sFast previews of long videos
Scene change (scene)One frame each time the shot changesEdited videos, ads, movies, shot lists

Input

FieldTypeDefaultDescription
Video URLs (videoUrls)arrayโ€”Direct links to videos. Public Google Drive and Dropbox share links are converted automatically. Up to 4 GB per file.
Sampling mode (mode)stringintervalinterval, keyframes or scene
Every N seconds (everySeconds)string1Time between candidates in interval mode
Scene change sensitivity (sceneThreshold)string0.350.2 = more frames, 0.5 = only big cuts
Skip blurry frames (skipBlurry)booleantrueDrop motion-blurred or out-of-focus frames
Blur threshold (blurThreshold)integer40Minimum sharpness to keep a frame. Every frame's score is in the output so you can tune it
Skip near-duplicate frames (skipDuplicates)booleantrueDrop frames where almost nothing changed
Minimum change (%) (minChangePercent)string0.5Share of the picture that must change since the last kept frame
Max side (px) (maxSide)integeroriginalDownscale so the longest side is at most this (e.g. 640 for YOLO-style models)
Image format (outputFormat)stringjpegjpeg, png or webp
Quality (quality)integer90JPEG / WebP quality (50โ€“100)
Create ZIP (createZip)booleanfalseAlso store one ZIP with all frames of each video
Max frames per video (maxFramesPerVideo)integer200Hard limit for cost control
Max minutes per video (maxDurationMinutes)integer60Only the first N minutes are scanned and billed

Example:

{
"videoUrls": ["https://example.com/camera-01.mp4", "https://example.com/drone-flight.mov"],
"mode": "interval",
"everySeconds": "2",
"maxSide": 640,
"createZip": true,
"maxFramesPerVideo": 500
}

Supported inputs: MP4, MOV, WebM, MKV, AVI and other common containers; H.264, H.265/HEVC, VP8, VP9, AV1 and more.

Output

One dataset row per kept frame:

{
"sourceUrl": "https://example.com/camera-01.mp4",
"videoIndex": 1,
"frameIndex": 4,
"timestampSeconds": 3.017,
"imageUrl": "https://api.apify.com/v2/key-value-stores/.../records/video001-frame-00004.jpg",
"width": 1920,
"height": 1080,
"sharpness": 1234.7
}
FieldMeaning
imageUrlDownload link of the frame image
timestampSecondsPosition of the frame in the video
width, heightSize of the stored image (after Max side)
sharpnessHigher = sharper. Clearly blurred frames score below ~40, typical sharp frames several hundred
videoIndex, frameIndexWhich video and which kept frame
errorPresent only when a video failed (failed videos are free)

A per-video summary is saved in the run's key-value store as SUMMARY-video001:

{ "framesExtracted": 15, "candidates": 25, "skippedBlurry": 3, "skippedDuplicates": 7, "durationSeconds": 12.03, "zipUrl": "https://api.apify.com/v2/key-value-stores/.../records/video001-frames.zip" }

Pricing

Pay per frame kept, plus a small fee per started minute of video scanned, plus a tiny per-run start fee. No subscription. Skipped frames and failed videos are free. Apify plan discounts apply automatically (see the Pricing tab).

ExampleFrames keptMinutes scanned
12-second clip, every 0.5 s (image above)151
10-minute video, every 2 s, mostly static camera~100โ€“30010
1-hour video, scene modeone per shot60

Use Max frames per video, Max minutes per video or the run's maximum cost setting to cap spending; the Actor stops cleanly when the limit is reached.

Use it from code or an AI agent

Python: download a clean dataset

import pathlib, urllib.request
from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("adorable_partial/video-frame-extractor-ai").call(run_input={
"videoUrls": ["https://example.com/camera-01.mp4"],
"mode": "interval", "everySeconds": "1", "maxSide": 640,
})
out = pathlib.Path("dataset/images"); out.mkdir(parents=True, exist_ok=True)
for frame in client.dataset(run["defaultDatasetId"]).iterate_items():
if "imageUrl" in frame:
urllib.request.urlretrieve(frame["imageUrl"], out / f"v{frame['videoIndex']}_{frame['timestampSeconds']:.2f}.jpg")

JavaScript

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('adorable_partial/video-frame-extractor-ai').call({
videoUrls: ['https://example.com/ad.mp4'],
mode: 'scene',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((f) => console.log(f.timestampSeconds, f.imageUrl));

HTTP

curl -X POST "https://api.apify.com/v2/acts/adorable_partial~video-frame-extractor-ai/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"videoUrls": ["https://example.com/camera-01.mp4"], "everySeconds": "2"}'

No-code and agents: use the Apify modules in Make, Zapier or n8n, trigger runs from webhooks or schedules, or let an AI agent call it through the Apify MCP server (for example to give a multimodal LLM a few representative frames of a video).

FAQ

How do I get more or fewer frames? Lower Every N seconds for more frames. Raise Minimum change (%) (e.g. 2โ€“5) to keep only clearly different images, or set it to 0 to disable the duplicate check while keeping the blur filter.

Too many frames marked blurry (or not enough)? Check the sharpness values in the output and set Blur threshold just below the sharp frames you want to keep. Dark, low-detail scenes naturally score lower.

Does it upscale? No. Max side only downsizes.

Can I get every single frame? Set Every N seconds to the frame interval (e.g. 0.04 for 25 fps) and turn off both filters. Mind Max frames per video.

Are faces or license plates blurred? No. If you need anonymized frames, run the related anonymizer Actor on the output images.

Is my data kept? Videos are processed inside your run; frames are stored only in your own Apify storage, under your account's data-retention settings.

Support

Need another sampling strategy or export format (COCO, YOLO folders)? Open an issue in the Issues tab.