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

На снимке публичного встроенного интерфейса, сделанном 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.io/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 432 | 3,493 |
| Без маски | 785 921 из 786 432 | 5,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 с прозрачностью, использованной здесь. Проверяйте альфа-канал и соблюдайте документированные требования к входным данным выбранного эндпоинта.


