Brand color check for AI images: Delta E in Python before publishing
Prompts cannot guarantee a brand hex. Measure the nearest palette color in a generated image with CIE76 Delta E and block misses. Code for Sume output.

To check an AI image against a brand color, reduce the picture to a handful of dominant colors, convert both them and your hex to CIELAB, and take the smallest Delta E between the brand color and any dominant color. A value under roughly 10 is close for most uses and under 2 is hard to tell apart by eye; set your own bar and block anything above it. Naming a hex in a prompt only nudges a model, so measuring the result is the dependable control.
Run the check on the files you get back from Sume. The request has no color-management parameter, so the only way to know what you got is to look at the pixels.
What do the Delta E numbers mean?
CIE76 is the simple Euclidean distance in Lab space. It is crude for saturated colors but cheap and adequate for a gate.
| Delta E | Typical perception | Gate |
|---|---|---|
| 0 to 2 | Hard to tell apart | Pass |
| 2 to 10 | Close, noticeable side by side | Pass for backgrounds, review for logos |
| 10 to 50 | Clearly different | Fail |
| 50 and up | A different color | Fail |
What is the script?
It uses Pillow to find five dominant colors, NumPy for the sRGB to Lab conversion with a D65 white point, and no other libraries.
import sys
import numpy as np
from PIL import Image
M = np.array([[.4124, .3576, .1805], [.2126, .7152, .0722], [.0193, .1192, .9505]])
W = np.array([.95047, 1.0, 1.08883])
def lab(rgb):
c = np.asarray(rgb, dtype=float) / 255
c = np.where(c > .04045, ((c + .055) / 1.055) ** 2.4, c / 12.92)
t = (c @ M.T) / W
f = np.where(t > .008856, np.cbrt(t), 7.787 * t + 16 / 116)
return np.array([116 * f[1] - 16, 500 * (f[0] - f[1]), 200 * (f[1] - f[2])])
def nearest(path, hexcolor):
img = Image.open(path).convert("RGB").resize((128, 128))
pal = img.quantize(colors=5).getpalette()[:15]
cols = [pal[i:i + 3] for i in range(0, 15, 3)]
b = [int(hexcolor.lstrip("#")[i:i + 2], 16) for i in (0, 2, 4)]
return min(np.linalg.norm(lab(c) - lab(b)) for c in cols)
if __name__ == "__main__":
d = nearest(sys.argv[1], sys.argv[2])
print(f"Delta E {d:.1f}", "pass" if d < 10 else "fail")What should happen on a fail?
Regenerate with a larger n and keep the candidates that pass. Sume bills each completed image, so a rejected image still cost money; track the pass rate the way the approved-image cost post tracks pick rate. If the color matters on a flat area, a model that accepts reference images can be given a swatch as an input_references entry, and for exact hexes in logos or UI, composite the color yourself after generation.
What are the limits of this check?
Quantizing to five colors can merge a small brand mark into its surroundings, so crop to the area where the color must appear before measuring. Delta E also ignores the display: sRGB values in a file look different on a calibrated print proof. Treat the number as a gate for the digital file, not a proof of printed color.
Sources
Related posts
More in Use cases
- Anime series by API: a job-queue pattern on Sume
Pika's API Club spotlights show members making anime and dramas by API. A series needs a queue: idempotent submits, polling, and a webhook as a fast path.
- 100 holiday videos in one Sume bulk run: how long with concurrency 16
A bulk queue of 100 items at concurrency 16 drains in 7 waves. Wave arithmetic from the docs' 15 to 30 minute long-form figure, and what else caps the window.
- Dubbed video: burn target-language captions with script_text
After dubbing into Spanish or German, caption the new audio with your translated script as script_text so burned words match your approved translation.
- CAWG identity assertion 1.2: who made it, not whether it is AI
A CAWG identity assertion says which named actor stands behind an asset. It does not say the asset is AI-generated. What Sume stores that you can pair with it.
Written by Sume