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>
86 lines
3.8 KiB
PowerShell
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"
|