{"slug": "dia-de-los-muertos-photo-booth", "title": "Día de los Muertos Photo Booth", "summary": "A developer built Día de los Muertos Photo Booth, a small self-hosted Flask web app that detects faces locally with MediaPipe Face Mesh and alpha-composites themed Día de los Muertos adornments — sugar skulls, sombreros, marigold crowns, La Catrina veils and monarch butterflies — onto each face using facial landmarks. Themes are data-driven, defined by folders of transparent PNGs plus a theme.json describing anchoring, scaling, offset and rotation, so new themes can be added without code changes. The app processes uploads entirely in memory with no external API and nothing written to disk, and ships as a single-service Docker stack.", "body_md": "*This is a submission for the [Hacktoberfest Open-Source AI Challenge Week 3: Ghost in the Machine](https://dev.to/challenges/hacktoberfest-week3-2026-10-19)*\n\n**Día de los Muertos Photo Booth** is a tiny Flask web app that decorates every face in a photo with festive Día de los Muertos adornments — all on your own machine, with no cloud, no account, and no photo ever leaving your computer.\n\nYou upload one JPEG or PNG. The app detects the faces in the picture locally, randomly assigns each face one of eight Día de los Muertos themes — **calavera** (sugar-skull decoration + forehead marigold), **sombrero** (hat + mustache), **flower_crown** (cempasúchil marigold crown), **catrina** (La Catrina veil + stitched lips), **papel_picado** (decorative banner), **marigold_path** (petal overlay + crown), **mariachi** (mustache + bow tie + sombrero), and **monarch** (monarch butterfly) — and alpha-composites transparent PNG adornments onto each face using its facial landmarks. Each adornment is anchored to the face: hats, crowns, veils, the monarch butterfly, and the forehead marigold sit above the head; the calavera decoration frames the eyes; mustaches and stitched lips land on the mouth; the bow tie sits at the chin; and the papel picado and marigold-petal overlays cover the whole frame. Several themes layer multiple pieces (for example, mariachi combines a mustache, a bow tie, and a sombrero). Every adornment scales with each face and rotates to follow a tilted head, so the result tracks the real geometry of the photo. You then see the composed image in the browser and can download it in the same format you uploaded.\n\n**Who it's for:** anyone who wants a fun, instant, private way to celebrate Día de los Muertos with a photo effect — families, classrooms, cultural events, or a self-hosted photo-booth kiosk. Because everything runs locally and in memory, it's a good fit for anyone who cares that their faces and family photos are never uploaded to a third-party service.\n\nThe privacy guarantee is structural, not a promise: there is no external API, nothing is written to disk, and every buffer is released in a `try/finally` block on every request path. Processing is fully in-memory and ephemeral.\n\nThe adornment catalog is fully data-driven: each theme is a folder of transparent PNGs plus a `theme.json` describing how each piece is anchored, scaled, offset, and rotated, so themes can be added, enriched, or swapped without touching a line of code.\n\nRun it locally with Docker (see the run steps below) and open **[http://localhost:5000](http://localhost:5000)**.\n\nPick a JPEG or PNG with one or more faces, click **Create portrait**, and the composed, adorned image appears in the result panel ready to download. No setup, seeding, or model download is required on first start — the app is ready the moment the container is up.\n\nAll code lives in `desafio5/` and is deliberately small: a thin Flask layer over pure, independently testable domain modules, a vanilla-JS frontend, and a single-service Docker stack.\n\n`app.py` — Flask app factory and routes. `GET /` renders the single-page upload UI; `POST /process` runs the full in-memory pipeline (validate → detect → assign themes → compose → encode) and streams the result back. Loads the adornment set once at startup, warms up the detection model, and enforces a 30 s processing budget.`validation.py` — decodes and validates the upload entirely in memory (10 MB limit, JPEG/PNG only, decodability, non-zero dimensions). Never touches disk.`detection.py` — the Detection_Engine wrapper around MediaPipe Face Mesh; locates up to 20 faces and derives the eye/mouth/head/chin landmarks used to anchor adornments.`composition.py` — the Composition_Engine: assigns a random theme per face and scales, rotates, and alpha-composites each PNG onto the photo while preserving the input dimensions.`adornments.py` — loads the Adornment_Set (PNG assets + per-theme `theme.json`) once at startup and encodes the result image back to bytes.`templates/index.html` — single-page upload interface (all labels in English).`static/app.js` + `static/style.css` — the fetch-based uploader and the responsive stylesheet.`static/adornments/<theme>/` — the transparent PNG assets and placement metadata per theme.`Dockerfile` + `docker-compose.yml` — a single `web` service served by gunicorn.`README.md` — project documentation (features, themes, architecture, run and usage instructions).\nThe whole image-processing pipeline runs on an **open-source, fully local AI/vision stack**. No request ever leaves the machine, and no model weights are downloaded at runtime — they are bundled into the Docker image at build time. Here is each open-source tool and the core image-processing job it does:\n\n`requirements.txt`, and the app runs one throwaway inference at startup to warm the model graph so the first real upload isn't penalized by lazy initialization.`opencv-python-headless`, the server-friendly build with no GUI dependencies) provides the image-array handling and color/format conventions that MediaPipe builds on, so the decoded photo is presented to the detector in the array layout its inference graph expects.\nOn top of that vision stack, **Flask** serves the single page and the processing endpoint, run under **gunicorn** inside the container, and the frontend is plain vanilla JavaScript that posts the file with `fetch` and renders the returned image blob.\n\nThe design decision I care most about is **local-and-ephemeral by construction**. Validation decodes the bytes in a memory buffer and never writes them; detection and composition operate on in-memory images; and the `/process` route releases every upload buffer and decoded image in a `finally` block on success, failure, and timeout alike. A face whose landmarks can't be computed is simply skipped while the other faces are still adorned, so a hard photo degrades gracefully instead of erroring out.\n\nBuilding this on **open-source local models** instead of a hosted vision API changed what the project could be:\n\nOpen weights and on-device inference are what let a genuinely useful, face-aware AI effect live on cheap, private, offline hardware that anyone can own.\n\nBefore running the Docker commands below, make sure you have:\n\n`docker compose` v2\nNo other local setup is needed. In particular, **there is no model-pull step**: unlike an Ollama-based app, the MediaPipe models are bundled into the image at build time, so nothing is downloaded at runtime.\n\nFrom the `desafio5/` directory, run these commands in order:\n\n```\n# 1. Build the image and start the app (single web service).\n#    The MediaPipe models are baked into the image during the build,\n#    so there is NO separate model-pull step and no network at request time.\ndocker compose up --build\n\n# 2. Open the app in your browser.\n#    http://localhost:5000\n```\n\nThat's the complete sequence: `docker compose up --build` builds the image and starts the `web` service, which gunicorn serves on `0.0.0.0:5000` inside the container, published to the host as `5000:5000`. The app is then reachable at **[http://localhost:5000](http://localhost:5000)**.\n\nTo stop the app, press `Ctrl+C` in that terminal (or run `docker compose down` from the `desafio5/` directory).\n\nIf no face is detected, or the file is not a valid JPEG/PNG within the size limit, the app shows a clear message and stays ready for another upload without reloading the page.", "url": "https://wpnews.pro/news/dia-de-los-muertos-photo-booth", "canonical_source": "https://dev.to/engcompaulo/dia-de-los-muertos-photo-booth-19pf", "published_at": "2026-10-29 01:04:54+00:00", "updated_at": "2026-10-29 01:11:26.779595+00:00", "lang": "en", "topics": ["computer-vision", "ai-tools", "developer-tools", "artificial-intelligence"], "entities": ["MediaPipe", "Flask", "Docker", "Hacktoberfest", "Día de los Muertos Photo Booth"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/dia-de-los-muertos-photo-booth", "markdown": "https://wpnews.pro/news/dia-de-los-muertos-photo-booth.md", "text": "https://wpnews.pro/news/dia-de-los-muertos-photo-booth.txt", "jsonld": "https://wpnews.pro/news/dia-de-los-muertos-photo-booth.jsonld"}}