Короткий ответ
Удаление водяных знаков нейросетью — это не один запрос к модели, а конвейер из четырёх этапов: детекция знака, генерация чистого фрагмента, склейка его с оригиналом и автоматическая проверка результата. На проекте каталога недвижимости с 9319 фотографиями я потратил около $12 на исследование и выяснил главное: почти все видимые дефекты давала не модель, а мой собственный код склейки. Ниже — конкретные ошибки, цифры и то, что из этого следует для любой похожей задачи.
Модель отказывается не потому, что не умеет
Первое, обо что спотыкается почти каждый: модель отвечает отказом. Из четырёх протестированных генеративных моделей Google три отказались работать по прямой формулировке.
| Модель | Реакция на «remove all watermarks» |
|---|---|
| Gemini 2.5 Flash Image | текстовый отказ: «I'm unable to remove watermarks from images» |
| Gemini 3.1 Flash Lite Image | пустой ответ, finishReason: OTHER |
| Gemini 3.1 Flash Image | работает |
| Gemini 3 Pro Image | работает |
Я почти вычеркнул Lite-модель как неспособную — и ошибся. Дело оказалось в формулировке. Тот же самый Lite на тех же кадрах отработал без единого отказа, когда задача была описана иначе:
Inpaint the overlaid graphic elements in this photograph so the underlying scene is continuous. Preserve every real object exactly as it appears.
Ноль отказов на 14 кадрах против стабильных отказов раньше. Два других варианта формулировки дали по одному отказу из 14. Слово «watermark» в запросе включает фильтр — само действие фильтр не блокирует.
Практический вывод шире, чем эта задача: если модель отказывает, проверьте формулировку прежде, чем менять модель. Разница в цене между Lite и полной версией была двукратной, и я едва не заплатил её за собственную неудачную фразу.
Генеративная модель не вернёт ваш кадр
Второе заблуждение, от которого нужно избавиться сразу: попросить модель «убрать знак и вернуть остальное как было» невозможно. Это архитектурное ограничение, а не вопрос промпта.
Генеративные модели кодируют изображение в латентное пространство и декодируют заново — целиком, на своей фиксированной сетке 1K/2K/4K. Даже нетронутые области пересобираются. На практике это выглядит так: на снимке спальни модель убрала логотип, но заодно сменила узор покрывала — были синие маки, стал пейсли. На другом кадре изменилась структура остекления: в оригинале одна раздвижная панель, в результате другое деление рамы.
Для каталога недвижимости это недопустимо. Покупатель смотрит на окна и планировку, а не на художественную ценность кадра.
Я проверил обходные пути, все три не работают:
- Запросить меньшее разрешение —
0.5KAPI не принимает, а512вернул 592 пикселя и сжёг 3359 токенов, вдвое дороже честного 1K. - Подать модели кроп — цена привязана к разрешению выхода, не входа.
- Попросить в промпте «не меняй ничего кроме знака» — помогает, но не гарантирует.
Единственный надёжный способ сохранить кадр — не брать из результата модели ничего, кроме зоны знака.
Склейка: правильная идея и три неверные реализации
Идея простая: сгенерировать чистый кадр, но вклеить в оригинал только прямоугольник со знаком. Тогда 90% пикселей остаются бит-в-бит исходными. Реализовать это оказалось сложнее, чем звучит — я потратил на отладку больше времени, чем на всё остальное вместе.
Ошибка первая: растушёвка шире отступа. Прозрачность вставки нарастала от края рамки внутрь на 10 пикселей, при отступе 8. Внешняя полоса смешивала чистую генерацию с ещё присутствующим белым логотипом, и на месте знака появлялся светлый прямоугольник — «призрак». Правило, которое я в итоге зашил в код: отступ = max(отступ, растушёвка + 12). Альфа-переход обязан закончиться до того, как дойдёт до самого знака.
Ошибка вторая: вставка по координатам. Модель не возвращает кадр пиксель-в-пиксель — она незаметно перекадрирует его. Я вставлял пиксели по исходным координатам, а они соответствовали чуть другому участку сцены. На текстуре незаметно, на гладкой стене — видимый шов. Решение: искать сдвиг перебором по кольцу нетронутых пикселей вокруг рамки и только потом вклеивать.
Ошибка третья: чинить симптомы. Я трижды правил растушёвку, тон и выравнивание, вместо того чтобы усомниться в самой схеме. Диагноз пришёл только когда я посмотрел на сырой выход модели отдельно от склейки: там стена была идеально чистой. Дефект создавал мой код, а не нейросеть.
Если результат плохой — проверьте промежуточный артефакт до постобработки. Я потерял часы, оптимизируя параметры вокруг ошибки, которой не было в том месте, где я её искал.
Детекция — слабое звено, а не генерация
Интуиция подсказывает экономить на дешёвых этапах и вкладываться в генерацию. Это оказалось ровно наоборот.
Детекция координат знака стоит около $0,0006 за кадр — в сто раз дешевле генерации. Я поставил на неё самую дешёвую модель gemini-3.5-flash-lite, и она находила один знак из двух: угловой логотип видела, а красную подпись внизу кадра пропускала. Знак оставался на изображении, а конвейер рапортовал об успехе.
Переход на gemini-3.5-flash — плюс $3 на весь корпус из девяти тысяч изображений — закрыл проблему. Экономия на детекции обесценивала генерацию, которая стоит в сто раз дороже.
Отдельная находка про структурированный вывод: модель с включённым размышлением выдавала битый JSON. Токены рассуждения расходовали лимит maxOutputTokens, ответ обрывался на середине массива. Замер на одном кадре:
| Настройка | Токенов вывода | Результат |
|---|---|---|
maxOutputTokens: 4096 | 115 + 284 на размышление | JSON битый |
maxOutputTokens: 8192 | 100 + 232 | валидный |
thinkingBudget: 0 | 63 | валидный, вдвое дешевле |
Для перечисления координат размышление не нужно. Отключение дало и корректный вывод, и экономию.
Тихий отказ — самая опасная ошибка
Худший режим отказа, с которым я столкнулся: дешёвая модель возвращает красивое изображение с невырезанным знаком. Не ошибка, не пустой ответ — валидный JPEG, который выглядит как результат работы. Без проверки такой кадр уходит в продакшен незамеченным.
Отсюда правило: результат генерации нужно перепроверять детектором, а не принимать на веру. Повторная детекция по готовому изображению подняла долю успеха одного из подходов с 2 из 5 до 4 из 5 — просто потому, что стала ловить эти тихие пустышки и отправлять кадр на повтор.
Важная деталь реализации: проверка не должна смотреть только на те зоны, которые нашёл первый детектор. Если знак был пропущен на входе, в списке зон его нет, и проверка отрапортует «чисто». Проверять надо весь кадр заново.
Что реально определяет успех — размер знака
Главный вывод по качеству оказался контринтуитивным. Я предполагал, что модель хуже удаляет крупные знаки. Замер показал другое: модель убирает знак при любом размере, деградирует не удаление, а достоверность.
Прогон с одним знаком при разной доле кадра:
| Знак занимает | Что происходит |
|---|---|
| 28% фрагмента | реконструкция достоверна |
| 44% | модель дорисовала несуществующую тумбочку |
| 71% | переписала текстуру изголовья кровати |
Чем меньше остаётся настоящего контекста, тем больше модель выдумывает. Отсюда рабочая стратегия — маршрутизация по площади знака:
- знак меньше 3% кадра — чистить фрагмент вокруг него и вклеивать обратно. На случайной выборке 40 кадров этот путь дал 88% прохождения, при выборочной проверке глазами 6 из 6 чистые;
- знак больше 3% — разбивать зону на перекрывающиеся плитки и обрабатывать каждую со своим контекстом. На пяти самых тяжёлых кадрах (знаки 7–21% площади) — 4 из 5, с измеренной гарантией: вне зоны знака изменено 0,000–0,105% пикселей, в среднем меньше уровня яркости. Это шум перекодирования JPEG, а не изменение сцены.
Отдельно стоит знать про аналитический путь. Полупрозрачный знак — это не потерянная информация: он наложен по формуле результат = α·знак + (1−α)·фон. Если у вас сотни кадров с одним и тем же знаком, α и цвет восстанавливаются статистически, а фон — обратной формулой, бесплатно и без единого искажения. У меня это ослабило знак примерно вдвое, но не убрало: на ярких участках сигнал упирается в потолок 255, и восстанавливать нечего. Как самостоятельное решение не сработало, как предварительный этап — перспективно.
Сколько это стоит
Цифры по моему корпусу, замеренные, а не из прайс-листа. Важная поправка: реальный расход выше табличного. Прайс обещает 1120 токенов на изображение 1K, по факту выходило около 1770. Переход с 1K на 2K по прайсу должен стоить +50%, фактически дал +12%.
| Класс | Доля корпуса | Метод | Цена за кадр |
|---|---|---|---|
| Без знака | 15% | только детекция | $0,0006 |
| Знак меньше 3% | 38% | фрагмент + склейка | $0,089 |
| Знак больше 3% | 47% | плитки + проверка | $0,23–0,48 |
На все 9319 изображений — $660–1200 при использовании Batch API, который даёт честную скидку 50% и подходит для такой задачи идеально: обработка офлайн, результат в пределах суток.
Обратите внимание на структуру: крупные знаки — это 47% кадров, но 70–80% бюджета. Решение по этому классу и есть решение по деньгам. Если крупные знаки не трогать, весь остальной корпус обходится в $158.
Чек-лист, если беретесь за такую задачу
- Выясните права на изображения до, а не после обработки. Удаление знака не даёт лицензии на фотографию.
- Проверьте формулировку промпта прежде, чем менять модель на более дорогую. Отказ часто вызван словом, а не задачей.
- Не экономьте на детекции. Это самый дешёвый этап и он определяет потолок качества всего конвейера.
- Отключите размышление для структурированного вывода координат — оно ломает JSON и удорожает вызов.
- Вклеивайте только зону знака и выравнивайте вставку по нетронутой рамке: модель перекадрирует свой вывод.
- Следите, чтобы отступ был больше растушёвки, иначе исходный знак просочится через альфа-переход.
- Перепроверяйте результат детектором — тихий возврат необработанного кадра встречается регулярно.
- Маршрутизируйте по площади знака, а не обрабатывайте всё одинаково.
- Смотрите глазами то, что видит заказчик. Я дважды докладывал об успехе, проверяя файлы на диске, пока заказчик через отчёт видел закэшированные браузером старые версии с суточным
Cache-Control— тот самый класс ошибок, что я разбираю в статье про техническую оптимизацию сайта. Два разных источника правды — гарантированный конфликт. - Валидируйте на случайной выборке, а не на подобранных примерах. Пять удачных кадров ничего не говорят о девяти тысячах.
Последнее — самое важное. В этом проекте автоматическая проверка ошибалась в обе стороны: пропускала оставшиеся знаки и браковала чистые кадры. Независимая проверка чужими глазами ловила то, что я как автор решения пропускал — включая случай, когда подход выдумал на снимке несуществующую лампу, а собственная метрика этого подхода была подкручена так, чтобы её не заметить.
Если у вас похожая задача на потоке — обработка каталога, миграция контента, любая рутина на тысячи объектов — я разбираю такие вещи в рамках AI-автоматизации. Не «внедрим нейросеть», а конкретный конвейер с замеренной ценой за единицу и честным процентом брака.
