Subtitle Edit can convert image-based subtitle formats to text using OCR.

Open-source OCR engine with language packs.
Built-in trainable OCR engine.
Binary image comparison engine.
Cloud-based OCR using Google Lens (free, but capped).
Cloud-based OCR using Google Cloud Vision API.
Local LLM-based OCR via an Ollama server (e.g. with a vision model).
http://localhost:11434/api/chatLocal LLM-based OCR using a llama.cpp-compatible server. Subtitle Edit can download llama.cpp and the model for you, or talk to a server you run yourself.
http://127.0.0.1:8080/v1/chat/completions*.gguf in the llama.cpp models folder together with its mmproj vision projector, named either mmproj-<file>.gguf or <file>-mmproj.gguf (the two naming schemes used on Hugging Face). A *.gguf without a projector next to it is not offered — it cannot see the imageLocal OCR engine with multiple model backends (free/open source).
q4_k model of about 2.31 GBq4_k and a higher-quality q8_0 model (from about 445 MB for GOT-OCR2 q4_k up to about 2.29 GB for Qwen3-VL-2B q8_0)Cloud-based OCR using Mistral API.
Local OCR engine.
The subtitle list’s right-click menu has Save all images with HTML index…. Pick a folder and
Subtitle Edit writes index.html plus images/0001.png, 0002.png, … — every subtitle bitmap
next to the text OCR produced for it, which is the quickest way to proof-read a run against the
originals.
The page is self-contained (no external css, js or fonts), so it opens straight off the file system. It follows the reader’s light/dark preference, with an Auto/Light/Dark override, and adds a text filter, an “only lines without text” filter for spotting images that produced nothing, a dark/light/checkerboard backdrop for the transparent bitmaps, click-to-zoom, and each line’s number, time codes, duration and image size.
| Shortcut | Action |
|---|---|
| Escape | Cancel OCR / Close window |
| Ctrl+G | Go to line number |
| Ctrl++ | Zoom in (images in grid) |
| Ctrl+- | Zoom out (images in grid) |
| F1 | Show help |
| Shortcut | Action |
|---|---|
| Ctrl+I | Toggle italic formatting |
| Ctrl+P | View selected image (use arrow keys to navigate) |
| Delete | Delete selected line(s) |
| Home | Jump to first line |
| End | Jump to last line |
| Double-click | Inspect line (nOCR/Binary OCR only) |
| Shortcut | Action |
|---|---|
| Enter | Jump to subtitle line containing the selected unknown word |
Note: All shortcuts can be customized. Go to Options → Shortcuts to view and change key bindings.
Before OCR, you can apply image pre-processing:
Batch Convert can process image-based subtitle files with OCR. Subtitle Edit 5 includes Binary OCR in Batch Convert and can auto-detect several nOCR/Binary OCR settings such as language and pixels-are-space values.
For VobSub files, Batch Convert isolates the glyph color before OCR (on by default, configurable in the Batch Convert settings): the subpicture is rebuilt as a crisp black-on-white bitmap from pixel frequency, so outline and anti-alias colors no longer melt adjacent characters together.
For command-line workflows, see Command Line (seconv), which documents OCR engines and options for headless conversion.

