Files
pak-jabit/docs/napkin-image-pipeline.md
T
juxtapoandClaude Opus 5 a58a61b98c Pak Jabit: full stack — storefront, Spree customisations, catalogue tooling
A South African men's apparel store on Spree 5.6 + Next.js 15. 160 products
live, three markets, live Malaysian courier rates, customer-owned carts.

What is here:
  storefront/  the Next.js app
  backend/     ONLY the custom Spree code. Spree itself is a prebuilt image
               that mounts no app code, so every file here needs an explicit
               volume entry in docker-compose.yml or it is silently ignored.
  catalogue/   the scripts that loaded and maintain the products
  docs/        manifest, mock registry, napkins

Real .env files are committed deliberately. This lives on the house's private
Gitea, and a clone that runs beats a clone that needs a secrets handover.
LANDMINE.md maps every key, what it opens, and what must be rotated before this
repo goes anywhere less private.

The admin API key used to be a literal in six catalogue scripts. They now read
SPREE_ADMIN_KEY from the environment and refuse to run without it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-15 22:09:55 +08:00

8.3 KiB
Raw Blame History

🎨 Image Pipeline — Napkin Notes

What the spec wants

Seller uploads phone photo → system auto-standardizes → consistent product catalogue

  • Background removal
  • Colour correction
  • Lighting normalization
  • Resize/crop to exact same dimensions
  • Store processed image against product variant

