Install
openclaw skills install jpg-ocr-stat-video-frame-extractionExtract frames from video files and save them as images using OpenCV
openclaw skills install jpg-ocr-stat-video-frame-extractionThis skill enables extraction of individual frames from video files (MP4, AVI, MOV, etc.) using OpenCV. Extracted frames are saved as image files in a specified output directory. It is suitable for video analysis, creating training datasets, thumbnail generation, and preprocessing video content for further processing.
The following Python libraries are required:
import cv2
import os
import json
from pathlib import Path
All extraction results must be returned as valid JSON conforming to this schema:
{
"success": true,
"source_video": "sample.mp4",
"output_directory": "/path/to/frames",
"frames_extracted": 150,
"extraction_params": {
"interval": 1,
"start_frame": 0,
"end_frame": null,
"output_format": "jpg"
},
"video_metadata": {
"total_frames": 300,
"fps": 30.0,
"duration_seconds": 10.0,
"resolution": [1920, 1080]
},
"output_files": [
"frame_000001.jpg",
"frame_000002.jpg"
],
"warnings": []
}
success: Boolean indicating whether frame extraction completedsource_video: Original video filenameoutput_directory: Path where frames were savedframes_extracted: Total number of frames successfully savedextraction_params.interval: Frame sampling interval (1 = every frame, 2 = every other frame, etc.)extraction_params.start_frame: First frame index extractedextraction_params.end_frame: Last frame index extracted (null if extracted to end)extraction_params.output_format: Image format used for saving framesvideo_metadata.total_frames: Total frame count in source videovideo_metadata.fps: Frames per second of source videovideo_metadata.duration_seconds: Video duration in secondsvideo_metadata.resolution: Video dimensions as [width, height]output_files: List of generated frame filenameswarnings: Array of issues encountered during extractionimport cv2
import os
def extract_all_frames(video_path, output_dir):
"""Extract all frames from a video file."""
os.makedirs(output_dir, exist_ok=True)
cap = cv2.VideoCapture(video_path)
frame_count = 0
while True:
ret, frame = cap.read()
if not ret:
break
filename = os.path.join(output_dir, f"frame_{frame_count:06d}.jpg")
cv2.imwrite(filename, frame)
frame_count += 1
cap.release()
return frame_count
import cv2
import os
def extract_frames_at_interval(video_path, output_dir, interval=1):
"""Extract frames at specified intervals."""
os.makedirs(output_dir, exist_ok=True)
cap = cv2.VideoCapture(video_path)
frame_index = 0
saved_count = 0
while True:
ret, frame = cap.read()
if not ret:
break
if frame_index % interval == 0:
filename = os.path.join(output_dir, f"frame_{saved_count:06d}.jpg")
cv2.imwrite(filename, frame)
saved_count += 1
frame_index += 1
cap.release()
return saved_count
import cv2
import os
import json
from pathlib import Path
def extract_frames_to_json(video_path, output_dir, interval=1,
start_frame=0, end_frame=None, output_format="jpg"):
"""Extract frames and return results as JSON."""
video_name = os.path.basename(video_path)
warnings = []
output_files = []
try:
os.makedirs(output_dir, exist_ok=True)
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
raise ValueError(f"Cannot open video: {video_path}")
# Get video metadata
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
fps = cap.get(cv2.CAP_PROP_FPS)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
duration = total_frames / fps if fps > 0 else 0
# Set end frame if not specified
if end_frame is None:
end_frame = total_frames
# Seek to start frame
if start_frame > 0:
cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)
frame_index = start_frame
saved_count = 0
while frame_index < end_frame:
ret, frame = cap.read()
if not ret:
if frame_index < end_frame:
warnings.append(f"Video ended early at frame {frame_index}")
break
if (frame_index - start_frame) % interval == 0:
filename = f"frame_{saved_count:06d}.{output_format}"
filepath = os.path.join(output_dir, filename)
cv2.imwrite(filepath, frame)
output_files.append(filename)
saved_count += 1
frame_index += 1
cap.release()
result = {
"success": True,
"source_video": video_name,
"output_directory": str(output_dir),
"frames_extracted": saved_count,
"extraction_params": {
"interval": interval,
"start_frame": start_frame,
"end_frame": end_frame,
"output_format": output_format
},
"video_metadata": {
"total_frames": total_frames,
"fps": fps,
"duration_seconds": round(duration, 2),
"resolution": [width, height]
},
"output_files": output_files,
"warnings": warnings
}
except Exception as e:
result = {
"success": False,
"source_video": video_name,
"output_directory": str(output_dir),
"frames_extracted": 0,
"extraction_params": {
"interval": interval,
"start_frame": start_frame,
"end_frame": end_frame,
"output_format": output_format
},
"video_metadata": {
"total_frames": 0,
"fps": 0,
"duration_seconds": 0,
"resolution": [0, 0]
},
"output_files": [],
"warnings": [f"Extraction failed: {str(e)}"]
}
return result
# Usage
result = extract_frames_to_json("video.mp4", "./frames", interval=10)
print(json.dumps(result, indent=2))
import cv2
import os
def extract_frames_by_seconds(video_path, output_dir, seconds_interval=1.0):
"""Extract frames at specific time intervals (in seconds)."""
os.makedirs(output_dir, exist_ok=True)
cap = cv2.VideoCapture(video_path)
fps = cap.get(cv2.CAP_PROP_FPS)
frame_interval = int(fps * seconds_interval)
if frame_interval < 1:
frame_interval = 1
frame_index = 0
saved_count = 0
while True:
ret, frame = cap.read()
if not ret:
break
if frame_index % frame_interval == 0:
filename = os.path.join(output_dir, f"frame_{saved_count:06d}.jpg")
cv2.imwrite(filename, frame)
saved_count += 1
frame_index += 1
cap.release()
return saved_count
import cv2
import os
import json
from pathlib import Path
def process_video_directory(video_dir, output_base_dir, interval=1):
"""Process all videos in a directory and extract frames."""
video_extensions = {'.mp4', '.avi', '.mov', '.mkv', '.wmv', '.flv', '.webm'}
results = []
for video_file in sorted(Path(video_dir).iterdir()):
if video_file.suffix.lower() in video_extensions:
video_output_dir = os.path.join(
output_base_dir,
video_file.stem
)
result = extract_frames_to_json(
str(video_file),
video_output_dir,
interval=interval
)
results.append(result)
print(f"Processed: {video_file.name} -> {result['frames_extracted']} frames")
return results
# JPEG format (default, good balance of quality and size)
cv2.imwrite("frame.jpg", frame)
# PNG format (lossless, larger files)
cv2.imwrite("frame.png", frame)
# JPEG with custom quality (0-100)
cv2.imwrite("frame.jpg", frame, [cv2.IMWRITE_JPEG_QUALITY, 95])
# PNG with compression level (0-9)
cv2.imwrite("frame.png", frame, [cv2.IMWRITE_PNG_COMPRESSION, 3])
# Seek by frame number
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
# Seek by milliseconds
cap.set(cv2.CAP_PROP_POS_MSEC, milliseconds)
# Seek by ratio (0.0 to 1.0)
cap.set(cv2.CAP_PROP_POS_AVI_RATIO, 0.5) # Middle of video
def extract_resized_frames(video_path, output_dir, target_size=(640, 480)):
"""Extract and resize frames to specified dimensions."""
os.makedirs(output_dir, exist_ok=True)
cap = cv2.VideoCapture(video_path)
frame_count = 0
while True:
ret, frame = cap.read()
if not ret:
break
resized = cv2.resize(frame, target_size)
filename = os.path.join(output_dir, f"frame_{frame_count:06d}.jpg")
cv2.imwrite(filename, resized)
frame_count += 1
cap.release()
return frame_count
Extract video properties before processing:
def get_video_info(video_path):
"""Retrieve video metadata."""
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
return None
info = {
"total_frames": int(cap.get(cv2.CAP_PROP_FRAME_COUNT)),
"fps": cap.get(cv2.CAP_PROP_FPS),
"width": int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
"height": int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)),
"codec": int(cap.get(cv2.CAP_PROP_FOURCC)),
"duration_seconds": cap.get(cv2.CAP_PROP_FRAME_COUNT) / cap.get(cv2.CAP_PROP_FPS)
}
cap.release()
return info
For extracting frames at exact positions:
def extract_specific_frames(video_path, output_dir, frame_numbers):
"""Extract specific frames by their indices."""
os.makedirs(output_dir, exist_ok=True)
cap = cv2.VideoCapture(video_path)
extracted = []
for frame_num in sorted(frame_numbers):
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_num)
ret, frame = cap.read()
if ret:
filename = os.path.join(output_dir, f"frame_{frame_num:06d}.jpg")
cv2.imwrite(filename, frame)
extracted.append(frame_num)
cap.release()
return extracted
Issue: Video file cannot be opened
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
print(f"Error: Cannot open video file: {video_path}")
print("Check file path, permissions, and codec support")
Issue: Frames read as None
ret, frame = cap.read()
if not ret or frame is None:
print("Failed to read frame - video may be corrupted or ended")
Issue: Codec not supported
# Check if video has valid properties
fps = cap.get(cv2.CAP_PROP_FPS)
if fps == 0:
print("Warning: Could not detect FPS - codec may be unsupported")
Issue: Disk space exhausted
import shutil
def check_disk_space(output_dir, required_mb=100):
"""Check available disk space before extraction."""
stat = shutil.disk_usage(output_dir)
available_mb = stat.free / (1024 * 1024)
return available_mb >= required_mb
Before returning results, verify:
json.loads() to validate)success, source_video, frames_extracted, video_metadata)cap.release()