</>CoderDIY
← Back to list
Article

Build a DIY AI Photo Booth with Raspberry Pi

Author: CoderDIY··Đọc bằng tiếng Việt
📸

Renting a photo booth for a party or event usually costs a few hundred dollars a pop, even though under the hood it’s just a webcam, a small touchscreen, and software that counts down and snaps a photo. With a Raspberry Pi, a webcam, and a lightweight face-detection library (no dedicated GPU needed), you can build your own photo booth that “knows” when someone’s standing in frame and auto-counts down to capture — no button required.

How it works

  1. The webcam continuously captures frames, feeding each one through a lightweight face detector (OpenCV’s Haar Cascade or MediaPipe Face Detection) that runs smoothly on a Raspberry Pi without hardware AI acceleration.
  2. Once at least one face is detected consistently across several consecutive frames (to avoid triggering on someone just walking past), the system starts a countdown shown on screen (3, 2, 1…).
  3. When the countdown ends, the system captures a high-resolution photo and automatically overlays an event frame/logo using the Pillow image library.
  4. The finished photo is saved to the memory card and optionally sent to a thermal printer or uploaded to a shared folder (Google Drive, a local network share) so guests can grab it via a QR code.
<rect x="10" y="20" width="140" height="60" rx="10" class="box" />
<text x="80" y="45" text-anchor="middle" class="label">Read frames</text>
<text x="80" y="62" text-anchor="middle" class="label">webcam, continuous</text>

<rect x="170" y="20" width="140" height="60" rx="10" class="box" />
<text x="240" y="45" text-anchor="middle" class="label">Detect faces</text>
<text x="240" y="62" text-anchor="middle" class="label">(OpenCV/MediaPipe)</text>

<rect x="330" y="20" width="140" height="60" rx="10" class="box" />
<text x="400" y="45" text-anchor="middle" class="label">Countdown</text>
<text x="400" y="62" text-anchor="middle" class="label">(3-2-1 on screen)</text>

<rect x="490" y="20" width="140" height="60" rx="10" class="box" />
<text x="560" y="45" text-anchor="middle" class="label">Capture + frame</text>
<text x="560" y="62" text-anchor="middle" class="label">→ save/print/share</text>

<line x1="150" y1="50" x2="170" y2="50" class="arrow" />
<line x1="310" y1="50" x2="330" y2="50" class="arrow" />
<line x1="470" y1="50" x2="490" y2="50" class="arrow" />

<path d="M 560 80 L 560 190 L 80 190 L 80 80" class="arrow" fill="none" />
<text x="320" y="210" text-anchor="middle" class="sublabel">Returns to waiting for the next face after each capture</text>
Photo booth loop: wait for a stable face, count down, capture and process the photo, then wait for the next turn.

Parts list

  • A Raspberry Pi 4 (4GB or more recommended for smooth image processing)
  • A decent USB webcam (720p or higher) or a Raspberry Pi Camera Module
  • A display (HDMI monitor or a small touchscreen) to show the preview and countdown
  • (Optional) A small USB/Bluetooth thermal printer to print photos on the spot
  • A microSD card with enough capacity to store the whole event’s photos
  • An enclosure/frame to protect and decorate it, up to your creativity
<rect x="235" y="90" width="170" height="120" rx="10" class="box" />
<text x="320" y="155" text-anchor="middle" class="label">Raspberry Pi 4</text>

<rect x="20" y="30" width="150" height="70" rx="10" class="box" />
<text x="95" y="70" text-anchor="middle" class="label">USB webcam</text>

<rect x="470" y="30" width="150" height="70" rx="10" class="box" />
<text x="545" y="70" text-anchor="middle" class="label">HDMI display</text>

<rect x="20" y="200" width="150" height="70" rx="10" class="box" />
<text x="95" y="240" text-anchor="middle" class="label">Thermal printer</text>

<line x1="170" y1="65" x2="235" y2="120" class="wire" />
<text x="180" y="95" text-anchor="middle" class="sublabel">USB</text>

<line x1="405" y1="120" x2="470" y2="65" class="wire" />
<text x="455" y="95" text-anchor="middle" class="sublabel">HDMI</text>

<line x1="170" y1="235" x2="235" y2="180" class="wire" />
<text x="180" y="205" text-anchor="middle" class="sublabel">USB/BT</text>
The Raspberry Pi is the processing hub, connected to a webcam for capture, a display for preview, and an optional thermal printer for instant prints.

Sample code

import cv2
import time
from datetime import datetime
from PIL import Image, ImageDraw, ImageFont

FRAME_DIR = "captures"
FACE_STABLE_FRAMES = 15   # consecutive frames a face must be present before counting down
COUNTDOWN_SECONDS = 3

face_cascade = cv2.CascadeClassifier(
    cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
)
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)

stable_count = 0

def add_frame_overlay(photo_path, output_path):
    img = Image.open(photo_path).convert("RGB")
    border = 40
    framed = Image.new("RGB", (img.width + border * 2, img.height + border * 2), "white")
    framed.paste(img, (border, border))
    draw = ImageDraw.Draw(framed)
    draw.text((border, framed.height - border + 5), "CoderDIY Photo Booth", fill="black")
    framed.save(output_path)

def capture_photo():
    ret, frame = cap.read()
    if not ret:
        return
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    raw_path = f"{FRAME_DIR}/raw_{timestamp}.jpg"
    final_path = f"{FRAME_DIR}/photo_{timestamp}.jpg"
    cv2.imwrite(raw_path, frame)
    add_frame_overlay(raw_path, final_path)
    print(f"Saved photo: {final_path}")

while True:
    ret, frame = cap.read()
    if not ret:
        continue

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5)

    if len(faces) > 0:
        stable_count += 1
    else:
        stable_count = 0

    display = frame.copy()
    for (x, y, w, h) in faces:
        cv2.rectangle(display, (x, y), (x + w, y + h), (0, 255, 0), 2)

    if stable_count >= FACE_STABLE_FRAMES:
        for i in range(COUNTDOWN_SECONDS, 0, -1):
            ret, frame = cap.read()
            preview = frame.copy()
            cv2.putText(preview, str(i), (frame.shape[1] // 2 - 30, frame.shape[0] // 2),
                        cv2.FONT_HERSHEY_SIMPLEX, 4, (0, 0, 255), 6)
            cv2.imshow("Photo Booth", preview)
            cv2.waitKey(1000)

        capture_photo()
        stable_count = 0
        time.sleep(2) # pause before the next frame check, avoids back-to-back captures

    cv2.imshow("Photo Booth", display)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

Common pitfalls

  • Capturing too early when someone briefly passes through frame — always require a face to stay present across several consecutive frames (FACE_STABLE_FRAMES) before starting the countdown, to avoid triggering on passersby.
  • Low light makes Haar Cascade detection unreliable — add a ring light or a white backlight near the webcam, especially for indoor events with dim lighting.
  • No delay (time.sleep) after capturing causes the system to fire off several duplicate captures right after finishing one — always pause a few seconds before returning to the waiting state.
  • Running face detection at full resolution slows down the frame rate on a Raspberry Pi — detect faces on a downscaled frame (e.g. 640x480), and only capture the full-resolution original when the shutter actually fires.

Extensions

Add event-themed photo filters (black-and-white, vintage) using OpenCV, generate a QR code right after capture so guests can scan and download the photo to their phone, or pair it with the DIY smart mirror with Raspberry Pi project to turn a mirror into a photo booth and personal assistant in one.

Tags:#raspberry-pi#python#ai

Comments