GPT Image: проверьте маску и пиксели вне области правки

Проверьте правку GPT Image по маске и сравните пиксели за пределами целевой области. В статье — реальные результаты, проверка альфа-канала и воспроизводимые входные данные.

Трафаретный лист с чёрным рисунком на светлой карточке, шалфейный фон, коралловые точки и надпись GPT Image Masks.

Маска указывает запросу на редактирование изображения, где нужно внести изменения, но остальные части полученного изображения всё равно следует проверить. В нашем тесте GPT Image 2.5 Flare отредактированный с маской фон за пределами целевой полосы выглядел почти так же, как исходный, однако сравнение пикселей выявило там различия. Результат не был точечной правкой без потерь.

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

Англоязычный публичный интерфейс Ofox для работы с изображениями с уведомлением о том, что доступна только генерация

На снимке публичного встроенного интерфейса, сделанном 12 октября, показано уведомление о том, что в этом интерфейсе редактирование изображений недоступно. Результаты редактирования в статье получены через показанный ниже маршрут API, а не в этом интерфейсе. В представленном снимке не следует искать элементы управления маской или загрузкой референсного изображения.

Задайте область до отправки запроса

Мы повторно использовали созданное Ofox 13 сентября исходное изображение: янтарный флакон без брендинга с крышкой цвета слоновой кости. Это синтетическое изображение товара, а не фотография товара клиента. Исходник — PNG размером 1024 × 1024. На нём нет текста на этикетке, поэтому этот тест ничего не говорит о сохранении логотипов, состава или обязательной маркировки.

Исходное синтетическое изображение янтарного флакона, использованное для редактирования

Целевая область намеренно проста: первые 256 столбцов слева, от x = 0 до x = 255. Флакон находится за пределами этой полосы. Мы просим заменить фон только в полосе на приглушённый шалфейный оттенок, оставив флакон, крышку, их положение и остальные области без изменений.

Маска — это PNG RGBA тех же размеров. В редактируемой полосе значение альфа-канала равно нулю, а за её пределами маска полностью непрозрачна. Изображение в оттенках серого, которое лишь выглядит чёрно-белым, не заменяет эту маску с альфа-каналом. В справочнике OpenAI по редактированию изображений описаны прозрачные области маски и связь между маской и исходным изображением; всегда проверяйте актуальные ограничения эндпоинта на входные файлы.

Проверьте маску локально

Перед загрузкой проверьте режим файла, размеры и распределение значений альфа-канала. Этот код проверяет наш тестовый файл; скорректируйте область в соответствии с задуманной правкой.

from PIL import Image

source = Image.open("source.png")
mask = Image.open("mask-left.png")
assert source.size == mask.size == (1024, 1024)
assert mask.mode == "RGBA"
alpha = mask.getchannel("A")
assert alpha.crop((0, 0, 256, 1024)).getextrema() == (0, 0)
assert alpha.crop((256, 0, 1024, 1024)).getextrema() == (255, 255)

Эти проверки выявляют инвертированный или сведённый альфа-канал до платного запроса. В обычном редакторе проверяйте реальную прозрачность, а не ориентируйтесь на цвет маски в режиме предпросмотра. Сохраните исходное изображение и маску отдельно, чтобы результат не перезаписал ни один из файлов.

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

Отправьте запрос на редактирование

В успешном тесте использовался openai/gpt-image-2.5-flare и эндпоинт Ofox /v1/images/edits. В запросе были параметры size=1024x1024 и n=1, без явно заданного качества. В ответе было указано quality: low; это зафиксированное поле ответа, а не обещание, что такое значение будет по умолчанию в каждом запросе.

