Blob portrait: gradient descent for a photo
Paints a photo with 2D Gaussian blobs. It starts from 1,500 random blobs, and each step it renders them, measures the error against the photo (L1 + SSIM), and moves every blob a little to reduce it (Adam). New tiny blobs spawn where the error is highest. No image generator is involved: the picture is learned.
Steps
-
Get the photo. Use the attached file. Note its subject position so you can set
--focus(the area that gets the most close-up training, usually the face). -
Install. Python 3.10+.
pip install -r requirements.txt # torch, numpy, pillowIf the PyTorch index is blocked, plain
pip install torchfrom PyPI works. -
Pick a preset from the hardware and time available:
preset steps max blobs 2-core CPU GPU quick300 15,000 ~8 min ~1 min default1,600 45,000 ~35 min ~5 min full6,000 100,000 hours ~20-40 min On a CPU, run
defaultin the background and poll the log; don't block on it. -
Run.
python scripts/blobfit.py photo.jpg --out result/ --preset default \ --aspect 4:5 --focus 0.5,0.4,0.45,0.5--aspectcrops to w:h around the centre (nonekeeps the photo's own shape).--focus cx,cy,w,his in 0-1 of the cropped image. For a centred selfie,0.5,0.4,0.45,0.5covers the face.--steps/--blobsoverride the preset.- Device is picked automatically: CUDA, then Apple MPS, then CPU.
-
Check the result before delivering. Open
compare.jpgandzoom.jpgand look at them.stats.jsonhas the PSNR: around 28-30 dB is a soft likeness (quick), 38 dB+ is hard to tell apart at normal size. If the face is soft, rerun with more steps or move--focusonto the face. -
Deliver. Send
compare.jpg,render.pngandtraining.gif. Say how many blobs and steps, the PSNR, and that it was trained, not generated.
Outputs
| file | what it is |
|---|---|
render.png | the blob painting at full fitted size |
compare.jpg | real photo beside the code version |
zoom.jpg | close-up proof around the eyes |
training.gif | noise to picture, one frame per milestone |
blobs.npz | every blob: position, scale, angle, colour, strength (for re-rendering or animation) |
stats.json | steps, blob count, PSNR, seconds, device |
Honest limits
- It copies one photo. It can't invent a new view or a new face.
- Quality depends on steps: short CPU runs come out soft; fine skin grain needs the
fullpreset. - The backdrop colour is taken from the photo's edges, so blobs only need to paint what differs from it.