{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Extract frames from videos\n", "\n", "Pull frames from video files at specified intervals for analysis, thumbnails, or training data." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Problem\n", "\n", "You have video files and need to extract frames for:\n", "\n", "- Object detection on video content\n", "- Creating thumbnails or previews\n", "- Building training datasets\n", "- Scene analysis and classification\n", "\n", "| Video | Duration | Frames at 1 FPS |\n", "|-------|----------|-----------------|\n", "| interview.mp4 | 30 min | 1,800 frames |\n", "| product_demo.mp4 | 5 min | 300 frames |\n", "| surveillance.mp4 | 1 hour | 3,600 frames |" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Solution\n", "\n", "**What's in this recipe:**\n", "\n", "- Extract frames at a fixed rate (FPS)\n", "- Extract a specific number of frames\n", "- Extract only keyframes for efficiency\n", "\n", "You create a view with a `frame_iterator` that automatically extracts frames from each video. New videos are processed without extra code." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Setup" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "execution": { "iopub.execute_input": "2025-12-12T02:40:18.663966Z", "iopub.status.busy": "2025-12-12T02:40:18.663735Z", "iopub.status.idle": "2025-12-12T02:40:21.194236Z", "shell.execute_reply": "2025-12-12T02:40:21.193674Z" } }, "outputs": [], "source": [ "%pip install -qU pixeltable" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "execution": { "iopub.execute_input": "2025-12-12T02:40:21.210593Z", "iopub.status.busy": "2025-12-12T02:40:21.210379Z", "iopub.status.idle": "2025-12-12T02:40:22.344484Z", "shell.execute_reply": "2025-12-12T02:40:22.344084Z" } }, "outputs": [], "source": [ "import pixeltable as pxt\n", "from pixeltable.functions.video import frame_iterator" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Load videos" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2025-12-12T02:40:22.346571Z", "iopub.status.busy": "2025-12-12T02:40:22.346374Z", "iopub.status.idle": "2025-12-12T02:40:22.622091Z", "shell.execute_reply": "2025-12-12T02:40:22.621780Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata\n", "Created directory 'video_demo'.\n" ] }, { "data": { "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Create a fresh directory\n", "pxt.drop_dir('video_demo', force=True)\n", "pxt.create_dir('video_demo')" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2025-12-12T02:40:22.624464Z", "iopub.status.busy": "2025-12-12T02:40:22.624329Z", "iopub.status.idle": "2025-12-12T02:40:22.673211Z", "shell.execute_reply": "2025-12-12T02:40:22.672824Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Created table 'videos'.\n" ] } ], "source": [ "# Create table for videos\n", "videos = pxt.create_table('video_demo/videos', {'video': pxt.Video})" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2025-12-12T02:40:22.675084Z", "iopub.status.busy": "2025-12-12T02:40:22.674932Z", "iopub.status.idle": "2025-12-12T02:40:23.821362Z", "shell.execute_reply": "2025-12-12T02:40:23.820840Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Inserting rows into `videos`: 1 rows [00:00, 212.90 rows/s]\n", "Inserted 1 row with 0 errors.\n" ] }, { "data": { "text/plain": [ "1 row inserted, 2 values computed." ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Insert a sample video\n", "videos.insert(\n", " [\n", " {\n", " 'video': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/bangkok.mp4'\n", " }\n", " ]\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Extract frames at fixed rate\n", "\n", "Create a view that extracts 1 frame per second:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "execution": { "iopub.execute_input": "2025-12-12T02:40:23.824404Z", "iopub.status.busy": "2025-12-12T02:40:23.823542Z", "iopub.status.idle": "2025-12-12T02:40:24.881762Z", "shell.execute_reply": "2025-12-12T02:40:24.881262Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Inserting rows into `frames`: 19 rows [00:00, 8687.65 rows/s]\n" ] } ], "source": [ "# Extract 1 frame per second\n", "frames = pxt.create_view(\n", " 'video_demo/frames',\n", " videos,\n", " iterator=frame_iterator(\n", " videos.video,\n", " fps=1.0, # 1 frame per second\n", " ),\n", ")" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2025-12-12T02:40:24.883929Z", "iopub.status.busy": "2025-12-12T02:40:24.883747Z", "iopub.status.idle": "2025-12-12T02:40:25.249104Z", "shell.execute_reply": "2025-12-12T02:40:25.248558Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
framepos
\n", " \n", "
0
\n", " \n", "
1
\n", " \n", "
2
" ], "text/plain": [ " frame pos\n", "0 \n", " \n", " \n", " frame\n", " \n", " \n", " \n", " \n", "
\n", " \n", "
\n", " \n", " \n", "
\n", " \n", "
\n", " \n", " \n", "
\n", " \n", "
\n", " \n", " \n", "" ], "text/plain": [ " frame\n", "0