curl --silent --show-error --fail-with-body \
  --connect-timeout 15 --max-time 120 \
  'https://api.ofox.ai/v1/images/edits' \
  -H "Authorization: Bearer $OFOX_API_KEY" \
  -F 'model=openai/gpt-image-2.5-flare' \
  -F 'image=@source.png;type=image/png' \
  -F 'mask=@mask-left.png;type=image/png' \
  -F 'size=1024x1024' -F 'n=1' \
  -F 'prompt=Change only the background strip at the far left quarter to muted sage green. Keep the amber bottle, cap, its position, and every other area unchanged.' \
  -o edit-response.json

Перед запуском безопасно задайте OFOX_API_KEY. Команда может повлечь расходы на использование API. Перед декодированием прочитайте сохранённый ответ: в ответе с ошибкой нет изображения для декодирования. В наших успешных ответах содержалось data[0].b64_json; декодируйте это поле из base64, сохраните байты и проверьте, что изображение открывается и имеет ожидаемые размеры.

В архиве для скачивания есть исходное изображение, маска, полученные файлы и скрипт с ограниченной областью действия, выполняющий эту операцию. В общем руководстве по API GPT Image описаны первоначальный доступ и сохранение ответа с изображением.

Сравните два реальных результата

Мы один раз отправили тот же текстовый запрос на редактирование без маски и получили успешный результат. Первая попытка с маской завершилась тайм-аутом на стороне клиента; более поздний запрос с маской прошёл успешно. Тайм-аут клиента не доказывает, что генерация не состоялась или плата не была списана, поэтому при последующей сверке расходов не исключайте эту попытку.

Реальная правка с маской: ровная шалфейная полоса слева от изображения флакона

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

Реальная правка без маски: текстурированная шалфейная полоса слева

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

Мы сравнили декодированные значения RGB в области x = 256–1023 по всем 1024 строкам. В этой области 786 432 пикселя. Пиксель считается изменённым, если отличается хотя бы один из трёх каналов, даже всего на один уровень.

Успешный результатИзменено пикселей за пределами целевой полосыСредняя абсолютная разница каналов, шкала 0–255
С маской784 316 из 786 4323,493
Без маски785 921 из 786 4325,464

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

Повторите проверку защищённой области

Используйте скачанные исходные файлы, а не снимки экрана, JPEG-превью или уменьшенные миниатюры. Такие преобразования вносят отличия, не связанные с правкой модели.

from PIL import Image, ImageChops, ImageStat

a = Image.open("source.png").convert("RGB")
b = Image.open("edited.png").convert("RGB")
if a.size != b.size:
    raise ValueError("Output dimensions changed; inspect before comparing")
region = (256, 0, 1024, 1024)
diff = ImageChops.difference(a.crop(region), b.crop(region))
changed = sum(any(pixel) for pixel in diff.getdata())
mean_difference = sum(ImageStat.Stat(diff).mean) / 3
print(changed, mean_difference)

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

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

Разберитесь с ошибками до повторного запроса

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

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

Если миниатюра выглядит хорошо, перед приёмкой проверьте исходное изображение в полном размере. Для товаров с маркировкой добавьте отдельные проверки текста и логотипов: на этом тестовом флаконе без маркировки нельзя проверить такие свойства. Если нужно изменить всю сцену, сохранив товар, см. тест с одним или двумя референсами.

Часто задаваемые вопросы

Гарантирует ли маска, что все остальные пиксели останутся идентичными?
В нашем успешном примере с маской значения RGB за пределами целевой области сохранились не полностью. Если точное сохранение — обязательное условие сдачи результата, проверяйте пиксели явно.
Результат с маской всегда лучше?
Этой пары недостаточно, чтобы сделать такой вывод. В ней среднее отклонение в защищённой области было меньше у результата с маской, но оба результата всё равно нужно принимать по визуальным критериям конкретной задачи.
Можно ли использовать чёрно-белый JPEG вместо маски с альфа-каналом?
Не вместо маски PNG с прозрачностью, использованной здесь. Проверяйте альфа-канал и соблюдайте документированные требования к входным данным выбранного эндпоинта.