Fix a washed-out AI image with ImageOps.autocontrast before you re-run
A flat, low-contrast result from a Sume image model needs no second paid call. Measure the brightness range, apply autocontrast with a cutoff, and compare.

Check the image's brightness range with ImageStat. If it spans far less than 0 to 255, run ImageOps.autocontrast(img, cutoff=1) before paying for another generation. It remaps the darkest 1 percent to black and the lightest 1 percent to white and stretches the rest, which fixes most washed-out results for free.
How to spot a flat image
Look at the extremes, not the average. ImageStat.Stat(gray).extrema returns the minimum and maximum brightness. A healthy photo reaches near 0 and near 255. A foggy result might run from 60 to 210.
Foggy results show up with prompts that ask for soft, hazy, pastel scenes, with some models more than others. A repeat call can return the same haze, so it is worth trying the free fix first. A second call costs the full image price again, for example $0.05 billed on Seedream 4.5.
Measure, fix, compare
The script prints the range before and after, and saves both so you can look at them. Apply the fix per channel (the default for RGB) for a stronger effect, or on a luminance-only copy to avoid color shifts via preserve_tone=True.
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="bytedance-seed/seedream-4.5", aspect_ratio="4:3",
prompt="Misty pine forest at dawn, soft pastel haze, gentle light")
img = fetch(out["data"][0]["url"]).convert("RGB")
lo, hi = ImageStat.Stat(img.convert("L")).extrema[0]
print("range before", lo, hi, "cost", out["usage"]["cost"])
fixed = ImageOps.autocontrast(img, cutoff=1, preserve_tone=True)
lo2, hi2 = ImageStat.Stat(fixed.convert("L")).extrema[0]
print("range after", lo2, hi2)
img.save("before.png")
fixed.save("after.png")Which setting to pick
Cutoff sets how aggressively extremes are clipped. Preserving tone avoids hue shifts but gives a gentler change.
| Setting | Effect | Use for |
|---|---|---|
| cutoff=0 | Uses the true min and max | Clean images with one hot pixel risk |
| cutoff=1 | Ignores 1 percent each end | Default for most results |
| cutoff=3 | Strong stretch | Very hazy scenes |
| preserve_tone=True | Stretches luminance only | When hue must not shift |
Limits
- It cannot add detail the model did not draw. A hazy image will look crisper, not sharper.
- Strong stretching can band smooth skies. Add light noise afterward if that shows.
- Brand colors may drift. Run your color check after the fix.
- If the whole series is flat, change the prompt or the model instead of fixing each file.
Make it routine
Put the extrema check in your pipeline and fix only images below a threshold such as a range under 200. Then confirm the brand colors with the delta E check, keep the fix inside your regression test so changes are visible, and keep lossless files between steps as in PNG between passes.
Sources
Related posts
More in Media tools
- Fix banding in an AI image gradient by adding light noise in Pillow
Visible steps in a sky or studio backdrop from a Sume image? Add one level of Gaussian noise before the JPEG save to break the bands. Code and amount table.
- Fix the text in a reference image before it goes into an H3 clip
Spend one cheap Ideogram 4.5 edit on a reference image's text before you spend on a MiniMax H3 clip that copies it. The order on Sume, with a request.
- Godot MP3 loop: begin point only, so loop a Sume track as wav
Godot gives MP3 and Ogg only a loop begin point, no loop end. For a Sume BGM loop, cut a sample-exact wav with timeline audio split and set the loop on import.
- Graduation video music: a 2-minute anthem with a build
Brief a 2-minute graduation video bed with a build to a triumphant moment at 1:20 for the diploma reel: one generation and a render, $0.325 on Sume.
Written by Sume