I run marketing for 4 plastic surgery practices in Chicago. Every time a surgeon sends over before/after patient photos, I need to resize and brand those photos for every channel we publish to:
- Email (1072px wide, retina)
- Website gallery (1200px)
- Instagram carousel (1080x1350, 4:5)
- Stories / Reels / TikTok (1080x1920, 9:16)
- Facebook / LinkedIn / X (1200x630)
- Google Business Profile (1200x900)
- Pinterest (1000x1500)
Seven sizes. But some need a side-by-side composite and some need individual crops with labels. Real output is 9 files per photo pair.
I was doing this in Canva. One at a time. Four brands, each with different logos and surgeon names. A single photo pair took 20-30 minutes. We get 3-5 new pairs per week across the brands. That's an entire afternoon just resizing photos.
What I Built
Two Python tools. Built with Claude Code in about 3 hours across 2 sessions.
ba-composer.py does the horizontal formats, the side-by-side composites. Takes two photos and outputs:
- Rounded corner panels on a dark background
- Logo watermark at 30% opacity, centered top
- Surgeon credit line at the bottom
- All 5 horizontal sizes in one run
ba-individual.py does the vertical formats, the tall crops for Stories and Pinterest. Takes one photo and:
- Center-crops to each vertical ratio
- Adds a BEFORE or AFTER pill label
- Applies a bottom gradient so the credit text reads over light skin tones
- All 4 vertical sizes in one run
Both tools know all 4 brands. Each brand has its own logo, practice name, and surgeon roster in a shared config file. I bundled the font (Lato) as .ttf so it doesn't depend on whatever's installed on the machine.
Day-to-Day
Drop the photos in a folder. Run one command. Batch mode auto-pairs before and after images based on filename, so if a file has "Skinny BBL" in the name, it detects the procedure automatically.
Things That Broke
The XSculpt logo was 19,650px wide. 138 million pixels. Pillow (the Python image library) threw a decompression bomb warning and refused to open it. My first instinct was to raise the pixel limit in code, but that felt wrong. Just resized the logo to 2,400px instead. Problem gone.
Rounded corners clipped unevenly on certain aspect ratios in the first version. The fix was making the corner radius scale proportionally to the output size instead of a fixed pixel value. Took maybe 3 tries to get it right.
Why It Holds Up
This isn't a one-off script. Adding a new brand is one entry in the config: logo path, practice name, surgeon list. Adding a new surgeon is one line. Adding a new output size is one tuple.
Last month a new surgeon joined CAS. I added his name to the config. Done. Every photo he sends from now on gets branded correctly without me touching anything else.
The alt text generation means I'm not writing image descriptions by hand. The folder structure matches what I already use for email campaigns, so outputs drop right into the existing workflow without renaming or moving files around.
The Math
Before: 20-30 minutes per photo pair in Canva. Manual. Easy to miss a size or forget to update a surgeon name.
After: ~3 seconds per photo pair. One command.
If You're Considering Something Like This
I'm not a developer. Claude Code wrote the Python, debugged the image processing errors, and structured the config. My job was knowing what I needed: the sizes, the branding rules, where the outputs go. If you have a repetitive visual production task with consistent rules across multiple brands, this approach works. Paid for itself in the first week.