Files
pgames-manager/docs/IMAGE_ENHANCE.md
T
mrleo1nid f7f7f5b1b3
CI / frontend (push) Failing after 26s
CI / backend (push) Failing after 1m37s
feat: add local image enhancement and lossless mod assets
2026-10-25 21:41:45 +03:00

16 KiB
Raw Blame History

Улучшение графики локальной нейросетью (дизайн)

Клиент у себя поднимает генератор изображений (SwarmUI; также ComfyUI и A1111), браузер ходит к нему напрямую — как к local-translator (LOCALIZATION.md). Сервер библиотеки о генераторе не знает; он получает только готовые файлы через уже существующую замену ассетов в моде (PUT /api/versions/{id}/asset, MODDING.md).

Два режима поверх одного движка задач:

Режим Что делает Где хранится результат Видят другие
На лету плеер подменяет картинку при показе на улучшенную IndexedDB браузера, на сервер ничего не уходит нет
В исходниках редактор «Улучшение графики» массово обрабатывает файлы мод-форка блобы мода (через замену ассета) да, как обычный мод

Схема

Браузер ──► SwarmUI / ComfyUI / A1111  (127.0.0.1, разрешён CORS для сайта)
   │
   ├─ IndexedDB: кэш результатов  key = версия|путь|хэш оригинала|хэш пресета
   ├─ плеер: engine.fileUrl() → кэш, если есть улучшение
   └─ редактор: результат → PUT /asset (мод-форк), по одному файлу

Модули клиента (frontend/src/imager/)

  • backends/ — интерфейс ImageBackend { health(), models(), process(png, preset, signal) → Blob } и адаптеры swarm.ts (основной: GetNewSession, GenerateText2Image с initimage, refinerupscale/refinerupscalemethod, ListT2IParams), comfy.ts, a1111.ts.
  • presets.ts — пресет = бэкенд + режим (upscale | img2img) + параметры (модель, апскейлер, множитель, creativity, prompt/negative, steps, cfg). Хранится в localStorage, экспорт в JSON. hash(preset) входит в ключ кэша.
  • queue.ts — очередь задач: параллелизм 1 (GPU), отмена, повтор, возобновление после перезагрузки (журнал в IndexedDB), прогресс.
  • cache.ts — IndexedDB: {key, blob, scale}; очистка по версии.
  • ImagerSetup.tsx — страница настройки, как TranslatorSetup (проверка связи, CORS-подсказка).
  • ImageEnhance.tsx — редактор массовой обработки (ниже).

Масштаб изображения (важно)

Плеер рисует <img> в натуральном размере (renderer.ts), поэтому ×2 апскейл без поправки покажется вдвое больше. Решение: у каждой улучшенной картинки хранится scale (отношение новой ширины к исходной).

  • на лету — scale лежит в записи кэша;
  • в исходниках — game.json → assetScale: { "images/bg room.webp": 2 } (проверяется mods.validate);
  • рендерер: после загрузки width = naturalWidth / scale для <img> без явного размера; размеры из def.size, ATL и масок масштабом не затрагиваются.

Это единственное изменение плеера и схемы PGVN; без него замена файла на файл другого размера ломает вёрстку.

Режим «на лету»

Хук в Engine.fileUrl(): если для (версия, логический путь) в кэше есть результат — возвращается blob: URL (создаётся один раз, освобождается в destroy). Предзагрузка ближайших картинок уже использует fileUrl, поэтому работает без изменений. Фоновая обработка: при показе картинки без улучшения в очередь ставится её задача (опция «улучшать по ходу игры»), результат подхватывается при следующем показе. Переключатель «оригинал / улучшенное» в меню плеера для сравнения.

Режим «в исходниках» (редактор)

Страница доступна для kind=mod с versions.fork:

  1. Список изображений версии (фильтр по папке/размеру/уже обработанные), миниатюры.
  2. Выбор пресета, предпросмотр «до/после» на одном файле (слайдер).
  3. Запуск пачки: для каждого файла — скачать оригинал → бэкенд → превью в списке → при «Применить» загрузка в мод. Загрузка последовательная (каждая замена меняет revision; клиент берёт актуальную из ответа). Для скорости позже — один пакетный эндпоинт POST …/assets с одной ревизией.
  4. Откат — обычная история ревизий; «вернуть оригинал» = восстановление блоба из родителя.

Что нужно поменять на сервере

  • converter/assets._image: не пересжимать результат повторно в WebP q88 (потеря качества после апскейла). Для замены из этого редактора принимать PNG/WebP без перекодирования лосслесс-веткой (параметр lossless=1 в /asset).
  • лимит WEBP_MAX = 16383: при апскейле ×4 больших фонов результат уйдёт в PNG — предупреждать в UI.
  • assetScale в game.json и его проверка в mods.validate.
  • пакетный эндпоинт замены ассетов (вторая очередь работ).

Риски и открытые вопросы

  • CORS: проверить, какие заголовки отдают SwarmUI/ComfyUI/A1111 при запросах с чужого origin; страница настройки должна объяснять флаг (--enable-cors-header, --cors-allow-origins, настройка Swarm). Mixed content: сайт по https → локальный http; Chrome разрешает для 127.0.0.1, Safari нет.
  • Качество: img2img на персонажах с альфой — нейросеть не сохраняет прозрачность. Для спрайтов режим «только апскейлер» (модель ESRGAN-типа) с отдельной обработкой альфа-канала (апскейл маски обычным ресайзом, цвета — моделью). Это должно быть в первой версии, иначе спрайты получат ореол.
  • Анимации (animated WebP) и видео — вне первой версии.
  • Права на результаты: производные изображения остаются в моде пользователя, в публичные версии не попадают без versions.publish.

