Grayscale value check for AI product images: does it read at a glance?
Convert a Sume image to grayscale, measure the contrast between product and background with ImageStat, and flag low-value-contrast images before they ship.

Convert to grayscale and compare the mean brightness of a center box, where the product usually sits, with the mean of the border strip around it. If the two means differ by less than about 40 out of 255, the product will blend into the background on a phone or in print, whatever its hue. The check takes under ten lines and finds problems that a color check misses.
Why value, not hue
Designers squint at an image to judge its value structure: light against dark. Two colors with very different hues, such as a red mug on a green cloth, can have the same brightness. In grayscale the mug vanishes. Color-blind viewers, thumbnails, and black-and-white print all see that grayscale version.
A brand color check measures color distance. It will happily pass the red-on-green image. A value check catches it.
A rough measure in code
The script takes the central 50 percent box as the subject area and the outer 10 percent frame as the background. It is a rough proxy, since the subject is not always in the center, so use it to flag and not to reject outright.
import os, io, requests
from PIL import Image
H = {"Authorization": "Bearer " + os.environ["SUME_API_KEY"]}
def gen(**body):
r = requests.post("https://api.sume.com/v1/images", json=body, headers=H, timeout=60)
r.raise_for_status()
if r.status_code == 202:
raise SystemExit("queued, read /v1/jobs/{id}/result: " + r.text)
return r.json()
def fetch(u):
return Image.open(io.BytesIO(requests.get(u, timeout=60).content))
from PIL import ImageOps, ImageStat
out = gen(model="google/nano-banana-2", aspect_ratio="1:1",
prompt="Red ceramic mug on a green tablecloth, flat lay, product photo")
g = ImageOps.grayscale(fetch(out["data"][0]["url"]).convert("RGB"))
w, h = g.size
center = g.crop((w // 4, h // 4, 3 * w // 4, 3 * h // 4))
frame = [g.crop((0, 0, w, h // 10)), g.crop((0, 9 * h // 10, w, h)),
g.crop((0, 0, w // 10, h)), g.crop((9 * w // 10, 0, w, h))]
bg = sum(ImageStat.Stat(f).mean[0] for f in frame) / 4
fg = ImageStat.Stat(center).mean[0]
gap = abs(fg - bg)
print("subject", round(fg), "background", round(bg), "gap", round(gap), "cost", out["usage"]["cost"])
print("LOW VALUE CONTRAST" if gap < 40 else "OK")Reading the gap
Treat the thresholds as a starting point and tune them on images your team has already judged by eye.
| Gap (0-255) | Reads as | Action |
|---|---|---|
| Under 25 | Blends in | Regenerate with a lighter or darker backdrop in the prompt |
| 25 to 40 | Weak | Review by eye |
| Over 40 | Clear | Pass |
Make the prompt help
- Name the value relation: dark product on a light backdrop, or the reverse.
- Avoid mid-tone backdrops with mid-tone products.
- Add rim light or a backlight for dark-on-dark scenes.
- Run the check on the thumbnail size your page uses, since small sizes lose subtle differences first.
Use it with the other checks
Value contrast is one gate. Add the delta E brand check for color, the duplicate finder to drop repeats, and keep them all in the regression test. A failed gate costs only the next call, which is a few cents on most Sume image models.
Sources
Related posts
More in Media tools
- H3 Max Recast: 1-4 person photos, 768p default, 1080p opt-in
H3 Max Recast swaps the people in one 5-30 s video using 1-4 reference photos, one per new person. Output is 768p by default; 1080p is opt-in.
- Hailuo 3.0 clips in a Sume timeline: set output.fps or omit it
Cutting 24 fps AI clips into a timeline at the wrong frame rate causes judder. Sume's Timeline output.fps accepts 24, 25, 30 or 60, or matches your sources.
- Hand an AI clip to an editor: MP4, PNG frames and WAV on Sume
What Sume can deliver for a finishing editor: MP4 clips, lossless PNG stills at source size, and sample-exact WAV audio. No EXR or HDR is documented.
- Headphones listing video: spec card on the left, clip on the right
Build a 16:9 listing video for headphones with Timeline compose: a vertical stack with the spec card left, 0.35 ratio, clip on the right, $0.02 a shot on Sume.
Written by Sume