Скрипт превращает изображение в seamless текстуру,
которую можно тайлить любое число раз (
Вход: 4 изображения — images/main.png (основное) и images/01.png, images/02.png,
images/03.png (для проверки). Для каждого нужно получить seamless текстуру. После
тайлинга все внутренние доски/плитки должны сохранять одинаковый размер (особенно при переходе одного тайла в другой),
без стыков, неровностей и обрезанных досок. Разрешение/соотношение сторон выходного изображения может
отличаться от входного, но качество текстуры должно быть не хуже входного.
| Изображение | Узор | Результат текстуры 3x3 |
|---|---|---|
main.png |
каменная сетка | Отличный (нет стыков, неровностей, обрезанных плит; одинаковый размер плиток) |
01.png |
паркет «ёлочка» | Хороший, но требует небольших доработок (есть малозаметные стыки при переходе одного тайла в другой, но одинаковый размер плиток) |
02.png |
светлые деревянные доски | Отличный (нет стыков, неровностей, обрезанных досок; одинаковый размер досок) |
03.png |
тёмные деревянные доски | Отличный (нет стыков, неровностей, обрезанных досок; одинаковый размер досок) |
Полноразмерные результаты в results/: *_seamless.png и
*_tiled_3x3.png. Разметка модели в marked/.
| Метод | Seamless? | Одинаковый размер досок / нет обрезки | Качество в сравнении с оригиналом | Универсальность |
|---|---|---|---|---|
| Простая обрезка и зацикливание | нет | случайная обрезка режет доски | = оригинал | да |
| Зеркальное отображение (отражение) | да | разный размер, нет обрезки (ещё минус: видна симметрия) | = оригинал | да |
| Периодическая обрезка + растушёвка | не всегда (шов может попасть в середину тайла) | гарантировано алгоритмом | = оригинал | да |
| Генеративный SD / Нейронные сети | да | разный размер досок | могут быть галлюцинации, качество может быть хуже | да |
| Детекция швов (Оцу) + обрезка по шву, растушёвка / min-cut / зеркало | да | да | = оригинал | нет — ветвление методов и пороги, подогнанные под конкретные картинки |
| Offset + перерисовывание креста швов через нейронную сеть | да | да | модель иногда галлюцинирует | да |
| Разметка seamless текстуры через нейронную сеть + детерминированная сборка (выбран) | да | да | = оригинал | да |
Ключевая идея. У регулярного пола узор повторяется: на фото есть группа целых плиток/досок, копии которой, приложенные друг к другу со сдвигом, выстилают весь пол. Если найти такую группу, seamless-текстура собирается из реальных пикселей снимка чисто геометрически, а все разрезы проходят по межплиточным швам (ни одна плитка/доска не режется). Нейронная сеть обводит такую группу красным контуром на фото, а все остальное выполняется детерминированным алгоритмом для обработки изображений.
Pipeline (main.py):
- Разметка (
ensure_marked→generate_marked). Если файлаmarked/<имя>.jpgещё нет, модель (GEMINI_IMAGE_MODELиз.env) получает фото и рисует поверх него красный контур вокруг подходящей группы плиток. Модель может ошибаться, поэтому генерируется несколько вариантов, и каждый проверяется численно (score_marked): действительно ли обведённая область повторяется по фото со сдвигами, аккуратно ли контур обведён по швам, компактная ли получается текстура. Сохраняется лучший вариант; если все негодны, генерация повторяется (до двух раз). Готовая разметка кэшируется — повторные запуски не тратят API. - Извлечение контура (
extract_masks,align_masks). Красная линия находится по цвету, замыкается и заливается — получается маска обведённой области. Разметка — это перерисованная моделью копия фото (возможно, чуть сдвинутая), поэтому маска совмещается с оригиналом по фазовой корреляции. - Векторы повторения (
lattice_pairs). Дальше нужно узнать, на какие сдвиги смещать область, чтобы её копии сошлись краями. У правильно обведённой группы противоположные края контура лежат на «одинаковых» швах: если сдвинуть контур на шаг узора, он совпадёт сам с собой. Такие сдвиги ищутся автокорреляцией контура — её сильные пики и есть кандидаты в пару векторов повторения. Ложные кандидаты (случайные частичные совпадения) отсекаются. - Выравнивание наклона (
estimate_theta,refine_theta). Пол почти всегда снят на доли градуса неровно, из-за чего «горизонтальный» сдвиг чуть уводит вниз. Угол наклона вычисляется прямо из найденных векторов и снимается одним поворотом картинки (для ровных снимков — нулевым). - Прямоугольная ячейка (
axis_cell,coset_shifts). Дляnp.tileнужен прямоугольник, который повторяется строго вправо и вниз, а найденные сдвиги могут быть «косыми». Тогда из целочисленных комбинаций сдвигов строится прямоугольник побольше, в котором укладывается ровно целое число копий области (например, 2 или 5 со смещением друг относительно друга). - Выравнивание освещения (
flatten_lighting). Из фото вычитается плавный градиент яркости. Без этого даже идеально сшитая текстура тайлится «полосами»: каждый тайл чуть темнее с одного края. - Сборка (
assemble). Пиксели области раскладываются по прямоугольной ячейке во всех положенных копиях; то, что выходит за край ячейки, продолжается с противоположного края. Сборка «мягкая»: контур модели не идеален, поэтому вокруг области берётся запас до 64 px, и на стыках копии плавно перетекают друг в друга (вес пикселя быстро падает с удалением от «своей» области). Щели от неточной обводки заполняются пикселями пришовной зоны, нахлёсты решаются в пользу ближайшей копии. Финальная проверка: если копии не покрыли ячейку целиком, сдвиги выбраны неверно — кандидат отклоняется.
Результат тайлится по построению: границы тайла и все внутренние разрезы идут
по межплиточным швам, выбранным моделью, а доски/плитки сохраняют реальный
размер. np.tile даёт проверочные изображения N×N.
- Качество = вход: текстура состоит из реальных пикселей фотографии; генерация нигде не подменяет контент (модель только рисует контур).
- Геометрия гарантирована: разрезы и границы тайла лежат на межплиточных швах, выбранных по узору; доски не режутся, размеры не меняются.
- End-to-end pipeline, без ветвления по типу узора и подогнанных порогов: все изображения проходят через одни и те же шаги.
- Самопроверка: каждый кандидат разметки проходит численную валидацию (решётка, площадь, покрытие сборки); негодные отбрасываются и перегенерируются автоматически.
- Экономно: разметка кэшируется в
marked/; повторные запуски полностью локальны и детерминированы.
- Нужен API-ключ (Gemini) при первом прогоне или перегенерации разметки; сама разметка недетерминирована — от запуска к запуску модель может выбрать другую группу (кэш это скрывает).
- «Эффект обоев»: ячейка повторяет один и тот же контент; чем меньше выбранная группа, тем заметнее повторение.
- Результат недетерминирован из-за выделения контура
cp .env.example .env # вписать GEMINI_API_KEY (ключ Gemini API)
uv sync
uv run main.py # создаёт results/ для всех четырёх изображенийГотовая разметка лежит в marked/, поэтому запуск по умолчанию не делает
обращений к API. Чтобы перегенерировать разметку для картинки — удалите
marked/<имя>.jpg и запустите снова (uv run main.py). Модель
задаётся переменной GEMINI_IMAGE_MODEL (по умолчанию
models/gemini-3-pro-image).
.
├── images/ # входные изображения (main, 01, 02, 03)
├── marked/ # разметка модели (красный контур)
├── results/ # seamless текстуры + тайтлинг 3x3
├── main.py # весь pipeline
└── pyproject.toml # зависимости