What we found (FAFO'd 2026-09-13)

  • URL: https://app.photoroom.com/create (signed up as celestjux)
  • Docs: https://docs.photoroom.com/
  • Pricing: Basic $20/mo (BG removal only) · Plus $100/mo (full pipeline) · Enterprise custom
  • Per image: $0.02 (Basic) / $0.10 (Plus)
  • Apparel-specific features:
    • Ghost Mannequin — makes clothes look worn without a real model
    • Virtual Model — AI fashion models wearing the clothes
    • Virtual Try-On — customers see clothes on themselves
    • AI Ironing — smooth wrinkles on clothes
    • AI Relight — fix lighting
    • AI Shadows — realistic shadows
    • Product Staging, AI Backgrounds, AI Beautifier, AI Expand
  • Batch: yes, directory/Google Drive/Sheets
  • Sandbox: 1,000 free watermarked calls + 10 real for testing
  • Verdict: Does EVERYTHING the spec asks in one API call. $100/mo for 1K images. Ghost mannequin + AI ironing = killer for men's apparel.

🥈 Bria AI (good BG removal, lighter features)

  • API key: saved at ~/.bria/credentials
  • Skills installed: /root/.claude/skills/bria-skills/
  • Tested: ✅ BG removal works (RMBG 2.0), 39% transparent pixels on test image
  • Features: BG removal, relighting, upscale, product photography, object editing
  • No ghost mannequin, no AI ironing, no virtual try-on
  • Verdict: Good for BG removal + basic edits. Fallback to Photoroom for apparel-specific stuff.

🥉 rembg (free, self-hosted, offline)

  • Installed: ✅ via pip, u2net model (170MB) cached at /root/.rembg/models/
  • Tested: ✅ works, decent edges, white-on-light handled ok
  • Limit: BG removal ONLY. No colour correction, no lighting, no crop.
  • Speed: ~20s on CPU per image
  • Verdict: Free forever. Good offline fallback. Need OpenCV/Pillow on top for the rest.

Quick-RMBG / Claude Background Removal Plugin

  • Repo: cloned at /mnt/playground/Playground_V2/Claude-Background-Removal-Plugin/
  • What it is: Claude Code skill wrapper around rembg (single/two-pass/batch/Dolphin right-click)
  • pipx install blocked by ctx git shim — not installed yet
  • Verdict: Nice wrapper but not needed if we use Photoroom/Bria for the platform.

Architecture decision

Production:  Photoroom Plus API ($100/mo)
             └─ one call = bg removal + lighting + shadows + crop + resize
             └─ ghost mannequin for clothes-on-invisible-model shots
             └─ AI ironing for wrinkle-free product photos

Fallback:    Bria RMBG 2.0 (free tier / per-image)
             └─ BG removal when Photoroom is down or over quota

Offline:     rembg + Pillow/OpenCV
             └─ dev/testing, batch processing without API costs

✅ BUILT 2026-09-15 evening — the pipe is real, end to end

phone/disk photo -> Image Studio (admin page) -> Photoroom /v2/edit
                 -> unattached ActiveStorage blob -> review before/after
                 -> Apply -> Spree product (existing, or created here)
  • Admin page: /admin/image_studio (sidebar, between Products and Customers). Multi-upload from the device, background + size presets, per-file before/after, then attach to an existing product or create a new listing (name, price, publish toggle). Nothing touches the catalogue until Apply — the result is held as an unattached blob, so Discard is just navigating away.
  • CLI: scraped-catalogue/photoroom_to_spree.py --slug <slug> [--live]
  • Proven live: a 6.52 MB phone PNG → 543 KB at 1000×1000; and two uploads → a new published product visible on the storefront at R259.11.

The architecture question, settled

Abang's call, and he was right: the page is needed. I had argued for a silent auto-on-upload hook (the spec says "seller uploads → system standardises"), but a seller wants to choose the shot and see the result before it ships. Fully automatic means a bad cutout goes live silently and the customer finds it first. Manual control at the front, programmatic pipe behind it.

Photoroom's Shopify Products connector is a red herring for us — it exists for merchants with no backend. We have one, so the plain API is the simpler path.

Money, accurately

Reality
API free tier 10 images/month, not 7 days. 9 left as of 2026-09-15
Sandbox 1000/month, free, every output watermarked
7-day trial That's the web app, a separate meter. Its Batch tool does 250 images at once through the browser and costs no API credits
Plus $100/mo for the full pipeline

🔴 Photoroom adds little to the existing 160 products. They are scraped studio shots already on white — background removal removes a background that is already gone. Across 160 first-images there are only 3 aspect ratios and 4 sizes; framing is the sole real defect, and Pillow squares them for free, offline. Photoroom earns its money on phone photos of new stock, which is exactly what Image Studio is for.

Still open

  • Batch — capped at MAX_PHOTOS = 8, synchronous, ~3-5s per image. More needs a background job
  • Currently on the sandbox key, so everything is watermarked. Flip PHOTOROOM_API_KEY in the backend .env for clean output
  • Pillow square-and-pad backfill for the existing 160 — scrapped 2026-09-15, measured and unnecessary. See below.
  • Never tested on a real phone photo from Lisa — the actual input this exists for. Ghost mannequin and AI ironing have nothing to work with on a scraped studio shot
  • Virtual Try-On — could be a differentiator for the platform
  • Self-hosted option — for v2, when volume justifies building our own pipeline

📐 Framing survey — 2026-09-15, the backfill that wasn't needed

I had the Pillow square-and-pad backfill queued as the next job. Measuring the 160 source images first killed it. The real distribution of first images:

Size Ratio Count
762×1100 0.693 122
800×1200 / 801×1200 0.667 32
1200×1800 0.667 4
1524×2200 0.693 1
2400×2400 1.000 1

The grid renders at aspect-[3/4] (0.75) with object-cover. At 0.693 the crop takes 7.6% of the height; at 0.667 it takes 11%. The entire inconsistency across 159 products is a 3.4% difference in vertical crop — invisible. Padding them to square would also be self-defeating: a square displayed in a 3/4 frame gets re-cropped, so the padding I just added is what gets cut.

🔴 One image was genuinely broken — 129-adidas-lifestyle-barreda-shoes-men-white-jp5928, the only 1:1 in the catalogue. A square in a 3/4 frame loses 12.5% off each side, which cut the heel and the toe off the shoe. Its 22 sibling shoe listings are all 762×1100 with the subject at 90-95% width and 24-33% height, so it was reframed to match: crop to the subject bbox, scale to 93% width, centre on the source's own background (234,238,239). 186KB → 65KB. Applied live via reframe_129.py --apply (upload at position 1, verify by re-read, then delete the original).

The lesson, not the fix: the napkin said "3 aspect ratios and 4 sizes, framing is the sole real defect" and I turned that into a 160-file job. The numbers were right and the conclusion was wrong — nobody had checked how far apart those ratios actually were. Measure the spread, not the count.

Spree API shape — two gotchas that cost a run

  • Flat objects, not JSON:API. /api/v3/admin/products returns fields on the object itself (p["slug"]), not under p["attributes"]["slug"]. The helper in photoroom_to_spree.py only survives because its fallback scan happens to use the flat form.
  • per_page is silently ignored — it pages on limit. Sending per_page=100 gets you 25 rows and no error, so a scan that stops at page 1 "finds" nothing. Meta key is pages, not total_pages.

Keys: /root/.photoroom/credentials (chmod 600) on the house box; PHOTOROOM_API_KEY in the backend .env.