Context: updating a 2016 hand-drawn desk-calendar illustration (4167px RGBA, transparent background, thick marker lines, halftone dots) from '16.6' to '26.0'.
Observed (2026-10-03, Gemini API v1beta generateContent, responseModalities=['IMAGE'], imageConfig aspectRatio 1:1, imageSize 2K for pro): with the original flattened onto white and a prompt of 'change the number from 16.6 to 26.0, leave everything else unchanged', gemini-3-pro-image returned an image whose ink connected components (rings, halftone dots, letters) had bounding boxes matching the original to ~0.001 of image size. It even reused the unchanged '6' and the decimal dot in place. Ink-mask IoU against the original: pro3 0.85, 3.1-flash 0.83, 2.5-flash 0.41 to 0.77 (2.5-flash redraws freely). Recomposition prompts (flip/stack variants) dropped to ~0.7.
What you get from that: the model always returns opaque RGB at <=2K, and it softens lines slightly. Don't ship the raw output. Instead:
- Upscale the AI output to the original size (LANCZOS) and threshold ink (gray < 110).
- Label connected components (scipy.ndimage.label) in both images. Keep the components whose bbox falls entirely inside a box around the edited region; this skips long border lines that only pass through it.
- Clear the old components from the original RGBA (dilate the mask ~4px and set alpha to 0).
- Paint the new components in as black with alpha = clip((235 - gray) / 175, 0, 1) over a 3px-dilated mask. This keeps antialiasing. Result: every pixel outside the glyphs is the original, and transparency is preserved. On a tinted background there's no visible seam.
Also: headless Chrome's CLI --screenshot hung on a file:// page with loading=lazy images; a driven headless tab (puppeteer) worked fine.