AI image to CMYK for print: Pillow convert and what it misses

Image.convert('CMYK') makes a print-mode TIFF from a Sume image in two lines, but it is not color-managed. The code, the gamut risk, and when to ask for an ICC.

5 min readSume
All posts

Image.convert("CMYK") turns an RGB image from Sume into a four-channel print-mode image, and save("out.tif", compression="tiff_lzw") writes it. It is a naive conversion with no ICC profile and no gamut mapping, so saturated screen colors will print duller than they looked. Use it to produce a file a printer can open, and ask the printer for their ICC profile for anything where color has to match.

What the two lines do

Pillow's CMYK conversion applies a fixed formula to each pixel. It does not know your paper, ink, or press. RGB colors that sit outside what the press can reproduce, such as vivid cyan, electric blue, and neon green, are clipped without warning. The preview on screen will look brighter than the print.

That is acceptable for a draft, a sticker proof, or a printer that does its own conversion. It is not acceptable for packaging with a brand color you promised to match.

Generate large, then convert

Print wants pixels. At 300 dpi, a 4 by 6 inch card needs 1200 by 1800 pixels. Request a high resolution on a model that supports it, keep the master as PNG, and convert only at the end so you can redo the conversion with a better profile later.

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))
out = gen(model="bytedance-seed/seedream-4.5", aspect_ratio="3:4",
          prompt="Botanical illustration of a fern, flat inks, cream paper background")
rgb = fetch(out["data"][0]["url"]).convert("RGB")
rgb.save("master.png")
cmyk = rgb.convert("CMYK")
cmyk.save("print.tif", compression="tiff_lzw", dpi=(300, 300))
print(cmyk.mode, cmyk.size, "cost", out["usage"]["cost"])
back = Image.open("print.tif")
print("reopened as", back.mode)

Format and mode facts

JPEG and TIFF both carry CMYK in Pillow. PNG and WebP do not, so a CMYK image saved to those formats fails or must be converted back to RGB.

CMYK support by format in Pillow, read 2026-10-05
FormatWrites CMYKNote
TIFFYesUse compression tiff_lzw; many print shops prefer TIFF
JPEGYesSome viewers show inverted colors for Adobe CMYK JPEG
PNGNoKeep it for the RGB master
WebPNoWeb delivery only

What this conversion misses

  • No ICC profile is attached and none is applied, so the printer sees device numbers, not intent.
  • Total ink coverage is not limited. A very dark image can exceed what a press accepts.
  • Pure black text or line art becomes four-channel black instead of K-only. Fix it in the prepress tool, not here.
  • Spot colors and Pantone matches are not possible.

Safer print hand-off

Send the RGB PNG master plus the CMYK TIFF, and ask the printer which they want. Add bleed before conversion with the mirror padding recipe, and measure brand colors on the RGB master with the delta E check. For small merchandise runs such as the coaster set, the printer's own converter is usually the better last step.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume