Files
screen-translator/tools/make-sample-ru.ps1
T
gusadmin 7c6b182402
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>
2026-07-27 15:54:02 +03:00

86 lines
3.8 KiB
PowerShell

<#
Эталонный кадр с РУССКИМ интерфейсом.
Нужен как негативный тест: английская модель OCR читает кириллицу как бессмыслицу,
и эту бессмыслицу нельзя ни переводить, ни закрывать плашками. На таком кадре
правильный результат — ноль переведённых блоков.
#>
param([string]$Out = "$PSScriptRoot\..\dist\sample-ru.png")
Add-Type -AssemblyName System.Drawing
$W, $H = 1600, 900
$bmp = New-Object Drawing.Bitmap($W, $H, [Drawing.Imaging.PixelFormat]::Format32bppArgb)
$g = [Drawing.Graphics]::FromImage($bmp)
$g.SmoothingMode = 'AntiAlias'
$g.TextRenderingHint = 'ClearTypeGridFit'
function Brush($r, $gg, $b) { New-Object Drawing.SolidBrush ([Drawing.Color]::FromArgb($r, $gg, $b)) }
function Fnt($size, $style = 'Regular') { New-Object Drawing.Font('Segoe UI', $size, [Drawing.FontStyle]::$style, [Drawing.GraphicsUnit]::Pixel) }
function Text($s, $font, $brush, $x, $y) { $g.DrawString($s, $font, $brush, [single]$x, [single]$y) }
$g.FillRectangle((Brush 32 34 42), 0, 0, $W, $H)
$g.FillRectangle((Brush 24 26 32), 0, 0, 260, $H)
$white = Brush 236 238 243
$grey = Brush 150 155 168
$body = Brush 205 210 220
# боковая панель — короткие подписи, на них и сыпался фильтр
Text 'Быстрый доступ' (Fnt 12) $grey 20 18
Text 'Рабочий стол' (Fnt 14) $white 20 46
Text 'Загрузки' (Fnt 14) $white 20 76
Text 'Документы' (Fnt 14) $white 20 106
Text 'Изображения' (Fnt 14) $white 20 136
Text 'Этот компьютер' (Fnt 14) $white 20 176
Text 'Проекты' (Fnt 14) $white 20 206
Text 'Стрелец' (Fnt 14) $white 20 236
# панель инструментов
Text 'Создать' (Fnt 13) $white 290 20
Text 'Вырезать' (Fnt 13) $white 370 20
Text 'Копировать' (Fnt 13) $white 460 20
Text 'Переименовать' (Fnt 13) $white 570 20
Text 'Сортировать' (Fnt 13) $white 700 20
Text 'Просмотреть' (Fnt 13) $white 810 20
# заголовки таблицы
Text 'Имя' (Fnt 13) $grey 290 70
Text 'Дата изменения' (Fnt 13) $grey 600 70
Text 'Тип' (Fnt 13) $grey 820 70
Text 'Размер' (Fnt 13) $grey 980 70
# строки таблицы: имена файлов латиницей рядом с русскими типами
$rows = @(
@('config.json', '27.07.2026 13:05', 'Файл "JSON"', '3 КБ'),
@('cache.json', '27.07.2026 13:17', 'Файл "JSON"', '18 КБ'),
@('sample.png', '27.07.2026 13:05', 'Файл "PNG"', '41 КБ'),
@('ScreenTranslator.exe', '27.07.2026 13:17', 'Приложение', '81 КБ')
)
$y = 104
foreach ($r in $rows) {
Text $r[0] (Fnt 14) $body 290 $y
Text $r[1] (Fnt 13) $body 600 $y
Text $r[2] (Fnt 13) $body 820 $y
Text $r[3] (Fnt 13) $body 980 $y
$y += 30
}
# связный русский абзац
$para = @(
'Выберите файлы, которые нужно переместить в архив. После подтверждения',
'операция будет выполнена в фоновом режиме, а исходные файлы останутся',
'на месте до завершения проверки контрольных сумм.'
)
$y = 300
foreach ($line in $para) { Text $line (Fnt 15) $body 290 $y; $y += 26 }
Text 'Элементов: 6' (Fnt 12) $grey 290 420
Text 'Выбран 1 элемент: 73,5 КБ' (Fnt 12) $grey 420 420
$g.Dispose()
New-Item -ItemType Directory -Force -Path (Split-Path $Out -Parent) | Out-Null
$bmp.Save($Out, [Drawing.Imaging.ImageFormat]::Png)
$bmp.Dispose()
Write-Host "Русский эталонный кадр: $Out"