Contador de personas privado para la puerta de una tienda
Cuenta visitantes que entran y salen con una cámara cenital que guarda solo números: nunca se guarda ninguna imagen.
Las tiendas pequeñas quieren saber sus horas pico. Una cámara gran angular montada sobre la puerta mira hacia abajo, OpenCV encuentra figuras en movimiento y el Pi cuenta cuando cruzan una línea virtual. Los cuadros se procesan en memoria y se descartan, así que solo se guardan totales por hora.
Cuenta personas solo en un local que sea tuyo o que administres, nunca grabes ni transmitas el video y sigue las reglas locales de avisos y privacidad para contar clientes.
Pasos de la misión
Móntala arriba
Pon la cámara en el techo sobre la puerta mirando derecho hacia abajo; la vista desde arriba muestra formas, no caras.
Instala los paquetes
Corre sudo apt install python3-picamera2 python3-opencv.
Ajusta la línea
Cambia LINE_Y para que la línea virtual cruce la puerta y MIN_AREA para que una persona forme una sola mancha.
Cuenta y registra
El script empareja cada mancha con la más cercana del cuadro anterior y cuenta los cruces de la línea en cada dirección.
Sé transparente
Pon un letrero que diga que los visitantes se cuentan de forma anónima y que no se graba video.
Código
import cv2, time, csv, datetime as dt
from picamera2 import Picamera2
cam = Picamera2()
cam.configure(cam.create_video_configuration(main={'size': (640, 480), 'format': 'RGB888'}))
cam.start()
bg = cv2.createBackgroundSubtractorMOG2(history=500, detectShadows=False)
LINE_Y, MIN_AREA, MAX_JUMP = 240, 5000, 120
prev, ins, outs, hour = [], 0, 0, dt.datetime.now().hour
while True:
gray = cv2.GaussianBlur(cv2.cvtColor(cam.capture_array(), cv2.COLOR_BGR2GRAY), (21, 21), 0)
mask = cv2.threshold(bg.apply(gray), 200, 255, cv2.THRESH_BINARY)[1]
contours, _ = cv2.findContours(cv2.dilate(mask, None, iterations=2), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
now = [(x + w // 2, y + h // 2) for x, y, w, h in
(cv2.boundingRect(c) for c in contours if cv2.contourArea(c) > MIN_AREA)]
for cx, cy in now:
if not prev: break
px, py = min(prev, key=lambda p: (p[0] - cx) ** 2 + (p[1] - cy) ** 2)
if abs(py - cy) > MAX_JUMP: continue
if py < LINE_Y <= cy: ins += 1
elif py >= LINE_Y > cy: outs += 1
prev = now # frame is discarded here
if dt.datetime.now().hour != hour:
with open('visits.csv', 'a', newline='') as f:
csv.writer(f).writerow([dt.datetime.now().strftime('%Y-%m-%d %H:00'), ins, outs])
ins = outs = 0; hour = dt.datetime.now().hourCode comments are in English, like most libraries and docs — a good habit for makers.
Preguntas
Dos personas caminando juntas cuentan como una.
El conteo simple de manchas une figuras cercanas. Monta la cámara más alto, baja MIN_AREA con cuidado o pasa a un modelo de detección de personas en el AI HAT+.