The Jerusalem PostUS Congresswoman Ilhan Omar's home raided in DOJ immigration probe - reportESPN DeportesBrewers vino de atrás ante Dodgers y abrió con triunfo SCLNESPNHollywood Allen: How the Bills QB has gone from MVP to 'cool as a cucumber' on setPunchUNHCR sounds alarm over funding crisisDaily MaverickMADLANGA COMMISSION: Ipid searches Hawks officer’s home after Madlanga extrajudicial killing allegationsBollywood HungamaJailer 2 Hindi release: Makers clarify Rajinikanth-starrer will hit theatres on October 15; say, “There has been no change”Inquirer‘Kutseros’ preserving Vigan heritage get rice aid3DNewsВстроенная графика AMD почти догнала GeForce RTX 5060 в играх — первые тесты мини-ПК на Ryzen AI Max+ Pro 495ZDF heuteEntdecken Sie das ZDF-NachrichtenstudioGlobal NewsTurkey gravy brand recalled due to undeclared mustardBBC NewsGarvaghy Road residents fail in bid to challenge Drumcree decisionNMENME’s top 10 shows from Beyond The Music 2026
The Daily Newsstand · Free, Always
Monday, October 12, 2026

Как обойти проверку SynthID

Translate

Кадр один: девочка у пруда, утки в траве, осенние деревья, 1536 × 1024. Я мял его обработкой и снова получал одно и то же. SynthID обнаружен. Картинка уже выглядела хуже. Проверка по-прежнему находила знак.

Сработал другой ход. Сгенерировать вторую версию сцены через ControlNet Canny, взять из неё полосу текстуры и подмешать в исходник. Ручная проверка этого файла вернула not detected. Трава изменилась. Утки остались утками.

Два разных следа

EXIF, текстовые поля PNG и C2PA хранят сведения о файле и его происхождении отдельно от пикселей. Пиксельный знак живёт в самом изображении. Файл можно пересохранить без метаданных и оставить такой знак читаемым.

У SynthID второй вариант. Google DeepMind описывает его как невидимый человеку водяной знак и говорит об устойчивости к обычным преобразованиям: обрезка, фильтры, сжатие с потерями. Меня интересовала граница этой заявленной устойчивости на конкретной картинке, не устройство знака вообще.

Проверял через OpenAI Verify. У сервиса разные результаты для поддерживаемых сигналов, включая C2PA и SynthID. Я смотрел SynthID. Пустые метаданные не заменяют отрицательный результат по пиксельному знаку.

not detected значит, что проверка не нашла поддерживаемый знак. Это не сертификат съёмки на камеру. Так отрицательный результат читает и OpenAI.

Картинку испортить легче, чем изменить ответ детектора

Первый инструмент был приземлённым. Небольшая геометрическая деформация, изменение размера, размытие, цветовые возмущения, шум, JPEG. Несколько проходов, если одного мало.

Идея казалась здравой: сигнал слабый, достаточно расшатать пиксели.

На этом кадре — недостаточно.

Дальше заходы тяжелее. GAN-реставрация с увеличением и обратным уменьшением. Растр. Имитация цепочки «напечатали — пересняли»: перспектива, муар, виньетка, зерно бумаги, повторное сжатие. Картинку так можно сделать совсем непривлекательной. Детектор от этого ещё не обязан сдаться.

Журнал короткий. Все строки — одна сцена и ручные проверки, не бенчмарк.

Обработка

Записанный результат SynthID

Сильная классическая цепочка с шумом и JPEG

Обнаружен

GAN-реставрация

Обнаружен

Растр и имитация печати с пересъёмкой

Обнаружен

Проверенные перерисовки с ControlNet Tile

Обнаружен

Проверенные перерисовки с ControlNet Canny

Не обнаружен

Полосовая замена с Canny-донором, keep 0.10 и 0.12

Не обнаружен

Из таблицы нельзя вывести, что JPEG или реставрация никогда не меняют результат SynthID. Можно перестать выдавать очередной фильтр за готовый способ. Для моего исходника эти попытки не сработали.

Почему донором стал Canny

В remaster.py я использовал Stable Diffusion 1.5 и ControlNet. У Tile в условие идёт изображение с его тональной структурой. В проверенных вариантах, включая сильную перерисовку, SynthID оставался обнаруживаемым.

