Переводчик экрана: наложение перевода поверх текста на экране
build / build (push) Failing after 10s
build / build (push) Failing after 10s
Программа накладывает русский перевод прямо на английские надписи — как переводчик по фото, только для монитора. Работает с играми, интерфейсами и документами: распознаёт кадр, собирает текст в связные блоки, переводит и рисует плашки под цвет фона. Распознавание — встроенный Windows.Media.Ocr, офлайн. Перевод — цепочка движков с автопереключением (google, lingva, mymemory), потому что до разных сервисов из разных сетей достучаться получается по-разному. Собирается без .NET SDK: Roslyn из Build Tools плюс сборки Framework 4.8, которые уже есть в системе. На выходе один exe без установщика. Главная сложность оказалась не в переводе, а в том, чтобы понять, где на экране связный текст, а где отдельные надписи. Классические алгоритмы сегментации решают это по межстрочному зазору, но в плотном интерфейсе он одинаков у строк абзаца и у соседних контролов. Поэтому решение принимает ансамбль признаков: фон, граница между строками, заполненность строки, кегль. Надписи на чужом языке отсеиваются тремя уровнями — форма слова, вторая модель распознавания и частоты буквенных пар. Чтобы правки эвристик не оценивались на глаз, в tools лежат эталонные кадры и RenderTest: прогон конвейера без окна с подсчётом попаданий. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
# Переводчик экрана
|
||||
|
||||
Наводит перевод поверх английского текста прямо на экране — как Яндекс.Переводчик по фото,
|
||||
только для монитора. Нажал горячую клавишу → английские надписи закрылись плашками
|
||||
под цвет фона, на них русский текст. Остальная картинка остаётся на месте.
|
||||
|
||||
Работает с любой программой: игры, PDF, видео, интерфейсы, картинки — всё, что видно на экране.
|
||||
|
||||
* Распознавание — встроенный в Windows OCR: офлайн, бесплатно, ничего ставить не надо.
|
||||
* Перевод — несколько движков с автоматическим переключением, если какой-то не отвечает.
|
||||
* Один файл `ScreenTranslator.exe` (81 КБ), без установщика и без .NET SDK.
|
||||
|
||||
## Как пользоваться
|
||||
|
||||
| Клавиши | Действие |
|
||||
|---|---|
|
||||
| `Ctrl+Alt+Z` | перевести монитор, на котором сейчас курсор |
|
||||
| `Ctrl+Alt+X` | выделить область рамкой и перевести её |
|
||||
| `Esc` или повторное нажатие | закрыть перевод |
|
||||
| Пробел (удерживать) | подсмотреть оригинал под плашками |
|
||||
| Наведение мышью | показать оригинал одного фрагмента |
|
||||
| Клик по плашке | скопировать её перевод |
|
||||
| `Ctrl+C` | скопировать весь перевод |
|
||||
|
||||
Значок в трее — те же команды плюс «Проверить переводчики» и правка настроек.
|
||||
|
||||
Пока идёт распознавание и перевод, экран уже заморожен, а фрагменты подсвечены — видно,
|
||||
что программа работает. Полный цикл на мониторе 3440×1440 — около трёх секунд
|
||||
(0,6 с распознавание + ~2 с перевод), повтор того же текста — мгновенно из кэша.
|
||||
|
||||
## Установка
|
||||
|
||||
Скопировать папку `dist` куда угодно и запустить `ScreenTranslator.exe`. Всё.
|
||||
|
||||
Если Windows покажет «SmartScreen защитил ваш компьютер» — «Подробнее» → «Выполнить в любом случае»
|
||||
(файл без цифровой подписи).
|
||||
|
||||
Автозапуск: `Win+R` → `shell:startup` → положить туда ярлык на exe.
|
||||
|
||||
### Требование одно: модель распознавания
|
||||
|
||||
Нужен установленный в Windows языковой компонент «Распознавание текста» для английского.
|
||||
Проверить: значок в трее → «Проверить переводчики» покажет ошибку, если моделей нет.
|
||||
|
||||
Ставится так: **Параметры → Время и язык → Язык и регион** → у английского языка `···` →
|
||||
**Компоненты языка** → отметить **Распознавание текста**. На большинстве систем уже стоит.
|
||||
|
||||
## Настройка
|
||||
|
||||
Рядом с exe лежит `config.json` (создаётся при первом запуске). После правки —
|
||||
«Перечитать настройки» в трее, перезапуск не нужен.
|
||||
|
||||
Самое полезное:
|
||||
|
||||
```jsonc
|
||||
"hotkeyTranslate": "Ctrl+Alt+Z", // своя комбинация, если эта занята
|
||||
"ocrLanguage": "en-US", // язык оригинала на экране
|
||||
"targetLang": "ru", // на какой переводить
|
||||
"overlayMode": "frozen", // frozen — стоп-кадр; live — плашки поверх живого экрана
|
||||
"captureScope": "monitor", // monitor — экран под курсором; all — все мониторы сразу
|
||||
"ocrScale": 2.0, // 1.0 быстрее, 2.0 точнее на мелком шрифте
|
||||
"detectForeign": true, // не трогать надписи на языке системы (см. ниже)
|
||||
"markTranslated": true, // подчёркивать переведённые места
|
||||
"chain": ["google", "lingva", "mymemory"]
|
||||
```
|
||||
|
||||
### Смешанный экран
|
||||
|
||||
Английская модель OCR, встретив русскую надпись, читает её как бессмыслицу вроде `C03AaTb`
|
||||
или `Pa3Mep` — и без защиты программа переводила бы эту кашу, залепляя плашками весь
|
||||
русский интерфейс.
|
||||
|
||||
Защита двухступенчатая:
|
||||
|
||||
1. `skipGarbage` — отсев по форме слов: цифра посреди слова, заглавная после строчной,
|
||||
символы-артефакты, имена файлов.
|
||||
2. `detectForeign` — кадр дополнительно распознаётся моделью второго языка. Если она видит
|
||||
на этом месте кириллицу, надпись изначально русская и её не трогают. Это ещё примерно
|
||||
столько же времени на распознавание; выключается, если весь экран и так на одном языке.
|
||||
3. Список ходовых английских слов ([EnglishWords.cs](src/EnglishWords.cs)) — арбитр в спорных
|
||||
случаях. Русская модель читает латинское «Cancel» как «Сапсеl», то есть по алфавиту оно
|
||||
неотличимо от русского «Профиль» → `npocbwlb`. Если основная модель выдала узнаваемое
|
||||
английское слово, верим ей.
|
||||
|
||||
На эталонах это даёт 22 распознанных блока из 22 на английском кадре (ни одной потери)
|
||||
и 2 оставшихся из 20 на русском.
|
||||
|
||||
Совсем идеально не будет: очень мелкие надписи (вкладки браузера, 8 px) обе модели читают
|
||||
плохо, и часть их может проскочить. **Если на экране мешанина языков — надёжнее `Ctrl+Alt+X`
|
||||
и рамка вокруг нужного куска.**
|
||||
|
||||
### Чего программа не умеет и не сможет
|
||||
|
||||
Рабочий стол, панель задач, списки установленных программ — не её сценарий. Подписи там это
|
||||
названия приложений, а отличить название от обычного слова невозможно: «Steam», «Discord»,
|
||||
«Wand» одновременно и то, и другое. Перевод получится буквальным и бессмысленным.
|
||||
|
||||
Помогает `"minWords": 2` — тогда переводятся только фразы от двух слов, а одиночные подписи
|
||||
остаются как есть. Но за это приходится платить: на интерфейсе игры теряется половина надписей
|
||||
(«Mining», «Search», «Consumable» — тоже одиночные слова), поэтому по умолчанию стоит `1`.
|
||||
|
||||
### Про переводчики и Россию
|
||||
|
||||
`chain` — это порядок попыток. Первый ответивший движок используется, упавший временно
|
||||
исключается и пробуется следующий. Ничего перенастраивать при переезде в другую сеть не нужно —
|
||||
если Google недоступен, программа сама уйдёт на запасной.
|
||||
|
||||
Что стоит знать:
|
||||
|
||||
* **google** — бесплатный публичный эндпоинт, без ключа, лучшее соотношение качества и скорости.
|
||||
`translate.googleapis.com` в России не заблокирован (замедляли YouTube, а не API), но
|
||||
у отдельных провайдеров ТСПУ рвёт к нему TLS. Отсюда и нужна цепочка.
|
||||
* **lingva** — открытый шлюз к тому же Google на других адресах. Ровно тот случай, когда
|
||||
напрямую не пускает, а через шлюз проходит. Медленнее (2–4 с).
|
||||
* **mymemory** — последний рубеж, без ключа, лимит около 5000 слов в сутки на IP.
|
||||
* **yandex** — самый устойчивый канал внутри РФ, но нужен платный Yandex Cloud
|
||||
(`folderId` + API-ключ).
|
||||
* **deepl** — лучшее качество en→ru, есть бесплатный тариф с ключом.
|
||||
* **llm** — перевод целым экраном через Claude API: лучше держит контекст интерфейса,
|
||||
но платный по токенам и медленнее.
|
||||
* **libretranslate** / **relay** — свой сервер, ничего не уходит наружу.
|
||||
|
||||
Проверить, что доступно из конкретной сети: трей → **Проверить переводчики** — покажет
|
||||
время ответа или ошибку по каждому.
|
||||
|
||||
Через прокси (например, свой sing-box в режиме mixed):
|
||||
|
||||
```jsonc
|
||||
"proxy": "http://192.168.5.42:2080"
|
||||
```
|
||||
|
||||
### Свой релей
|
||||
|
||||
Если публичные сервисы недоступны, а свой сервер есть — поднимите эндпоинт, принимающий
|
||||
`POST {"texts": [...], "from": "en", "to": "ru"}` и возвращающий `{"texts": [...]}`
|
||||
той же длины, затем:
|
||||
|
||||
```jsonc
|
||||
"chain": ["relay", "google"],
|
||||
"engines": { "relay": { "url": "https://ваш-домен/translate" } }
|
||||
```
|
||||
|
||||
Внутри релей может ходить хоть в Google, хоть в локальную модель — клиенту всё равно.
|
||||
|
||||
## Приватность
|
||||
|
||||
Распознавание идёт локально, наружу уходит **только распознанный текст** — картинка экрана
|
||||
никогда не покидает компьютер. Но текст с экрана всё же уходит в выбранный сервис перевода,
|
||||
поэтому для чувствительного содержимого используйте `libretranslate` или `relay` на своём сервере.
|
||||
|
||||
Кэш переводов лежит рядом в `cache.json` — его можно удалять в любой момент.
|
||||
|
||||
## Сборка из исходников
|
||||
|
||||
.NET SDK не нужен — используется Roslyn из Visual Studio Build Tools и сборки .NET Framework 4.8,
|
||||
которые уже есть в Windows.
|
||||
|
||||
```powershell
|
||||
.\build.ps1 # собрать в .\dist
|
||||
.\build.ps1 -Run # собрать и запустить
|
||||
.\build.ps1 -Test # плюс RenderTest.exe
|
||||
```
|
||||
|
||||
`RenderTest.exe` — не сама программа, а диагностика: прогоняет весь конвейер без окна и
|
||||
сохраняет две картинки (как OCR разметил блоки и как лёг перевод). Запускать лучше из терминала;
|
||||
при двойном клике он всё равно отработает и подождёт Enter, а картинки положит
|
||||
в `%TEMP%\screen-translator-test`.
|
||||
|
||||
```powershell
|
||||
.\dist\RenderTest.exe --in кадр.png --out .\dist\test
|
||||
.\dist\RenderTest.exe --scale 1.0 --no-translate
|
||||
```
|
||||
|
||||
Два эталонных кадра для прогонов рисуются скриптами:
|
||||
|
||||
* `tools\make-sample.ps1` — англоязычный интерфейс. Проверка «всё ли переводится»:
|
||||
правильный результат — 22 блока.
|
||||
* `tools\make-sample-ru.ps1` — русский интерфейс. Негативная проверка: английская модель OCR
|
||||
читает кириллицу как кашу, и её нельзя ни переводить, ни закрывать плашками.
|
||||
Правильный результат — около 5 блоков (мелкие обрывки вроде «Tn»), было 20 до фильтра.
|
||||
|
||||
Оба стоит прогонять после правок в `Blocks.cs`: ужесточение фильтра легко начинает резать
|
||||
короткие английские кнопки, и английский кадр это сразу показывает.
|
||||
|
||||
Флаг `--expect файл.txt` считает, сколько заранее известных строк реально распозналось —
|
||||
без такой метрики улучшение и ухудшение на глаз выглядят одинаково убедительно.
|
||||
`--selftest` проверяет биграммную модель на примерах настоящих надписей и каши.
|
||||
|
||||
```powershell
|
||||
.\dist\RenderTest.exe --in wow.jpg --expect tools\expect-wow.txt --no-translate
|
||||
.\dist\RenderTest.exe --selftest
|
||||
```
|
||||
|
||||
Текущие показатели (после каждой правки должны держаться):
|
||||
|
||||
| Кадр | Метрика | Значение |
|
||||
|---|---|---|
|
||||
| Скриншот игры | распознано известных надписей | 14 из 14 |
|
||||
| Английский эталон | ложно отброшено | 0 |
|
||||
| Русский эталон | осталось непойманной каши | 2 из 20 |
|
||||
|
||||
## Что внутри
|
||||
|
||||
| Файл | Зачем |
|
||||
|---|---|
|
||||
| `src/Program.cs` | трей, горячие клавиши, порядок работы |
|
||||
| `src/ScreenCapture.cs` | снимок экрана и отдельная копия пикселей для фоновой обработки |
|
||||
| `src/Ocr.cs` | Windows.Media.Ocr, увеличение кадра ради мелкого шрифта |
|
||||
| `src/Blocks.cs` | склейка строк в абзацы, отсев каши, подбор цветов плашки |
|
||||
| `src/Translators.cs` | движки перевода, цепочка фоллбеков, кэш |
|
||||
| `src/PlateRenderer.cs` | подбор кегля и отрисовка плашки |
|
||||
| `src/OverlayForm.cs` | окно поверх экрана |
|
||||
| `src/RegionSelector.cs` | выделение области рамкой |
|
||||
|
||||
| `src/EnglishBigrams.cs` | частоты буквенных пар: отличает слово от каши распознавания |
|
||||
| `src/EnglishWords.cs` | список ходовых английских слов — арбитр в спорных случаях |
|
||||
|
||||
Откуда взялись решения (короткий разбор чужого опыта):
|
||||
|
||||
* Классические алгоритмы сегментации — RLSA, Docstrum, XY-cut, Voronoi — принимают решение
|
||||
«склеивать или нет» по одному сигналу, межстрочному зазору. В плотном интерфейсе он
|
||||
одинаков у строк абзаца и у соседних контролов, поэтому ни один из них эту задачу не решает.
|
||||
Работает только ансамбль независимых признаков — как в Tesseract (`paragraphs.h`, геометрия
|
||||
плюс пунктуация) и UIED, где графику детектируют отдельно от текста.
|
||||
* Отсюда четыре признака разрыва: разный фон, граница между строками, «строка не дотянула
|
||||
до правого края» (в абзаце короткой бывает только последняя) и несовпадение кегля.
|
||||
* Определение чужого языка — это не задача language identification: готовые детекторы
|
||||
(cld3, fastText) всегда выбирают какой-то язык из списка и уверенно зовут кашу английским.
|
||||
Нужна одноклассовая модель «похоже на настоящий английский», отсюда биграммы.
|
||||
* Проверено и отвергнуто: перевод в оттенки серого и инверсия тёмного фона перед OCR.
|
||||
Обещали заметный прирост, на замере не дали ничего (14 из 20 во всех вариантах),
|
||||
поэтому по умолчанию выключены, хотя в конфиге остались.
|
||||
|
||||
Тонкие места, если будете править:
|
||||
|
||||
* Приложение помечено `PerMonitorV2` — все координаты физические, иначе разметка OCR
|
||||
разъезжается с экраном при масштабе интерфейса больше 100 %.
|
||||
* OCR обводит только тело букв, без выносных элементов, поэтому плашка расширяется
|
||||
по вертикали — иначе сверху и снизу торчат хвосты оригинала.
|
||||
* Строки склеиваются в абзац по межстрочному интервалу и одинаковому кеглю: рваные строки
|
||||
переводятся заметно хуже целого абзаца.
|
||||
Reference in New Issue
Block a user