Verify that AI-generated product imagery actually preserved the real packaging — then repair it when it didn't.
Image models get pose, lighting and shadow convincingly right, then quietly reinvent the small print: a weight badge, a certification roundel, the fine type along a seam. The frame still looks beautiful. It is also unshippable, and no amount of looking at it will reliably tell you which.
packfix replaces "does it look fine?" with a number.
1. Measure — fidelity.py
SIFT features on the real pack plate, matched against the generated frame, homography solved with RANSAC, and the inlier count taken as the score. Above the gate, the printed artwork genuinely corresponds to the real pack. Below it, the model invented print, and the frame goes back for regeneration.
It does not modify a pixel. Measuring and fixing are deliberately separate.
from fidelity import score, passed
r = score("30_Gen/hero_1200x1200.png", {"salted_egg": "plates/salted_egg.jpg"})
# {"salted_egg": ("PASS", 313)}
passed(r) # True2. Repair — packfix.py
When the scene is right but the print drifted, warp the real pack into the exact quad the homography found, then carry the generated pack's own low-frequency shading onto the real artwork — a clamped ratio of heavily blurred luminances — so the true print sits in the scene's light instead of looking pasted on.
The alpha is eroded slightly and feathered, so the generated pouch's own edge and contact shadow survive.
python3 packfix.py plates/salted_egg.jpg 30_Gen/hero.png out/hero_fixed.png
# inliers: 313A perceptual or embedding distance answers "do these look similar", which is the question that already failed — the whole problem is that a wrong pack looks similar. SIFT keypoints matched under a RANSAC homography answer a stricter question: is this the same printed surface, seen under a plane projection? Reinvented print produces geometrically inconsistent matches and the inlier count collapses.
From a real 15-frame run, four packs per frame, gate at 150:
| pack | inliers | verdict |
|---|---|---|
| salmon | 214 | PASS |
| shrimp | 349 | PASS |
| mushroom | 230 | PASS |
| rice | 382 | PASS |
Frames scoring below the gate were regenerated, not shipped.
| File | What it does |
|---|---|
fidelity.py |
The gate. PASS/FAIL per plate per frame, with the inlier count. |
packfix.py |
Homography repair — real artwork warped in, scene lighting preserved. |
export.py |
Cover-fit to an exact pixel size under a byte ceiling. Asserts the result rather than trusting the resize, because ad platforms reject on exact dimensions. |
qa.py |
The deliverable checklist run as code — dimensions, byte ceiling, colour mode, duplicate hashes, excluded SKUs, and whether a logo asset was actually used. |
cost.py |
Prices a run from the token counts the API actually returned, not an assumed per-image rate. |
The logo check strips comments before it looks. qa.py parses each build source and removes comment lines before searching for logo usage — otherwise a file that says # no logos in this build passes a check it should fail. A checklist you tick by hand is a checklist that lies.
SIFT runs on a bounded copy. Frames are downscaled to 1600px on the long edge before feature detection. Full-size float arrays at 4K are what exhausted memory on an 8 GB machine; the gate is scale-invariant anyway, so nothing is lost.
pip install -r requirements.txtNeeds opencv-python (SIFT is in the main package as of 4.4), numpy, Pillow.
The plates are yours to supply — a flat, straight-on photograph or render of each pack front, ideally with an alpha channel. No packaging artwork is included in this repository.
MIT licensed.