Files
pak-jabit/docs/napkin-image-pipeline.md
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

170 lines
8.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🎨 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)
### 🥇 Photoroom (recommended for production)
- **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
- [x] ~~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`.