name: ocr-images description: Распознавание текста с изображений, скриншотов и сканов, включая текст внутри docx/pdf. Загружать, когда нужно вытащить текст с картинок, когда документ состоит из изображений, при работе с выгрузками-сканами или когда пользователь спрашивает про OCR.
OCR: как читать текст с картинок
Проверено 15.09.2026 на 51 скриншоте (2340×1080, тёмная тема, окна PowerPoint).
Три движка, у каждого своя роль
| Движок | Когда | Скорость | Качество |
|---|---|---|---|
| macOS Vision (Swift) | основной проход по печатному тексту | 1.8 с на кадр | хорошее, но рвёт строки и врёт на стилизованном тексте |
vision-модель (deepseek/deepseek-v4-flash-vision-exp) |
сложные кадры, окна программ, рукописное/стилизованное | ~10–30 с на кадр через субагента | заметно полнее: на тесте +27% текста |
| tesseract 5.5.3 | почти никогда | 0.2 с | на тёмных скриншотах вернул 0 строк |
Вывод: tesseract на скриншотах бесполезен, нативный Vision — рабочая лошадка, vision-модель — второе мнение и спасение на сложных кадрах.
Нативный Vision: собрать бинарь один раз
swift script.swift компилирует при каждом запуске (в тесте это давало 52 с вместо 1.8 с) — поэтому
собрать бинарник и вызывать его. Скрипт лежит в scripts/ocr.swift, сборка:
swiftc -O scripts/ocr.swift -o /tmp/ocrx
/tmp/ocrx image.jpeg "ru-RU,en-US" 1 2.0
# аргументы: файл, языки, языковая коррекция (0/1), масштаб
Полезные детали скрипта: сортировка результатов по координатам (иначе строки идут вразнобой),
вывод уверенность<TAB>текст, поддержка увеличения изображения вдвое (на мелком тексте помогает:
«красмаая» → «красивая»).
Проверка второго проходом
Не доверять одному проходу: прогнать кадр в двух-трёх вариантах (с коррекцией / без / увеличенным) и сравнить строки. В тесте это дало 51 исправленную строку и 7 строк, которые первый проход не увидел вовсе («неудовлстворенных импульсов … плохого опоа» → «неудовлетворенных … плохого опыта»). Сливались варианты по собственной уверенности модели.
Vision-модель как второй движок
Запускать субагентом: model: "deepseek/deepseek-v4-flash-vision-exp", промпт просит прочитать файл
по пути и вернуть текст дословно, сохраняя переносы, с [?] на неразборчивом. Работает и пачкой:
в промпт давать список «кадр N: путь», модель возвращает блоки ### кадр N.
Модель сама отделяет интерфейс программы от содержимого и честно помечает неуверенное — это удобно, но её вывод надо считать вторым мнением, а не истиной: она может «поправить» стиль и пунктуацию. Проверять по расхождениям с OCR.
Чистка служебного — отдельный обязательный этап
После распознавания всегда идёт чистка обвязки, единая для обоих движков. Нельзя полагаться на то, что vision-модель сама пометит интерфейс: она помечает не всегда, и в тесте после неё осталось 94 служебные строки в 14 кадрах. Что убирать:
- Строки из логов субагентов (
subagent_type=...) — если собираешь расшифровки из логов задач, эта метка легко попадает в текст (в тесте — семь раз). - Примечания модели в скобках («поверх слайда водяной знак», «в правом верхнем углу»).
- Панель миниатюр: перечни слайдов вида «36 Первичные отношения 37 Борец и Герой…» — это дублирование.
- Лента и статусная строка PowerPoint: «Вставить | Вырезать | Копировать…», «Слайд N из M», «88%».
- Заголовок окна («…[Автосохраненный] - PowerPoint»).
Правило «доля интерфейсных слов ≥ 60%» плюс словарь из ~60 слов ленты работает надёжно.
Грабли фильтров: каждая из них съела настоящий текст
len(строка) <= 6→ мусор выбросило слайд с одним словом «Либидо». Условие должно быть «короткая И без единой кириллической буквы».^(С|С текущего)\s(задумано для «С начала / С текущего слайда») совпало со строкой стихотворения «С тех пор все тянутся предо мною». Правило с якорем на одну букву — всегда опасность; писать полные строки интерфейса.- «Повторяется в ≥3 кадрах → колонтитул» выбросило заголовок слайда «Развитие». Повторяться может именно заголовок. Такие строки — только в журнал кандидатов, из текста не удалять.
- Римские цифры I/II с диаграмм не считать мусором: исключение
^[IVX]+$.
Вывод: любое правило фильтра проверять на настоящем тексте, и держать журнал удалённого — файл, где каждая убранная строка перечислена с причиной. Тогда потеря видна сразу, а не через неделю.
Проверка эталоном
Если пользователь вычитал часть текста руками — это идеальный тест: сравнить свой автоматический результат с его файлом по словам (difflib по списку слов, а не по строкам: разбивка на строки у всех разная). Совпадение 93% при ручной вычитке — хороший показатель; расхождения в остатке обычно артефакты выравнивания, но каждое надо посмотреть.
Грабли
- docx может не содержать текста вообще. Проверять сразу:
unzip -p file.docx word/document.xml | grep -c "<w:t", и смотретьword/media/. В разобранном файле было 51 изображение и ноль<w:t>. - Порядок картинок брать из
document.xmlпоr:embed+word/_rels/document.xml.rels, а не по именам файлов. - Не выбрасывать короткие строки по длине: фильтр «меньше 25 символов — мусор» выбросил «Либидо», подписи диаграмм и части фраз. Надёжное правило: выбрасывать только то, где нет ни одной кириллической буквы, и явные надписи интерфейса. Всё отброшенное — писать в отдельный файл «что не вошло».
- Латинские двойники: OCR путает
от/OT,по/ПO. Чинить переводом знаков-двойников, если в слове уже есть кириллица, или если всё слово состоит только из двойников. - Служебные слова капсом посреди фразы (
ПО какой-либо причине) — приводить к нижнему регистру после склейки абзацев, а не построчно (в разбитых строках слово стоит первым и правило не срабатывает).