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
- A 180-second Short from six Wan 3.0 clips: cost and Timeline join
YouTube allows Shorts up to 3 minutes. Six 30-second Wan 3.0 clips at 720p cost $22.50 on Sume, plus a $0.30 Timeline join, for one 9:16 video.
- 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.
- Lobby and elevator announcements with AI voice: 12 messages for $0.09
Twelve recorded building announcements at about 150 characters each cost $0.0855 on Sume TTS. How to batch them, vary languages and reuse the files.
- 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.
Written by Sume