16 KiB
Улучшение графики локальной нейросетью (дизайн)
Клиент у себя поднимает генератор изображений (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:
- Список изображений версии (фильтр по папке/размеру/уже обработанные), миниатюры.
- Выбор пресета, предпросмотр «до/после» на одном файле (слайдер).
- Запуск пачки: для каждого файла — скачать оригинал → бэкенд → превью в списке → при «Применить»
загрузка в мод. Загрузка последовательная (каждая замена меняет
revision; клиент берёт актуальную из ответа). Для скорости позже — один пакетный эндпоинтPOST …/assetsс одной ревизией. - Откат — обычная история ревизий; «вернуть оригинал» = восстановление блоба из родителя.
Что нужно поменять на сервере
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.
Этапы
- Каркас:
imager/(клиент SwarmUI, пресеты, очередь, кэш),ImagerSetup. assetScaleв схеме, рендерере и валидаторе; режим «на лету» в плеере.- Редактор массовой обработки + загрузка в мод + лосслесс-замена.
- Адаптеры 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.