Canny даёт другой вид условия: карту контуров. Через этот канал модель получает границы, а не цветовую картинку. Проверенные версии сцены уже возвращали not detected.

У объяснения есть предел. Исходный тайл всё ещё поступает в img2img. Canny не обнуляет информацию об исходнике, и смена условия не доказывает, каким путём знак держался в вариантах с Tile. Я поменял условие и увидел другой ответ. Внутренности детектора от этого не открылись.

Полная перерисовка вышла слишком заметной.

Композиция оставалась. Трава получала другой рисунок, мелкие детали ехали, некоторые поверхности становились живописнее, чем хотелось. Нужный ответ проверки можно купить ценой картинки, ради которой всё затевалось.

Тогда перерисованная версия стала донором. Теперь от неё нужна была только часть текстуры.

Заменить часть деталей и оставить остальное

У кадра есть детали разного масштаба. Тонкие линии и мелкое зерно. Более широкие переходы света и цвета. Между ними текстура, которую можно выделить разностью двух размытых копий.

Для изображения I Gaussian blur с параметром sigma — G_sigma(I). В bandmix.py полоса считается так:

B(I) = G_1.2(I) - G_8(I)

Та же операция выполняется над донором D. Его полоса не подмешивается как есть. match_local_energy умножает её на локальный коэффициент, чтобы сглаженная энергия донорской полосы следовала за энергией B(I). Окно этого сглаживания — --match-window. В финале оно равно 12 пикселям. Эту подогнанную полосу я обозначаю B_matched(D):

B_mix = k * B(I) + (1 - k) * B_matched(D)
R = I - B(I) + B_mix

k — доля исходной полосы, которую оставляем. В финальном файле это 0.12. Остальные 88% смеси берутся из подогнанной донорской полосы. Исходник сохраняет две составляющие: I - G_1.2(I) и G_8(I). Они отвечают за более тонкие детали и широкие переходы тона.

Формула ещё не PNG. На выходе значения ограничиваются диапазоном канала и округляются. Эти операции тоже меняют изображение, поэтому обещать абсолютную неизменность деталей других масштабов нельзя.

С числами 1.2 и 8 тоже лучше не геройствовать. Это sigma двух Gaussian-фильтров в пикселях. У фильтров нет резкой частотной границы. Один кадр не рисует карту, где живёт любой SynthID. Я нашёл параметры полезной обработки для этой картинки.

Дальше я двигал долю исходной полосы.

При 10% проверка давала not detected. При 12% тоже. При 15% знак снова обнаруживался, и с подгонкой контраста, и без неё. Разница небольшая. На этом изображении она оказалась существенной.

Результат в пикселях

Оба файла лежат в репозитории целиком, не только уменьшенные превью.

Исходник

Результат

Открыть исходник и результат в полном размере.

Величина

Значение

Размер обоих изображений

1536 × 1024

Средняя абсолютная разница значений каналов относительно исходника, шкала 0–255

16.91

PSNR относительно исходника

20.68 dB

Записанный ручной результат SynthID от 9 октября 2026 года

Не обнаружен

Метрики пересчитаны по двум PNG без вызова детектора; их можно проверить по опубликованным файлам. Ответ SynthID в таблице — отдельное наблюдение от 9 октября. Наличие файлов на GitHub проверку заново не запускает.

Что видно человеку? Сцена узнаваема. Самые броские изменения сидят в густой растительности: траве, тростнике, переплетении тонких деталей. В маленьком превью это легко пропустить. В полном размере уже труднее.

Средняя разница 16.91 не становится «незаметной правкой» оттого, что она меньше ранних попыток. PSNR измеряет близость пикселей. Вкуса у него нет, и одним числом вид не гарантируется.

Результат ближе к исходной сцене, чем полная перерисовка. До невидимого изменения я не добрался.

Как устроен проект

Неудачные ветки лежат рядом с основной. Они нужны для сравнения и не притворяются взаимозаменяемыми решениями.

Файл

Роль

remaster.py

Донор с ControlNet Canny, Tile или отключённым условием

bandmix.py

Выделяет полосы, подгоняет локальную энергию и смешивает их

sinthid_remover.py

Классическая цепочка, загрузка и сохранение, метрики

restore.py

GAN-реставрация с локальным checkpoint

recapture.py

Имитация печати и съёмки