Этапы

  1. Каркас: imager/ (клиент SwarmUI, пресеты, очередь, кэш), ImagerSetup.
  2. assetScale в схеме, рендерере и валидаторе; режим «на лету» в плеере.
  3. Редактор массовой обработки + загрузка в мод + лосслесс-замена.
  4. Адаптеры ComfyUI и A1111, обработка альфы, пакетный эндпоинт.

Реализация (25 октября 2026)

Добавлены все четыре группы функций:

  • /imager — настройка локального генератора, проверка API из браузера, список моделей и апскейлеров, пресеты с импортом/экспортом JSON. Последний сохранённый пресет используется плеером.
  • frontend/src/imager/ — адаптеры SwarmUI, ComfyUI и A1111, общая очередь, журнал и кэш IndexedDB. Ключ включает ID версии, логический путь, хэш исходного блоба и SHA-256 параметров пресета (включая адрес генератора и фиксированный seed). Переименование пресета кэш не меняет.
  • Плеер PGVN: Настройки → Графика → «Показывать улучшенные изображения» и отдельно «Улучшать по ходу игры». Переключатели сохраняются по версии. По умолчанию оба выключены. Завершённые задания подхватываются при следующем показе изображения; URL освобождаются при закрытии.
  • /versions/:versionId/enhance — выбор картинок, фильтры пути/размера файла/готовности, выбор пресета, сравнение слайдером, обработка выбранных файлов и отдельное применение. Открывается из редактора мода или его карточки. Применять можно только в доступный для редактирования черновик. Ошибки ревизий не удаляют локальные результаты.
  • PUT /api/versions/{id}/asset?lossless=1&scale=2 сохраняет проверенные статичные PNG/WebP без повторного сжатия. scale — полный масштаб относительно исходных игровых координат, поэтому повторный апскейл умножает прежний assetScale. Файл и метаданные пишутся одной ревизией. Обычная ручная замена без scale сбрасывает прежний масштаб этого файла.
  • POST /api/versions/{id}/assets: JSON {revision, assets: [{path, filename, data, scale}]}, где data — base64 без префикса data URL. Максимум 32 картинки / 128 МБ запроса; редактор дополнительно ограничивает группу 80 МБ исходных блобов. Каждый пакет атомарен и создаёт одну ревизию. При ошибке файлы версии и её ревизия не меняются.
  • POST /api/versions/{id}/asset/restore: {revision, path} возвращает файл из замороженной базовой ревизии форка, вместе с прежним масштабом. Для отката пачки также доступна история мода.
  • Альфа-канал масштабируется отдельно в браузере и возвращается в итоговый PNG. Img2img прозрачных изображений отклоняется с предложением выбрать апскейлер. Анимации отклоняются, а не превращаются незаметно в первый кадр. Изменение пропорций генератором также отклоняется.

Отмена активного задания отбрасывает его результат, но ждёт завершения запроса перед стартом следующего: глобальный interrupt генератора мог бы остановить чужую работу. После перезагрузки незавершённые задания видны в журнале и запускаются кнопкой «Повторить». При наличии Web Locks очередь сериализует GPU-запросы также между вкладками этого сайта. При очистке кэша нужно закрыть плеер, чтобы освободить уже загруженные в память blob-URL.

Текущие адаптеры рассчитаны на локальные API без обязательной авторизации. Прямые обращения генератора не проксируются через сервер библиотеки. Для SwarmUI настройка CORS называется Network.AccessControlAllowOrigin; для ComfyUI — --enable-cors-header ORIGIN, для A1111 — --api --cors-allow-origins ORIGIN. Страница настройки проверяет доступ к API, а скачивание результата проверяется первым пробным заданием. Реальный SwarmUI на 127.0.0.1:7801 при разработке не отвечал: GPU-генерация и CORS установленного сервиса пока не подтверждены. Протоколы адаптеров покрыты тестовыми HTTP-ответами.

Проверки: frontend/tests/imager.test.mjs, frontend/tests/imager-browser.test.mjs (опциональный Playwright/Chromium, как существующие браузерные тесты), backend/tests/test_image_enhance.py. Браузерный тест проверяет реальные пиксели альфы, геометрию <img>, сохранность IndexedDB после перезагрузки, освобождение URL и отсутствие загрузок на сервер в локальном режиме.

Результат проверки реализации: 9 клиентских тестов imager, 13 серверных тестов imager/mods и отдельный тест Chromium проходят; production-сборка и Ruff проходят. Интеграционный тест с PostgreSQL пропущен без TEST_POSTGRES. В полном клиентском наборе отдельно воспроизводится сбой изменённого теста интерпретатора Ren'Py interpreted instances can own arguments without exposing host function properties (frontend/tests/renpy-runtime.test.mjs, неизвестное имя list); этот тест не импортирует imager.

Первичная документация протоколов: SwarmUI API, параметры SwarmUI, ComfyUI API, A1111 API.