This script extracts plain text subtitles from a hardcoded video. It uses a local LLM via LM Studio to process the text.
- Automated Workflow: Extracts frames, generates metadata, crops subtitle areas, and performs OCR.
- Local LLM Support: Uses LM Studio (compatible with models like
qwen3-vl, etc.) for high-accuracy text recognition without data privacy concerns or high API costs. - Smart Merging: Automatically merges consecutive frames with identical text into single subtitle entries.
- Resume Capability: Saves progress incrementally (
ocr_results.json), allowing you to stop and resume processing without losing work. - Batch Processing: Can process a single video or a whole directory of videos.
- Selective Steps: Flags to skip frame extraction or cropping if steps are already completed.
- Time Ranges: Process specific segments of a video (e.g., test the first 5 minutes or a specific range).
- Python 3: Ensure Python 3 is installed.
- FFmpeg: Must be installed and accessible in your system PATH.
- Linux:
sudo apt install ffmpeg - Mac:
brew install ffmpeg - Windows: Download and add to PATH.
- Linux:
- LM Studio:
- Download and install LM Studio.
- Load a vision-capable model (
qwen/qwen3-vl-8bis the recommended and tested one). - Start the Local Server on port
1234(default).
- Python Libraries:
pip install requests pillow
Run the script from the command line.
Process a single video:
python3 video_ocr.py path/to/video.mkvProcess all videos in a directory:
python3 video_ocr.py path/to/folder/Test Mode (Process only the first 5 minutes):
python3 video_ocr.py video.mkv --testSpecific Time Range (e.g., minute 15 to minute 40):
python3 video_ocr.py video.mkv --range 15-40Resume / Skip Steps: If you interrupted the process or want to re-run only the OCR part:
# Skip extracting frames and cropping images
python3 video_ocr.py video.mkv --skip-extract --skip-crop| Argument | Description |
|---|---|
input |
Path to a video file or a directory containing videos. |
--skip-extract |
Skips the FFmpeg frame extraction step. Useful if frames already exist. |
--skip-crop |
Skips the image cropping step. Useful if images are already cropped. |
--test |
Runs the process only on the first 5 minutes of the video. |
--range START-END |
Process a specific time range in minutes (e.g., 10-20). |
--crop-height |
Default: 180. Sets the height of the subtitle crop area in pixels. |
--cleanup |
Automatically remove temporary frame directories after processing. If not set, you will be prompted. |
The script creates the following structure during execution:
.
├── video_ocr.py
├── prompt.txt
├── video.mkv
├── video.srt # Final Output
├── frames/
│ └── video_name/ # Extracted raw frames
│ ├── video_1.jpeg
│ └── metadata.json # Timing metadata
│ └── ocr_results.json # Saved OCR progress
└── cropped_frames/
└── video_name/ # Cropped subtitle images
└── cropped_video_1.jpeg
You can modify global variables at the top of video_ocr.py to tweak settings:
FPS: Frames per second to extract (default:2).API_URL: LM Studio endpoint (default:http://localhost:1234/v1/chat/completions).MODEL_ID: The expected model ID string (default:qwen/qwen3-vl-8b).VIDEO_EXTENSIONS: Supported file types.
- "No video file found": Check your path. If passing a directory, ensure it contains supported video extensions (
.mkv,.mp4, etc.). - Connection Refused: Ensure LM Studio Local Server is running and "Cross-Origin-Resource-Sharing (CORS)" is enabled (usually default).
- Wrong Text: Adjust the
prompt.txtto be more specific or try a larger/better model in LM Studio.