When the OCR engine encounters uncertain characters, you can:
Some of the choices in the unknown-word prompt are saved to disk and reused on later OCR runs; others only last as long as the OCR window is open.
| Choice | Remembered? | Stored in |
|---|---|---|
| Change all | Yes | {language}_OCRFixReplaceList.xml |
| Add to names list | Yes | {language}_names.xml |
| Add to user dictionary | Yes | {language}_user.xml |
| Skip once | No | — |
| Skip all | No — current OCR session only | — |
“Skip all” is deliberately temporary. It silences a word for the rest of the current OCR session and is forgotten when the OCR window closes, so a mis-click never has lasting consequences. If you want a word to be accepted permanently, use Add to user dictionary (or Add to names list for proper nouns) instead, and use Change all for a correction that should be applied automatically from now on.
Subtitle Edit uses language-specific XML files to automatically correct common OCR errors. These files are named {language}_OCRFixReplaceList.xml (e.g., eng_OCRFixReplaceList.xml for English) and are located in the Dictionaries folder.
See the example file: Dictionaries/eng_OCRFixReplaceList.xml
The OCR fix replacement list contains several sections that handle different types of corrections:
Replaces entire words that match exactly. This is the most common section for fixing OCR mistakes.
<WholeWords>
<Word from="tñere" to="there" />
<Word from="ri9ht" to="right" />
<Word from="0f" to="of" />
<Word from="alot" to="a lot" />
<Word from="becuase" to="because" />
</WholeWords>
Use cases:
0 → o, l → I)Replaces character sequences within words, always applied without spell checking.
<PartialWordsAlways>
<WordPart from="¤" to="o" />
<WordPart from="lVI" to="M" />
<WordPart from="IVl" to="M" />
</PartialWordsAlways>
Use cases:
Replaces entire lines that match exactly (including formatting tags).
<WholeLines>
<Line from="[chitte rs]" to="[chitters]" />
<Line from="Hil' it!" to="Hit it!" />
<Line from="<i>Hil' it!</i>" to="<i>Hit it!</i>" />
<Line from="ISIGHS]" to="[SIGHS]" />
</WholeLines>
Use cases:
Replaces text fragments within lines, always applied without spell checking.
<PartialLinesAlways>
<LinePart from="Apollo 1 3" to="Apollo 13" />
<LinePart from=",.," to="..." />
<LinePart from=" lt " to=" it " />
<LinePart from=" lf " to=" if " />
</PartialLinesAlways>
Use cases:
Replaces text fragments within lines (may be spell-checked).
<PartialLines>
<LinePart from=" /be " to=" I be " />
<LinePart from=" aren '1'" to=" aren't" />
<LinePart from=" aren'tyou" to=" aren't you" />
</PartialLines>
Uses regex patterns to fix errors, but only applies the replacement if the corrected word is in the dictionary.
<RegularExpressionsIfSpelledCorrectly>
<!-- Fix lowercase 'l' to uppercase 'I' if result is a valid word -->
<RegEx find="\bl([A-Z]+)\b" spellCheck="I$1" replaceWith="I$1" />
<RegEx find="\b([A-Z]+)l\b" spellCheck="$1I" replaceWith="$1I" />
<!-- Fix possessive forms: David's, there's -->
<RegEx find="\b([A-Z][a-z]+)['']s\b" spellCheck="$1" replaceWith="$1's" />
<RegEx find="\b([a-z]+)['']s\b" spellCheck="$1" replaceWith="$1's" />
<!-- Fix missing spaces: ofDavid → of David -->
<RegEx find="\bof([A-Z][a-z]+)\b" spellCheck="$1" replaceWith="of $1" />
<RegEx find="\bin([A-Z][a-z]+)\b" spellCheck="$1" replaceWith="in $1" />
<!-- Fix 'l' in brackets: [GRlNDlNG] → [GRINDING] -->
<RegEx find="\[([A-Z ]*)l([A-Z ]*)\]" spellCheck="[$1I$2]" replaceWith="[$1I$2]" />
</RegularExpressionsIfSpelledCorrectly>
Attributes:
find: The regex pattern to matchspellCheck: The text to check against the dictionary (use $1, $2 for capture groups)replaceWith: The replacement text if spell check passesreplaceAllFrom / replaceAllTo: Optional character replacement before spell checkingUse cases:
l to uppercase I (e.g., lTEM → ITEM)To add your own OCR fix rules:
Dictionaries/{language}_OCRFixReplaceList.xmlTips:
WholeWords for simple word replacementsRegularExpressionsIfSpelledCorrectly for pattern-based fixes where you want to verify the result is a real word