</>CoderDIY
← Quay lại danh sách
Bài viết

Photo booth AI tự chế với Raspberry Pi

Tác giả: CoderDIY··Đọc bằng tiếng Anh
📸

Thuê photo booth cho tiệc hoặc sự kiện thường tốn vài triệu đồng mỗi lần, trong khi bản chất chỉ là một webcam, một màn hình cảm ứng nhỏ và phần mềm đếm ngược rồi chụp. Với Raspberry Pi, webcam và thư viện phát hiện khuôn mặt nhẹ (không cần GPU rời), bạn hoàn toàn có thể tự dựng một photo booth “biết” khi nào có người đứng vào khung hình để tự động đếm ngược và chụp — không cần bấm nút.

Nguyên lý hoạt động

  1. Webcam liên tục chụp khung hình (frame) và đưa qua bộ phát hiện khuôn mặt nhẹ (Haar Cascade của OpenCV hoặc MediaPipe Face Detection) chạy được mượt trên Raspberry Pi mà không cần AI tăng tốc phần cứng.
  2. Khi phát hiện có ít nhất một khuôn mặt ổn định trong khung hình qua vài frame liên tiếp (tránh chụp nhầm khi người đi ngang qua), hệ thống bắt đầu đếm ngược hiển thị trên màn hình (3, 2, 1…).
  3. Hết đếm ngược, hệ thống chụp ảnh độ phân giải cao, tự động chèn khung viền/logo sự kiện lên ảnh bằng thư viện xử lý ảnh Pillow.
  4. Ảnh thành phẩm được lưu vào thẻ nhớ và tùy chọn gửi tới máy in nhiệt (thermal printer) hoặc tải lên thư mục chia sẻ (Google Drive, thư mục mạng nội bộ) để khách lấy ảnh qua 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">Đọc khung hình</text>
<text x="80" y="62" text-anchor="middle" class="label">webcam liên tục</text>

<rect x="170" y="20" width="140" height="60" rx="10" class="box" />
<text x="240" y="45" text-anchor="middle" class="label">Phát hiện khuôn mặt</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">Đếm ngược</text>
<text x="400" y="62" text-anchor="middle" class="label">(3-2-1 trên màn hình)</text>

<rect x="490" y="20" width="140" height="60" rx="10" class="box" />
<text x="560" y="45" text-anchor="middle" class="label">Chụp + ghép khung</text>
<text x="560" y="62" text-anchor="middle" class="label">→ lưu/in/chia sẻ</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">Quay lại chờ khuôn mặt tiếp theo sau khi chụp xong</text>
Vòng lặp photo booth: chờ khuôn mặt ổn định, đếm ngược, chụp và xử lý ảnh, rồi quay lại chờ lượt tiếp theo.

Linh kiện

  • Raspberry Pi 4 (khuyến nghị bản 4GB trở lên để xử lý ảnh mượt)
  • Webcam USB độ phân giải tốt (720p trở lên) hoặc Raspberry Pi Camera Module
  • Màn hình (HDMI hoặc màn cảm ứng nhỏ) để hiển thị preview và bộ đếm ngược
  • (Tùy chọn) Máy in nhiệt USB/Bluetooth nhỏ để in ảnh ngay tại chỗ
  • Thẻ nhớ microSD dung lượng đủ lớn để lưu toàn bộ ảnh sự kiện
  • Khung/vỏ ngoài để bảo vệ và trang trí, tùy sáng tạo của bạn
<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">Webcam USB</text>

<rect x="470" y="30" width="150" height="70" rx="10" class="box" />
<text x="545" y="70" text-anchor="middle" class="label">Màn hình HDMI</text>

<rect x="20" y="200" width="150" height="70" rx="10" class="box" />
<text x="95" y="240" text-anchor="middle" class="label">Máy in nhiệt</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>
Raspberry Pi là trung tâm xử lý, kết nối webcam để chụp, màn hình để hiển thị, và máy in nhiệt tùy chọn để in ngay.

Code mẫu

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

FRAME_DIR = "captures"
FACE_STABLE_FRAMES = 15   # so frame lien tiep phai co mat truoc khi dem nguoc
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")
    draw = ImageDraw.Draw(img)
    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"Da luu anh: {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) # nghi trước khi don frame tiep theo, tranh chup lien tuc

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

cap.release()
cv2.destroyAllWindows()

Những lỗi thường gặp

  • Chụp quá sớm khi người mới thoáng qua khung hình — luôn yêu cầu khuôn mặt xuất hiện ổn định qua nhiều frame liên tiếp (FACE_STABLE_FRAMES) trước khi bắt đầu đếm ngược, tránh chụp nhầm người đi ngang.
  • Ánh sáng yếu làm Haar Cascade phát hiện khuôn mặt kém chính xác — bổ sung đèn LED vòng hoặc đèn nền trắng gần webcam, đặc biệt cho các sự kiện trong nhà thiếu sáng.
  • Không có độ trễ (time.sleep) sau khi chụp khiến hệ thống chụp liên tục nhiều ảnh trùng nhau ngay khi vừa xong một lượt — luôn nghỉ vài giây trước khi quay lại trạng thái chờ.
  • Chạy phát hiện khuôn mặt ở độ phân giải quá cao làm giảm tốc độ khung hình trên Raspberry Pi — nên xử lý phát hiện khuôn mặt trên ảnh đã thu nhỏ (ví dụ 640x480) rồi mới chụp ảnh gốc độ phân giải cao khi thực sự bấm chụp.

Mở rộng

Thêm bộ lọc/hiệu ứng ảnh theo phong cách sự kiện (đen trắng, cổ điển) bằng OpenCV, tạo mã QR ngay sau khi chụp để khách quét tải ảnh về điện thoại, hoặc ghép chung với dự án gương thông minh Raspberry Pi để biến gương thành photo booth kiêm trợ lý cá nhân.

Thẻ:#raspberry-pi#python#ai

Bình luận