I have a small side project that turns pictures into coloring pages my daughter can print and color with crayons — nine of them so far, kept in one page she can browse and pick from. In an earlier post about this same project, I mentioned I was starting to wonder whether writing my own small script would beat leaning on general-purpose AI image tools for this one narrow job — turning an already-outlined picture into a clean, coloring-book-ready outline. It did. That script has been quietly converting one picture after another since. Last week it hit the first source image its original assumptions couldn’t handle, and fixing it properly meant editing the script’s rules, not just running it once with different numbers.
The Need: A Script That Only Worked on Half the Pictures
The pipeline behind these pages is a Python script that strips flat color fill out of an already-outlined picture, leaving just the colored lines behind — I’ve written about a version of this before, for a stuffed-toy drawing that came back over-filled from an AI image tool. It compares every pixel to pure white to decide what’s “outline” (close to the drawing’s edge) and what’s “fill” (deep inside a shape, safe to blank out). That comparison only makes sense if the space around the drawing is already white. Every picture I’d fed it so far happened to be that way.
Then my daughter wanted a cartoon shark sticker turned into a coloring page — a cute, rounded character sticker, navy outline, yellow and red details, sitting on a solid sky-blue square background. Nothing about the script’s logic could handle that blue field. Run as-is, it would have measured “distance from white” across the whole background too, and gotten confused about where the actual drawing started.
The Proof: One New Flag, and the Background Disappeared Cleanly
The fix was a short flood fill, run before the existing white-distance logic even starts: seed from all four corners of the image, and fill outward with white wherever the color is close enough to what’s already there. A solid one-color background gets swallowed in one pass; the actual character, sitting in the middle with different colors, is left alone.
FLOODFILL_BG = True
FLOODFILL_TOL = 80
if FLOODFILL_BG:
h, w = img.shape[:2]
ff_mask = np.zeros((h + 2, w + 2), np.uint8)
for seed in [(0, 0), (w - 1, 0), (0, h - 1), (w - 1, h - 1)]:
cv2.floodFill(
img, ff_mask, seed, (255, 255, 255),
loDiff=(FLOODFILL_TOL,) * 3, upDiff=(FLOODFILL_TOL,) * 3,
flags=4 | cv2.FLOODFILL_FIXED_RANGE,
)
That alone got most of the way there. What was left was a thin, pale halo tracing the outline of the sticker — a soft glow effect baked into the original artwork, not part of the background at all, so the flood fill correctly left it alone. The existing “is this pixel close enough to white to ignore” threshold was hard-coded low enough to keep that halo visible as a stray light-blue line. Pulling it out into its own named constant and raising it fixed that too, without touching any of the real outline colors, which sit far further from white than a pale glow does:
WHITE_TOL = 60 # raise this if a pale halo/glow survives; the real outline
# colors are much farther from white and stay unaffected
I checked the result the same way I check all of these — saved to disk, served over a local python -m http.server, opened in an actual browser tab, not just eyeballed through a chat window (an earlier picture in this same series taught me that images shown inline don’t always render on my end). Clean white background, colored outline intact, no trace of the blue.
The source sticker was a well-known licensed kids’ cartoon character, so I’m not reproducing that original image here — a rounded smiling face, navy outline, flat yellow/white/red fill, sitting on that solid sky-blue square. What the script produced from it is generic enough to show on its own: a plain outline, nothing that belongs to anyone but this printout.

Total cost: $0. Everything ran locally — OpenCV, Pillow, a few seconds of CPU time.
The Story: Fixing the Skill File, Not Just the Picture
I could have stopped there. The picture was done, the page was live, the card was added to the collection. But before wrapping up, I went back and asked for one more thing:
💬 Prompt that worked “Make it so I can see the original image. And update the skill too.”
That’s a short prompt, and on its own it doesn’t look like much. What it actually asked for was two different fixes at two different altitudes. “Let me see the original” was about this picture — the approval step had shown the converted result, but the local server showing the before/after comparison had already been shut down, so there was nothing left to look at. “Update the skill too” was about every future picture — it was a flag that the fix I’d just made (flood-fill background removal, the halo threshold) needed to live somewhere more permanent than this one conversation, so the next source image with a colored background wouldn’t require rediscovering the same fix from scratch.
That second half is the habit I keep coming back to in this whole project: when a fresh AI session fixes something, the fix is only as durable as where it gets written down. A patch that lives only in a finished conversation is gone the moment that conversation ends. The same patch, written into the instructions the AI reads back before starting the next similar job, survives.
The How: Two Small, Permanent Additions
Two files changed, and both changes were meant to outlive this one picture.
The script itself got the flood-fill preprocessing step and the renamed WHITE_TOL constant shown above — both off by default in spirit (the flood fill is a flag you turn on, the threshold has a sane default), so the next picture that does already have a white background isn’t affected.
The skill file — the instructions an AI coding assistant reads before starting this kind of task — got two additions:
🗂 Claude.md Rule The approval step for a converted coloring page must show the original image alongside the result, not just the result — and it must say so explicitly, because “show the result” alone was already the old instruction and it wasn’t enough. Even after the local preview server is shut down at the end of a task, the original stays reachable: the project’s reference notes always link to it by its saved path, so pointing someone back to that link works without spinning up a server again.
That rule is small, but it’s the kind of thing that would otherwise get relearned the same way every time — once per project, once per source image, once per person who tries this pattern and hits the same dead end I did. Writing it into the skill file means the next run starts already knowing it.
I keep a running note of every picture that goes through this pipeline — what it needed, which script options worked, what broke the first time. This shark sticker is entry nine. The two lines added to the skill file this time weren’t really about the sticker. They were about entry ten, whatever it turns out to be.
Key Takeaways
- A script that assumes “the background is already white” will silently misbehave the first time it isn’t — a four-corner flood fill (
cv2.floodFill,FLOODFILL_FIXED_RANGE) turns a solid-color background into white before the rest of the pipeline runs. - A soft glow/halo baked into source artwork isn’t the same as a background color and won’t be caught by a background-specific fix — a separate, named “how close to white counts as white” threshold catches it without disturbing real outline colors.
- Verify image output the same way every time (saved to disk, served locally, opened in an actual browser) rather than trusting an image shown inline in a chat window.
- When an AI coding session fixes something, the fix only outlives that conversation if it gets written into the instructions the AI reads next time — a patched script without a patched skill file just means relearning the same fix later.
- Total cost: $0.