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

160 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Улучшение графики локальной нейросетью (дизайн)
Клиент у себя поднимает генератор изображений (SwarmUI; также ComfyUI и A1111), браузер ходит к нему
напрямую — как к `local-translator` ([LOCALIZATION.md](LOCALIZATION.md)). Сервер библиотеки о генераторе
не знает; он получает только готовые файлы через уже существующую замену ассетов в моде
(`PUT /api/versions/{id}/asset`, [MODDING.md](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](https://github.com/mcmonkeyprojects/SwarmUI/blob/master/docs/API.md),
[параметры SwarmUI](https://github.com/mcmonkeyprojects/SwarmUI/blob/master/docs/APIRoutes/T2IAPI.md),
[ComfyUI API](https://github.com/comfyanonymous/ComfyUI/blob/master/script_examples/basic_api_example.py),
[A1111 API](https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/master/modules/api/models.py).