mottle.py

Плавные тональные и цветовые возмущения

provenance_check.py

Клиент API проверки происхождения, если доступ есть

Тесты проверяют воспроизводимость классической цепочки, размеры, прозрачность, режимы изображения, метаданные, метрики и ошибки. Отдельные ML-тесты проверяют геометрию тайлов и режимы условия. Внешний детектор они не вызывают и модели не скачивают. Зелёный pytest не равен успешному результату SynthID.

Повторить обработку

Опубликованное окружение — Python 3.14. Лёгкая часть использует Pillow и NumPy. Генерация донора вынесена отдельно: PyTorch, Diffusers, ControlNet. Рабочий ML-прогон был на macOS 14 или новее, Apple Silicon, MPS. Нет MPS — код выбирает CPU. Ветки CUDA нет, и на CUDA этот проект не проверялся. Скорости того прогона от CPU ждать не стоит.

Установка из корня клонированного репозитория:

python3.14 -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
python3.14 -m venv .venv-restore
.venv-restore/bin/python -m pip install -r requirements-diffusion.txt
mkdir -p candidates

Первый запуск генератора скачивает несколько гигабайт весов. Сами веса в Git не лежат.

Сначала донор. Здесь нынешний универсальный промпт проекта:

.venv-restore/bin/python remaster.py \  autumn-10x15-yellow-v2.png candidates/donor.png \  --controlnet lllyasviel/control_v11p_sd15_canny \  --control-mode canny --control-blur 0 --pre-blur 0 \  --controlnet-scale 1.0 --strength 0.9 --steps 30 \  --tile 640 --overlap 256

По умолчанию это a photograph, natural light, sharp focus, fine detail, realistic textures. Если сцене нужно более точное описание, его можно передать через --prompt.

Донор опубликованного успешного примера делался с описанием девочки в красной куртке, осеннего пруда, уток и деревьев. Команда выше создаёт новый вариант. Ему нужна собственная проверка. Нельзя заменить промпт и приписать новому файлу старый ответ детектора.

Затем полосы:

.venv/bin/python bandmix.py \  autumn-10x15-yellow-v2.png candidates/donor.png candidates/result.png \  --split-sigma 1.2 --correct 8 \  --keep-share 0.12 --match-window 12

Четыре параметра задают режим этого результата. Сдвиг полосы, порог, фаза, выравнивание и смешивание только яркости в примере не требуются. Остальное есть в --help.

Параметр

Что меняет

--split-sigma

Sigma более узкого Gaussian blur в пикселях; резкой частотной границы нет

--correct

Sigma второго, более широкого blur; ноль заменяет всё, что грубее первого

--keep-share

Долю исходной полосы в смеси, от 0 до 1

--match-window

Sigma окна, в котором энергия донора подгоняется к исходной полосе; ноль отключает подгонку

Для другой картинки заменяются входные пути в обеих командах. Размеры исходника и донора должны совпадать, иначе bandmix.py откажется писать результат. Один донор можно переиспользовать, пока перебираете параметры смеси. Десяти разным картинкам нужны десять доноров.

--overlap держите неотрицательным и меньше --tile. В команде выше это 256 и 640. Если у файла есть EXIF-ориентация, поверните пиксели сами до обработки: инструменты выбрасывают это поле и поворот не применяют.

Сравнивать удобнее PNG. Лишний JPEG добавит ещё один источник изменений. Потом файл проверяется отдельно. В репозитории есть API-клиент, но ему нужен доступ к соответствующему API. Сам факт наличия OpenAI-ключа такого доступа не обещает.

Чего этот кадр не доказывает

Серии независимых сцен у меня нет, долю удачных попыток считать не из чего. Нет доказательства, что похожей правки хватит для другой версии знака или другого проверяющего сервиса. Даже донор с тем же seed зависит от окружения, модели и настроек генерации. Совпадение на другой машине он не обещает.

Полосовая замена экономнее полной перерисовки, но материал картинки она всё равно меняет. Особенно там, где текстуры и так много. Следующий интересный шаг для меня — качество донорской текстуры и другие сцены. Ещё один шумовой фильтр из этого эксперимента не напрашивается.

Код под MIT: balyakin/synthid-remover. Если попробуете подход, полезнее всего набор из исходника, результата, параметров и ответа проверки.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.