Rounded-corner tile from an AI image with an alpha mask in Pillow

Round the corners of a Sume image with a supersampled ImageDraw mask and putalpha. A radius-to-size table, a smooth edge, and a PNG you can drop on any page.

5 min readSume
All posts

Draw a white rounded rectangle on a black mode L mask with ImageDraw.rounded_rectangle, at 4 times the size, shrink the mask with LANCZOS for smooth edges, and set it as the image's alpha with putalpha. Save as PNG. A radius of about 22 percent of the side gives the familiar phone-icon look.

Why supersample the mask

Pillow draws shapes without anti-aliasing. A mask drawn at the final size has jagged corners. Draw at 4 times the size and shrink, and the corner pixels get intermediate alpha values, which looks smooth.

A rounded tile needs a transparent corner, so the output has to be PNG or WebP. JPEG has no alpha and fills the corners with black.

The code

Start from a square result. Sume models list 1:1, so request it and you avoid cropping. If you start from another ratio, crop to a square with ImageOps.fit first.

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 ImageDraw, ImageOps
out = gen(model="google/nano-banana-2", aspect_ratio="1:1",
          prompt="Minimal app tile illustration of a paper plane over a blue gradient")
size = 512
img = ImageOps.fit(fetch(out["data"][0]["url"]).convert("RGBA"), (size, size), Image.LANCZOS)
radius = round(size * 0.22)
big = Image.new("L", (size * 4, size * 4), 0)
ImageDraw.Draw(big).rounded_rectangle((0, 0, size * 4 - 1, size * 4 - 1), radius * 4, fill=255)
mask = big.resize((size, size), Image.LANCZOS)
img.putalpha(mask)
img.save("tile_rounded.png")
print(img.mode, img.size, "radius", radius, "cost", out["usage"]["cost"])

Radius by tile size

Corner radius as a share of the side sets the style. Smaller shares look like cards, and larger shares look like icons or pills.

Corner radius presets, arithmetic read 2026-10-05
StyleShare of sideRadius at 512 pxRadius at 1024 px
Card8 percent4182
App icon22 percent113225
Pill-like50 percent256512

Gotchas

  • A radius of 50 percent makes a circle for a square image. Use it for avatars.
  • Do not blur or resize the PNG with JPEG tools afterward, since the corners will fill with black.
  • If the page applies border-radius in CSS, you do not need the mask. Use it when the file itself must carry the shape, such as in an email or a store listing.
  • Supersampling 4 times on a 4096 pixel tile uses 16,384 pixels per side. Cap the final size first.

Where it fits

For icon sets, start from the app icon recipe, which covers the sizes a store asks for, then round the corners last. Derive the other widths from one master with the srcset recipe, and keep PNG all the way, see PNG between passes.

Sources

Related posts

More in Media tools

All Media tools posts

Written by Sume