Check a generated ad image against your brand hex color in Python
The Image API takes a prompt, not a color parameter. Measure each result against your brand hex with Pillow and flag drift before the image goes in an ad.

Name the hex in the prompt, then measure the result: the Image API lists no color or palette parameter, so a brand color reaches the model only as words, and you verify it afterwards. Pillow can pull the five most common colors from an image and report how far the closest one sits from your brand hex.
The distance is a screening number, not a color-science verdict. Lighting, shadows and compression all shift pixels, so set a loose threshold and look at what gets flagged.
Why measure instead of eyeballing?
A batch of 40 images hides drift. One slightly teal blue in a set that should be navy is easy to miss in a grid and obvious in a number. Measuring also gives you a record you can attach to the job id.
What does the check do?
It shrinks the image, reduces it to five colors with median cut, sorts by pixel count and returns the Euclidean distance in RGB from your brand color to the nearest of them.
from PIL import Image
def top_colors(path, k=5):
im = Image.open(path).convert("RGB")
im.thumbnail((200, 200))
q = im.quantize(colors=k, method=Image.Quantize.MEDIANCUT)
pal = q.getpalette()
found = sorted(q.getcolors(), reverse=True)
return [tuple(pal[i * 3:i * 3 + 3]) for _, i in found]
def hex_to_rgb(h):
h = h.lstrip("#")
return tuple(int(h[i:i + 2], 16) for i in (0, 2, 4))
def drift(path, brand_hex):
brand = hex_to_rgb(brand_hex)
return min(
sum((a - b) ** 2 for a, b in zip(c, brand)) ** 0.5
for c in top_colors(path)
)
# Demo with a generated test file; swap in a downloaded ad image.
Image.new("RGB", (64, 64), (30, 80, 170)).save("demo.png")
print(round(drift("demo.png", "#1F4EA8"), 1))What threshold should I use?
Start near 40 on the 0 to 441 RGB scale, then adjust by looking at ten known good and ten known bad images from your own batches. If your brand color is small, such as a button, crop that region first; the whole-image palette will not see it.
What do I do with a flagged image?
Edit it with a reference image and a prompt that names the color again, or regenerate. If the exact brand color matters on a logo or button, add that element in code afterwards, the way the headline post does, instead of trusting a model to match it.
Sources
Related posts
More in Developers
- China's implicit AI label: provider name and content ID in metadata
China's implicit AI label is metadata with the provider name and a content ID. Why a re-encode can drop it, and how to check a delivered file with ffprobe.
- Choose an image model in code: filter GET /v1/images/models
Pick a Sume image model by what the job needs: references, 4:5, a mask, transparency. A Python filter over the catalog descriptors, with the price lookup.
- Claude Code routines: offset schedules and a Sume Format run
New Claude Code routine schedules start a few minutes past the hour, and unstarted runs show Failed. Key each Sume Format run to the tick so retries are safe.
- Claude structured outputs drop minimum and maxLength; Sume keeps them
Anthropic lists minimum, maxLength and recursion as unsupported in structured outputs. Sume's output_schema accepts the first two; here is what differs.
Written by Sume