[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6XEASDFEYEPA9HXD4B9BZ2N":3,"skill-same:01M6XEASDFEYEPA9HXD4B9BZ2N":60},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":22,"publishedAt":34,"linkOnly":35,"markdownContent":36,"markdownBody":37,"frontMatter":38,"refName":39,"commitSha":40,"skillDirectory":41,"forksCount":13,"repositoryHtmlUrl":42,"repositoryHomepageUrl":6,"ownerHtmlUrl":43,"ownerBlogUrl":6,"ownerDescription":44,"updatedAt":45,"install":46,"resources":59},"01M6XEASDFEYEPA9HXD4B9BZ2N","video-frame-extraction",null,"Extract frames from video files and save them as images using OpenCV","Waaaalnut\u002Fskillsbench","Waaaalnut","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F103995656?v=4","01M6XCS9K1V51EY3JMB7DGMX24","01M6XCS9Q4WPW88W62C55G1H44",0,"Apache-2.0","https:\u002F\u002Fgithub.com\u002FWaaaalnut\u002Fskillsbench\u002Fblob\u002Fmain\u002Ftasks\u002Fjpg-ocr-stat\u002Fenvironment\u002Fskills\u002Fvideo-frame-extraction\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000016","图像与多媒体","SCENARIO","使用场景",[23,24,25,26,27,28,29,30,31,32,33],"extraction","Video","frame","video frame extraction","opencv","save","extract","images","files","as","extract frames","2026-11-10 10:12:30",false,"---\nname: video-frame-extraction\ndescription: Extract frames from video files and save them as images using OpenCV\n---\n\n# Video Frame Extraction Skill\n\n## Purpose\nThis 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.\n\n## When to Use\n- Extracting frames for machine learning training data\n- Creating image sequences from video content\n- Generating video thumbnails or preview images\n- Preprocessing videos for object detection or tracking\n- Converting video segments to image collections for analysis\n- Sampling frames at specific intervals for time-lapse effects\n\n## Required Libraries\n\nThe following Python libraries are required:\n\n```python\nimport cv2\nimport os\nimport json\nfrom pathlib import Path\n```\n\n## Input Requirements\n- **File formats**: MP4, AVI, MOV, MKV, WMV, FLV, WEBM\n- **Video codec**: Must be readable by OpenCV (most common codecs supported)\n- **File access**: Read permissions on source video\n- **Output directory**: Write permissions on destination folder\n- **Disk space**: Ensure sufficient space for extracted frames (uncompressed images)\n\n## Output Schema\nAll extraction results must be returned as valid JSON conforming to this schema:\n\n```json\n{\n  \"success\": true,\n  \"source_video\": \"sample.mp4\",\n  \"output_directory\": \"\u002Fpath\u002Fto\u002Fframes\",\n  \"frames_extracted\": 150,\n  \"extraction_params\": {\n    \"interval\": 1,\n    \"start_frame\": 0,\n    \"end_frame\": null,\n    \"output_format\": \"jpg\"\n  },\n  \"video_metadata\": {\n    \"total_frames\": 300,\n    \"fps\": 30.0,\n    \"duration_seconds\": 10.0,\n    \"resolution\": [1920, 1080]\n  },\n  \"output_files\": [\n    \"frame_000001.jpg\",\n    \"frame_000002.jpg\"\n  ],\n  \"warnings\": []\n}\n```\n\n\n### Field Descriptions\n\n- `success`: Boolean indicating whether frame extraction completed\n- `source_video`: Original video filename\n- `output_directory`: Path where frames were saved\n- `frames_extracted`: Total number of frames successfully saved\n- `extraction_params.interval`: Frame sampling interval (1 = every frame, 2 = every other frame, etc.)\n- `extraction_params.start_frame`: First frame index extracted\n- `extraction_params.end_frame`: Last frame index extracted (null if extracted to end)\n- `extraction_params.output_format`: Image format used for saving frames\n- `video_metadata.total_frames`: Total frame count in source video\n- `video_metadata.fps`: Frames per second of source video\n- `video_metadata.duration_seconds`: Video duration in seconds\n- `video_metadata.resolution`: Video dimensions as [width, height]\n- `output_files`: List of generated frame filenames\n- `warnings`: Array of issues encountered during extraction\n\n\n## Code Examples\n\n### Basic Frame Extraction\n\n```python\nimport cv2\nimport os\n\ndef extract_all_frames(video_path, output_dir):\n    \"\"\"Extract all frames from a video file.\"\"\"\n    os.makedirs(output_dir, exist_ok=True)\n    \n    cap = cv2.VideoCapture(video_path)\n    frame_count = 0\n    \n    while True:\n        ret, frame = cap.read()\n        if not ret:\n            break\n        \n        filename = os.path.join(output_dir, f\"frame_{frame_count:06d}.jpg\")\n        cv2.imwrite(filename, frame)\n        frame_count += 1\n    \n    cap.release()\n    return frame_count\n```\n\n### Interval-Based Frame Extraction\n\n```python\nimport cv2\nimport os\n\ndef extract_frames_at_interval(video_path, output_dir, interval=1):\n    \"\"\"Extract frames at specified intervals.\"\"\"\n    os.makedirs(output_dir, exist_ok=True)\n    \n    cap = cv2.VideoCapture(video_path)\n    frame_index = 0\n    saved_count = 0\n    \n    while True:\n        ret, frame = cap.read()\n        if not ret:\n            break\n        \n        if frame_index % interval == 0:\n            filename = os.path.join(output_dir, f\"frame_{saved_count:06d}.jpg\")\n            cv2.imwrite(filename, frame)\n            saved_count += 1\n        \n        frame_index += 1\n    \n    cap.release()\n    return saved_count\n```\n\n### Full Extraction with JSON Output\n\n```python\nimport cv2\nimport os\nimport json\nfrom pathlib import Path\n\ndef extract_frames_to_json(video_path, output_dir, interval=1, \n                           start_frame=0, end_frame=None, output_format=\"jpg\"):\n    \"\"\"Extract frames and return results as JSON.\"\"\"\n    video_name = os.path.basename(video_path)\n    warnings = []\n    output_files = []\n    \n    try:\n        os.makedirs(output_dir, exist_ok=True)\n        cap = cv2.VideoCapture(video_path)\n        \n        if not cap.isOpened():\n            raise ValueError(f\"Cannot open video: {video_path}\")\n        \n        # Get video metadata\n        total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))\n        fps = cap.get(cv2.CAP_PROP_FPS)\n        width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))\n        height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))\n        duration = total_frames \u002F fps if fps > 0 else 0\n        \n        # Set end frame if not specified\n        if end_frame is None:\n            end_frame = total_frames\n        \n        # Seek to start frame\n        if start_frame > 0:\n            cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)\n        \n        frame_index = start_frame\n        saved_count = 0\n        \n        while frame_index \u003C end_frame:\n            ret, frame = cap.read()\n            if not ret:\n                if frame_index \u003C end_frame:\n                    warnings.append(f\"Video ended early at frame {frame_index}\")\n                break\n            \n            if (frame_index - start_frame) % interval == 0:\n                filename = f\"frame_{saved_count:06d}.{output_format}\"\n                filepath = os.path.join(output_dir, filename)\n                cv2.imwrite(filepath, frame)\n                output_files.append(filename)\n                saved_count += 1\n            \n            frame_index += 1\n        \n        cap.release()\n        \n        result = {\n            \"success\": True,\n            \"source_video\": video_name,\n            \"output_directory\": str(output_dir),\n            \"frames_extracted\": saved_count,\n            \"extraction_params\": {\n                \"interval\": interval,\n                \"start_frame\": start_frame,\n                \"end_frame\": end_frame,\n                \"output_format\": output_format\n            },\n            \"video_metadata\": {\n                \"total_frames\": total_frames,\n                \"fps\": fps,\n                \"duration_seconds\": round(duration, 2),\n                \"resolution\": [width, height]\n            },\n            \"output_files\": output_files,\n            \"warnings\": warnings\n        }\n        \n    except Exception as e:\n        result = {\n            \"success\": False,\n            \"source_video\": video_name,\n            \"output_directory\": str(output_dir),\n            \"frames_extracted\": 0,\n            \"extraction_params\": {\n                \"interval\": interval,\n                \"start_frame\": start_frame,\n                \"end_frame\": end_frame,\n                \"output_format\": output_format\n            },\n            \"video_metadata\": {\n                \"total_frames\": 0,\n                \"fps\": 0,\n                \"duration_seconds\": 0,\n                \"resolution\": [0, 0]\n            },\n            \"output_files\": [],\n            \"warnings\": [f\"Extraction failed: {str(e)}\"]\n        }\n    \n    return result\n\n# Usage\nresult = extract_frames_to_json(\"video.mp4\", \".\u002Fframes\", interval=10)\nprint(json.dumps(result, indent=2))\n```\n\n### Time-Based Frame Extraction\n\n```python\nimport cv2\nimport os\n\ndef extract_frames_by_seconds(video_path, output_dir, seconds_interval=1.0):\n    \"\"\"Extract frames at specific time intervals (in seconds).\"\"\"\n    os.makedirs(output_dir, exist_ok=True)\n    \n    cap = cv2.VideoCapture(video_path)\n    fps = cap.get(cv2.CAP_PROP_FPS)\n    frame_interval = int(fps * seconds_interval)\n    \n    if frame_interval \u003C 1:\n        frame_interval = 1\n    \n    frame_index = 0\n    saved_count = 0\n    \n    while True:\n        ret, frame = cap.read()\n        if not ret:\n            break\n        \n        if frame_index % frame_interval == 0:\n            filename = os.path.join(output_dir, f\"frame_{saved_count:06d}.jpg\")\n            cv2.imwrite(filename, frame)\n            saved_count += 1\n        \n        frame_index += 1\n    \n    cap.release()\n    return saved_count\n```\n\n### Batch Processing Multiple Videos\n\n```python\nimport cv2\nimport os\nimport json\nfrom pathlib import Path\n\ndef process_video_directory(video_dir, output_base_dir, interval=1):\n    \"\"\"Process all videos in a directory and extract frames.\"\"\"\n    video_extensions = {'.mp4', '.avi', '.mov', '.mkv', '.wmv', '.flv', '.webm'}\n    results = []\n    \n    for video_file in sorted(Path(video_dir).iterdir()):\n        if video_file.suffix.lower() in video_extensions:\n            video_output_dir = os.path.join(\n                output_base_dir, \n                video_file.stem\n            )\n            result = extract_frames_to_json(\n                str(video_file), \n                video_output_dir, \n                interval=interval\n            )\n            results.append(result)\n            print(f\"Processed: {video_file.name} -> {result['frames_extracted']} frames\")\n    \n    return results\n```\n\n\n## Extraction Configuration Options\n\n### Output Image Formats\n\n```python\n# JPEG format (default, good balance of quality and size)\ncv2.imwrite(\"frame.jpg\", frame)\n\n# PNG format (lossless, larger files)\ncv2.imwrite(\"frame.png\", frame)\n\n# JPEG with custom quality (0-100)\ncv2.imwrite(\"frame.jpg\", frame, [cv2.IMWRITE_JPEG_QUALITY, 95])\n\n# PNG with compression level (0-9)\ncv2.imwrite(\"frame.png\", frame, [cv2.IMWRITE_PNG_COMPRESSION, 3])\n```\n\n### Frame Seeking Methods\n\n```python\n# Seek by frame number\ncap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)\n\n# Seek by milliseconds\ncap.set(cv2.CAP_PROP_POS_MSEC, milliseconds)\n\n# Seek by ratio (0.0 to 1.0)\ncap.set(cv2.CAP_PROP_POS_AVI_RATIO, 0.5)  # Middle of video\n```\n\n### Frame Resizing\n\n```python\ndef extract_resized_frames(video_path, output_dir, target_size=(640, 480)):\n    \"\"\"Extract and resize frames to specified dimensions.\"\"\"\n    os.makedirs(output_dir, exist_ok=True)\n    \n    cap = cv2.VideoCapture(video_path)\n    frame_count = 0\n    \n    while True:\n        ret, frame = cap.read()\n        if not ret:\n            break\n        \n        resized = cv2.resize(frame, target_size)\n        filename = os.path.join(output_dir, f\"frame_{frame_count:06d}.jpg\")\n        cv2.imwrite(filename, resized)\n        frame_count += 1\n    \n    cap.release()\n    return frame_count\n```\n\n\n## Video Metadata Retrieval\n\nExtract video properties before processing:\n\n```python\ndef get_video_info(video_path):\n    \"\"\"Retrieve video metadata.\"\"\"\n    cap = cv2.VideoCapture(video_path)\n    \n    if not cap.isOpened():\n        return None\n    \n    info = {\n        \"total_frames\": int(cap.get(cv2.CAP_PROP_FRAME_COUNT)),\n        \"fps\": cap.get(cv2.CAP_PROP_FPS),\n        \"width\": int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),\n        \"height\": int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)),\n        \"codec\": int(cap.get(cv2.CAP_PROP_FOURCC)),\n        \"duration_seconds\": cap.get(cv2.CAP_PROP_FRAME_COUNT) \u002F cap.get(cv2.CAP_PROP_FPS)\n    }\n    \n    cap.release()\n    return info\n```\n\n\n## Specific Frame Extraction\n\nFor extracting frames at exact positions:\n\n```python\ndef extract_specific_frames(video_path, output_dir, frame_numbers):\n    \"\"\"Extract specific frames by their indices.\"\"\"\n    os.makedirs(output_dir, exist_ok=True)\n    \n    cap = cv2.VideoCapture(video_path)\n    extracted = []\n    \n    for frame_num in sorted(frame_numbers):\n        cap.set(cv2.CAP_PROP_POS_FRAMES, frame_num)\n        ret, frame = cap.read()\n        \n        if ret:\n            filename = os.path.join(output_dir, f\"frame_{frame_num:06d}.jpg\")\n            cv2.imwrite(filename, frame)\n            extracted.append(frame_num)\n    \n    cap.release()\n    return extracted\n```\n\n\n## Error Handling\n\n### Common Issues and Solutions\n\n**Issue**: Video file cannot be opened\n\n```python\ncap = cv2.VideoCapture(video_path)\nif not cap.isOpened():\n    print(f\"Error: Cannot open video file: {video_path}\")\n    print(\"Check file path, permissions, and codec support\")\n```\n\n**Issue**: Frames read as None\n\n```python\nret, frame = cap.read()\nif not ret or frame is None:\n    print(\"Failed to read frame - video may be corrupted or ended\")\n```\n\n**Issue**: Codec not supported\n\n```python\n# Check if video has valid properties\nfps = cap.get(cv2.CAP_PROP_FPS)\nif fps == 0:\n    print(\"Warning: Could not detect FPS - codec may be unsupported\")\n```\n\n**Issue**: Disk space exhausted\n\n```python\nimport shutil\n\ndef check_disk_space(output_dir, required_mb=100):\n    \"\"\"Check available disk space before extraction.\"\"\"\n    stat = shutil.disk_usage(output_dir)\n    available_mb = stat.free \u002F (1024 * 1024)\n    return available_mb >= required_mb\n```\n\n\n## Quality Self-Check\n\nBefore returning results, verify:\n\n- [ ] Output is valid JSON (use `json.loads()` to validate)\n- [ ] All required fields are present (`success`, `source_video`, `frames_extracted`, `video_metadata`)\n- [ ] Output directory was created successfully\n- [ ] Extracted frame count matches expected value based on interval\n- [ ] Warnings array includes all detected issues\n- [ ] Video was properly released with `cap.release()`\n- [ ] Frame filenames follow consistent zero-padded numbering\n\n\n## Limitations\n\n- OpenCV may not support all video codecs; install additional codecs if needed\n- Seeking in variable frame rate videos may be inaccurate\n- Large videos with high frame counts require significant disk space\n- Memory usage increases with video resolution\n- Some container formats (MKV with certain codecs) may have seeking issues\n- Encrypted or DRM-protected videos cannot be processed\n- Damaged or partially corrupted videos may extract partial results\n\n\n## Version History\n\n- **1.0.0** (2026-01-21): Initial release with OpenCV video frame extraction\n","# Video Frame Extraction Skill\n\n## Purpose\nThis 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.\n\n## When to Use\n- Extracting frames for machine learning training data\n- Creating image sequences from video content\n- Generating video thumbnails or preview images\n- Preprocessing videos for object detection or tracking\n- Converting video segments to image collections for analysis\n- Sampling frames at specific intervals for time-lapse effects\n\n## Required Libraries\n\nThe following Python libraries are required:\n\n```python\nimport cv2\nimport os\nimport json\nfrom pathlib import Path\n```\n\n## Input Requirements\n- **File formats**: MP4, AVI, MOV, MKV, WMV, FLV, WEBM\n- **Video codec**: Must be readable by OpenCV (most common codecs supported)\n- **File access**: Read permissions on source video\n- **Output directory**: Write permissions on destination folder\n- **Disk space**: Ensure sufficient space for extracted frames (uncompressed images)\n\n## Output Schema\nAll extraction results must be returned as valid JSON conforming to this schema:\n\n```json\n{\n  \"success\": true,\n  \"source_video\": \"sample.mp4\",\n  \"output_directory\": \"\u002Fpath\u002Fto\u002Fframes\",\n  \"frames_extracted\": 150,\n  \"extraction_params\": {\n    \"interval\": 1,\n    \"start_frame\": 0,\n    \"end_frame\": null,\n    \"output_format\": \"jpg\"\n  },\n  \"video_metadata\": {\n    \"total_frames\": 300,\n    \"fps\": 30.0,\n    \"duration_seconds\": 10.0,\n    \"resolution\": [1920, 1080]\n  },\n  \"output_files\": [\n    \"frame_000001.jpg\",\n    \"frame_000002.jpg\"\n  ],\n  \"warnings\": []\n}\n```\n\n\n### Field Descriptions\n\n- `success`: Boolean indicating whether frame extraction completed\n- `source_video`: Original video filename\n- `output_directory`: Path where frames were saved\n- `frames_extracted`: Total number of frames successfully saved\n- `extraction_params.interval`: Frame sampling interval (1 = every frame, 2 = every other frame, etc.)\n- `extraction_params.start_frame`: First frame index extracted\n- `extraction_params.end_frame`: Last frame index extracted (null if extracted to end)\n- `extraction_params.output_format`: Image format used for saving frames\n- `video_metadata.total_frames`: Total frame count in source video\n- `video_metadata.fps`: Frames per second of source video\n- `video_metadata.duration_seconds`: Video duration in seconds\n- `video_metadata.resolution`: Video dimensions as [width, height]\n- `output_files`: List of generated frame filenames\n- `warnings`: Array of issues encountered during extraction\n\n\n## Code Examples\n\n### Basic Frame Extraction\n\n```python\nimport cv2\nimport os\n\ndef extract_all_frames(video_path, output_dir):\n    \"\"\"Extract all frames from a video file.\"\"\"\n    os.makedirs(output_dir, exist_ok=True)\n    \n    cap = cv2.VideoCapture(video_path)\n    frame_count = 0\n    \n    while True:\n        ret, frame = cap.read()\n        if not ret:\n            break\n        \n        filename = os.path.join(output_dir, f\"frame_{frame_count:06d}.jpg\")\n        cv2.imwrite(filename, frame)\n        frame_count += 1\n    \n    cap.release()\n    return frame_count\n```\n\n### Interval-Based Frame Extraction\n\n```python\nimport cv2\nimport os\n\ndef extract_frames_at_interval(video_path, output_dir, interval=1):\n    \"\"\"Extract frames at specified intervals.\"\"\"\n    os.makedirs(output_dir, exist_ok=True)\n    \n    cap = cv2.VideoCapture(video_path)\n    frame_index = 0\n    saved_count = 0\n    \n    while True:\n        ret, frame = cap.read()\n        if not ret:\n            break\n        \n        if frame_index % interval == 0:\n            filename = os.path.join(output_dir, f\"frame_{saved_count:06d}.jpg\")\n            cv2.imwrite(filename, frame)\n            saved_count += 1\n        \n        frame_index += 1\n    \n    cap.release()\n    return saved_count\n```\n\n### Full Extraction with JSON Output\n\n```python\nimport cv2\nimport os\nimport json\nfrom pathlib import Path\n\ndef extract_frames_to_json(video_path, output_dir, interval=1, \n                           start_frame=0, end_frame=None, output_format=\"jpg\"):\n    \"\"\"Extract frames and return results as JSON.\"\"\"\n    video_name = os.path.basename(video_path)\n    warnings = []\n    output_files = []\n    \n    try:\n        os.makedirs(output_dir, exist_ok=True)\n        cap = cv2.VideoCapture(video_path)\n        \n        if not cap.isOpened():\n            raise ValueError(f\"Cannot open video: {video_path}\")\n        \n        # Get video metadata\n        total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))\n        fps = cap.get(cv2.CAP_PROP_FPS)\n        width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))\n        height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))\n        duration = total_frames \u002F fps if fps > 0 else 0\n        \n        # Set end frame if not specified\n        if end_frame is None:\n            end_frame = total_frames\n        \n        # Seek to start frame\n        if start_frame > 0:\n            cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)\n        \n        frame_index = start_frame\n        saved_count = 0\n        \n        while frame_index \u003C end_frame:\n            ret, frame = cap.read()\n            if not ret:\n                if frame_index \u003C end_frame:\n                    warnings.append(f\"Video ended early at frame {frame_index}\")\n                break\n            \n            if (frame_index - start_frame) % interval == 0:\n                filename = f\"frame_{saved_count:06d}.{output_format}\"\n                filepath = os.path.join(output_dir, filename)\n                cv2.imwrite(filepath, frame)\n                output_files.append(filename)\n                saved_count += 1\n            \n            frame_index += 1\n        \n        cap.release()\n        \n        result = {\n            \"success\": True,\n            \"source_video\": video_name,\n            \"output_directory\": str(output_dir),\n            \"frames_extracted\": saved_count,\n            \"extraction_params\": {\n                \"interval\": interval,\n                \"start_frame\": start_frame,\n                \"end_frame\": end_frame,\n                \"output_format\": output_format\n            },\n            \"video_metadata\": {\n                \"total_frames\": total_frames,\n                \"fps\": fps,\n                \"duration_seconds\": round(duration, 2),\n                \"resolution\": [width, height]\n            },\n            \"output_files\": output_files,\n            \"warnings\": warnings\n        }\n        \n    except Exception as e:\n        result = {\n            \"success\": False,\n            \"source_video\": video_name,\n            \"output_directory\": str(output_dir),\n            \"frames_extracted\": 0,\n            \"extraction_params\": {\n                \"interval\": interval,\n                \"start_frame\": start_frame,\n                \"end_frame\": end_frame,\n                \"output_format\": output_format\n            },\n            \"video_metadata\": {\n                \"total_frames\": 0,\n                \"fps\": 0,\n                \"duration_seconds\": 0,\n                \"resolution\": [0, 0]\n            },\n            \"output_files\": [],\n            \"warnings\": [f\"Extraction failed: {str(e)}\"]\n        }\n    \n    return result\n\n# Usage\nresult = extract_frames_to_json(\"video.mp4\", \".\u002Fframes\", interval=10)\nprint(json.dumps(result, indent=2))\n```\n\n### Time-Based Frame Extraction\n\n```python\nimport cv2\nimport os\n\ndef extract_frames_by_seconds(video_path, output_dir, seconds_interval=1.0):\n    \"\"\"Extract frames at specific time intervals (in seconds).\"\"\"\n    os.makedirs(output_dir, exist_ok=True)\n    \n    cap = cv2.VideoCapture(video_path)\n    fps = cap.get(cv2.CAP_PROP_FPS)\n    frame_interval = int(fps * seconds_interval)\n    \n    if frame_interval \u003C 1:\n        frame_interval = 1\n    \n    frame_index = 0\n    saved_count = 0\n    \n    while True:\n        ret, frame = cap.read()\n        if not ret:\n            break\n        \n        if frame_index % frame_interval == 0:\n            filename = os.path.join(output_dir, f\"frame_{saved_count:06d}.jpg\")\n            cv2.imwrite(filename, frame)\n            saved_count += 1\n        \n        frame_index += 1\n    \n    cap.release()\n    return saved_count\n```\n\n### Batch Processing Multiple Videos\n\n```python\nimport cv2\nimport os\nimport json\nfrom pathlib import Path\n\ndef process_video_directory(video_dir, output_base_dir, interval=1):\n    \"\"\"Process all videos in a directory and extract frames.\"\"\"\n    video_extensions = {'.mp4', '.avi', '.mov', '.mkv', '.wmv', '.flv', '.webm'}\n    results = []\n    \n    for video_file in sorted(Path(video_dir).iterdir()):\n        if video_file.suffix.lower() in video_extensions:\n            video_output_dir = os.path.join(\n                output_base_dir, \n                video_file.stem\n            )\n            result = extract_frames_to_json(\n                str(video_file), \n                video_output_dir, \n                interval=interval\n            )\n            results.append(result)\n            print(f\"Processed: {video_file.name} -> {result['frames_extracted']} frames\")\n    \n    return results\n```\n\n\n## Extraction Configuration Options\n\n### Output Image Formats\n\n```python\n# JPEG format (default, good balance of quality and size)\ncv2.imwrite(\"frame.jpg\", frame)\n\n# PNG format (lossless, larger files)\ncv2.imwrite(\"frame.png\", frame)\n\n# JPEG with custom quality (0-100)\ncv2.imwrite(\"frame.jpg\", frame, [cv2.IMWRITE_JPEG_QUALITY, 95])\n\n# PNG with compression level (0-9)\ncv2.imwrite(\"frame.png\", frame, [cv2.IMWRITE_PNG_COMPRESSION, 3])\n```\n\n### Frame Seeking Methods\n\n```python\n# Seek by frame number\ncap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)\n\n# Seek by milliseconds\ncap.set(cv2.CAP_PROP_POS_MSEC, milliseconds)\n\n# Seek by ratio (0.0 to 1.0)\ncap.set(cv2.CAP_PROP_POS_AVI_RATIO, 0.5)  # Middle of video\n```\n\n### Frame Resizing\n\n```python\ndef extract_resized_frames(video_path, output_dir, target_size=(640, 480)):\n    \"\"\"Extract and resize frames to specified dimensions.\"\"\"\n    os.makedirs(output_dir, exist_ok=True)\n    \n    cap = cv2.VideoCapture(video_path)\n    frame_count = 0\n    \n    while True:\n        ret, frame = cap.read()\n        if not ret:\n            break\n        \n        resized = cv2.resize(frame, target_size)\n        filename = os.path.join(output_dir, f\"frame_{frame_count:06d}.jpg\")\n        cv2.imwrite(filename, resized)\n        frame_count += 1\n    \n    cap.release()\n    return frame_count\n```\n\n\n## Video Metadata Retrieval\n\nExtract video properties before processing:\n\n```python\ndef get_video_info(video_path):\n    \"\"\"Retrieve video metadata.\"\"\"\n    cap = cv2.VideoCapture(video_path)\n    \n    if not cap.isOpened():\n        return None\n    \n    info = {\n        \"total_frames\": int(cap.get(cv2.CAP_PROP_FRAME_COUNT)),\n        \"fps\": cap.get(cv2.CAP_PROP_FPS),\n        \"width\": int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),\n        \"height\": int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)),\n        \"codec\": int(cap.get(cv2.CAP_PROP_FOURCC)),\n        \"duration_seconds\": cap.get(cv2.CAP_PROP_FRAME_COUNT) \u002F cap.get(cv2.CAP_PROP_FPS)\n    }\n    \n    cap.release()\n    return info\n```\n\n\n## Specific Frame Extraction\n\nFor extracting frames at exact positions:\n\n```python\ndef extract_specific_frames(video_path, output_dir, frame_numbers):\n    \"\"\"Extract specific frames by their indices.\"\"\"\n    os.makedirs(output_dir, exist_ok=True)\n    \n    cap = cv2.VideoCapture(video_path)\n    extracted = []\n    \n    for frame_num in sorted(frame_numbers):\n        cap.set(cv2.CAP_PROP_POS_FRAMES, frame_num)\n        ret, frame = cap.read()\n        \n        if ret:\n            filename = os.path.join(output_dir, f\"frame_{frame_num:06d}.jpg\")\n            cv2.imwrite(filename, frame)\n            extracted.append(frame_num)\n    \n    cap.release()\n    return extracted\n```\n\n\n## Error Handling\n\n### Common Issues and Solutions\n\n**Issue**: Video file cannot be opened\n\n```python\ncap = cv2.VideoCapture(video_path)\nif not cap.isOpened():\n    print(f\"Error: Cannot open video file: {video_path}\")\n    print(\"Check file path, permissions, and codec support\")\n```\n\n**Issue**: Frames read as None\n\n```python\nret, frame = cap.read()\nif not ret or frame is None:\n    print(\"Failed to read frame - video may be corrupted or ended\")\n```\n\n**Issue**: Codec not supported\n\n```python\n# Check if video has valid properties\nfps = cap.get(cv2.CAP_PROP_FPS)\nif fps == 0:\n    print(\"Warning: Could not detect FPS - codec may be unsupported\")\n```\n\n**Issue**: Disk space exhausted\n\n```python\nimport shutil\n\ndef check_disk_space(output_dir, required_mb=100):\n    \"\"\"Check available disk space before extraction.\"\"\"\n    stat = shutil.disk_usage(output_dir)\n    available_mb = stat.free \u002F (1024 * 1024)\n    return available_mb >= required_mb\n```\n\n\n## Quality Self-Check\n\nBefore returning results, verify:\n\n- [ ] Output is valid JSON (use `json.loads()` to validate)\n- [ ] All required fields are present (`success`, `source_video`, `frames_extracted`, `video_metadata`)\n- [ ] Output directory was created successfully\n- [ ] Extracted frame count matches expected value based on interval\n- [ ] Warnings array includes all detected issues\n- [ ] Video was properly released with `cap.release()`\n- [ ] Frame filenames follow consistent zero-padded numbering\n\n\n## Limitations\n\n- OpenCV may not support all video codecs; install additional codecs if needed\n- Seeking in variable frame rate videos may be inaccurate\n- Large videos with high frame counts require significant disk space\n- Memory usage increases with video resolution\n- Some container formats (MKV with certain codecs) may have seeking issues\n- Encrypted or DRM-protected videos cannot be processed\n- Damaged or partially corrupted videos may extract partial results\n\n\n## Version History\n\n- **1.0.0** (2026-01-21): Initial release with OpenCV video frame extraction\n",{"name":5,"description":7},"main","7eeb258ada72b7f58119b0e7a2c00ea89d9beedd","tasks\u002Fjpg-ocr-stat\u002Fenvironment\u002Fskills\u002Fvideo-frame-extraction","https:\u002F\u002Fgithub.com\u002FWaaaalnut\u002Fskillsbench","https:\u002F\u002Fgithub.com\u002FWaaaalnut","student of Zhejiang University","2026-04-21 23:16:23",{"source":8,"skillName":5,"commands":47,"prompt":57,"zipAvailable":58},[48,51,54],{"tool":49,"command":50},"npx","npx skills add Waaaalnut\u002Fskillsbench --skill video-frame-extraction",{"tool":52,"command":53},"bunx","bunx skills add Waaaalnut\u002Fskillsbench --skill video-frame-extraction",{"tool":55,"command":56},"pnpm","pnpm dlx skills add Waaaalnut\u002Fskillsbench --skill video-frame-extraction","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FWaaaalnut\u002Fskillsbench\u002Fblob\u002Fmain\u002Ftasks\u002Fjpg-ocr-stat\u002Fenvironment\u002Fskills\u002Fvideo-frame-extraction\u002FSKILL.md",true,[],[61,75,89,113,135,154],{"skillNo":62,"name":63,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":64,"categories":65,"keywords":71,"publishedAt":34},"01M6XEWMJ6VY3ZC53MPDCPG3GH","data-reconciliation","https:\u002F\u002Fgithub.com\u002FWaaaalnut\u002Fskillsbench\u002Fblob\u002Fmain\u002Ftasks\u002Fxlsx-recover-data\u002Fenvironment\u002Fskills\u002Fdata-reconciliation\u002FSKILL.md",[66],{"categoryNo":67,"categoryName":68,"taxonomyType":69,"taxonomyTypeDesc":70},"01JBSEEDCAT000000000000033","数据分析师","PROFESSION","适用职业",[72,73,74],"data reconciliation","reconciliation","data",{"skillNo":76,"name":77,"nameZh":6,"description":78,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":79,"categories":80,"keywords":81,"publishedAt":34},"01M6XEW8MNPMF1KRXV6ATGFRQF","speech-to-text","Transcribe video to timestamped text using Whisper tiny model (pre-installed).","https:\u002F\u002Fgithub.com\u002FWaaaalnut\u002Fskillsbench\u002Fblob\u002Fmain\u002Ftasks\u002Fvideo-tutorial-indexer\u002Fenvironment\u002Fskills\u002Fspeech-to-text\u002FSKILL.md",[],[82,83,84,85,86,87,24,88],"speech","text","speech to text","transcribe video","whisper","model","transcribe",{"skillNo":90,"name":91,"nameZh":6,"description":92,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":93,"categories":94,"keywords":96,"publishedAt":34},"01M6XEW3FQZP0T1RKQQJFXVY9T","video-processor","Process videos by removing segments and concatenating remaining parts. Use when you need to remove detected pauses\u002Fopenings from videos, create highlight reels, or batch process segment removals using ffmpeg filter_complex.","https:\u002F\u002Fgithub.com\u002FWaaaalnut\u002Fskillsbench\u002Fblob\u002Fmain\u002Ftasks\u002Fvideo-silence-remover\u002Fenvironment\u002Fskills\u002Fvideo-processor\u002FSKILL.md",[95],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},[24,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112],"processor","video processor","batch","create","reels","when","parts","segments","process","videos","you","segment","filter","ffmpeg","use","remove",{"skillNo":114,"name":115,"nameZh":6,"description":116,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":117,"categories":118,"keywords":120,"publishedAt":34},"01M6XEVXJ5KZ0NTY3XZKRY4AS1","silence-detector","Detect initial silence segments in audio\u002Fvideo using energy-based analysis. Use when you need to find low-energy periods at the start of recordings (title slides, setup time, pre-roll silence).","https:\u002F\u002Fgithub.com\u002FWaaaalnut\u002Fskillsbench\u002Fblob\u002Fmain\u002Ftasks\u002Fvideo-silence-remover\u002Fenvironment\u002Fskills\u002Fsilence-detector\u002FSKILL.md",[119],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},[121,122,123,124,102,107,125,126,127,128,129,104,130,131,24,132,133,134,111],"detector","silence detector","title","slides","time","energy","at","recordings","detect","find","Audio","analysis","start","setup",{"skillNo":136,"name":137,"nameZh":6,"description":138,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":139,"categories":140,"keywords":141,"publishedAt":34},"01M6XEVT3NGRN1FT7JPMAPXS6M","segment-combiner","Combine multiple segment detection results into a unified list. Use when you need to merge segments from different detectors, prepare removal lists for video processing, or consolidate detection outputs.","https:\u002F\u002Fgithub.com\u002FWaaaalnut\u002Fskillsbench\u002Fblob\u002Fmain\u002Ftasks\u002Fvideo-silence-remover\u002Fenvironment\u002Fskills\u002Fsegment-combiner\u002FSKILL.md",[],[108,142,24,143,144,145,146,147,102,148,149,104,107,150,151,111,152,153],"segment combiner","detection","prepare","merge","video processing","for","outputs","combine","processing","consolidate","results","list",{"skillNo":155,"name":156,"nameZh":6,"description":157,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":158,"categories":159,"keywords":163,"publishedAt":34},"01M6XEVMPVASGS2KX86QK4FMT7","report-generator","Generate compression reports for video processing. Use when you need to create structured JSON reports with duration statistics, compression ratios, and segment details after video processing.","https:\u002F\u002Fgithub.com\u002FWaaaalnut\u002Fskillsbench\u002Fblob\u002Fmain\u002Ftasks\u002Fvideo-silence-remover\u002Fenvironment\u002Fskills\u002Freport-generator\u002FSKILL.md",[160],{"categoryNo":161,"categoryName":162,"taxonomyType":69,"taxonomyTypeDesc":70},"01JBSEEDCAT000000000000034","行政与办公人员",[164,165,166,108,102,147,107,167,150,168,169,170,111,146,171,24,172,173,100],"generator","report generator","report","with","statistics","structured","reports","json","compression","generate"]