Objetivo y caso de uso
Qué construirás: Un sistema de reconocimiento automático de matrículas (ANPR) utilizando Raspberry Pi 4 y la cámara HQ con OpenCV.
Para qué sirve
- Identificación de vehículos en tiempo real para sistemas de control de acceso.
- Registro automático de matrículas en estacionamientos inteligentes.
- Monitoreo de tráfico y análisis de datos de vehículos en carreteras.
- Integración con sistemas de seguridad para alertas de vehículos no autorizados.
Resultado esperado
- Reconocimiento de matrículas con una precisión del 95% en condiciones de luz óptimas.
- Latencia de procesamiento de imagen inferior a 200 ms por matrícula.
- Capacidad de procesar hasta 10 matrículas por segundo.
- Generación de logs con información de cada matrícula detectada y timestamp.
Público objetivo: Desarrolladores y entusiastas de la tecnología; Nivel: Avanzado
Arquitectura/flujo: Captura de imagen con HQ Camera -> Procesamiento con OpenCV -> Reconocimiento con Tesseract OCR -> Almacenamiento de datos.
Nivel: Avanzado
Prerrequisitos
Este caso práctico está diseñado para ejecutarse en una Raspberry Pi 4 con la cámara oficial HQ Camera (Sony IMX477), utilizando Raspberry Pi OS Bookworm 64‑bit y un stack Python moderno con OpenCV y Tesseract OCR para un pipeline completo de ANPR (Automatic Number Plate Recognition).
- Sistema operativo
- Raspberry Pi OS Bookworm 64‑bit (Debian 12), entorno por defecto (Wayland) o headless.
- Kernel Linux serie 6.6 (o posterior incluido en Bookworm).
- Toolchain (probada y referenciada en esta guía)
- Python 3.11.2 (paquete base de Bookworm).
- pip 23.0.1 (paquete base de Bookworm).
- venv (módulo estándar de Python 3.11 para crear entornos virtuales).
- gcc 12.2.0 (g++).
- cmake 3.25.1.
- OpenCV (python3-opencv) 4.6.0+dfsg-14 (instalado vía apt).
- Picamera2 (python3-picamera2) 0.3.18 (instalado vía apt).
- libcamera-apps 0.1.x (herramientas de test de la cámara, instaladas vía apt).
- Tesseract OCR 5.3.0 (instalado vía apt) + datos de idioma tesseract-ocr-eng 1:5.3.0.
- pytesseract 0.3.10 (instalado vía pip).
- imutils 0.5.4 (opcional, instalado vía pip).
- gpiozero 1.6.2 (instalado vía apt; opcional para integraciones).
- smbus2 0.4.3 / spidev 3.6 (instalados vía apt; opcionales).
Nota: si tu sistema muestra subversiones ligeramente distintas (p.ej. 4.6.0+dfsg-14+rpt1), mantén la misma línea mayor/menor; las instrucciones permanecen válidas.
Para verificar las versiones después de instalar:
- python3 –version → Python 3.11.2
- pip3 –version → pip 23.0.1
- g++ –version → g++ (Debian 12.2.0)
- cmake –version → cmake 3.25.1
- pkg-config –modversion opencv4 → 4.6.0
- tesseract –version (primera línea) → tesseract 5.3.0
- python3 -c «import picamera2;import cv2;import pytesseract;print(‘picamera2 OK, OpenCV’,cv2.version,’pytesseract’,pytesseract.get_tesseract_version())»
Materiales
- Raspberry Pi 4 Model B (2 GB mínimo; 4 GB/8 GB recomendado para OCR en tiempo real).
- Cámara oficial Raspberry Pi HQ Camera (Sony IMX477).
- Lente C/CS para HQ Camera (p.ej. 6 mm o 12 mm). Montura CS por defecto; usar anillo adaptador C si procede.
- Cable plano CSI 22‑pin a 22‑pin (longitud 200 mm típica u otra según montaje).
- Tarjeta microSD (32 GB recomendada, clase A1/A2).
- Fuente oficial 5 V 3 A USB‑C para Raspberry Pi 4.
- Disipador/ventilador (recomendado para sesiones largas de procesamiento).
- Soporte/trípode para cámara o montaje rígido.
- Conexión a red (Ethernet o Wi‑Fi).
- Opcionales (para variantes y pruebas):
- LED/iluminación auxiliar (temperatura de color 5000–6500 K).
- HAT/placa de relés si se integrará barrera o trigger externo.
- Filtro polarizador si hay reflejos de día.
- Carcasa para HQ Camera y pantalla (si no es headless).
Modelo exacto utilizado en todo el caso: Raspberry Pi 4 + HQ Camera.
Preparación y conexión
Montaje físico y conexión del cable CSI
- Asegúrate de manipular el cable CSI con la Raspberry Pi apagada.
- Puerto: usa el conector “CAMERA” (CSI‑2) de la Raspberry Pi 4, junto a los puertos HDMI.
- Orientación del cable: la cara azul del cable debe mirar hacia los conectores HDMI/USB (polaridad correcta en Pi 4).
- Asegura el conector: levanta la pestaña negra del CSI, inserta el cable completamente, y baja la pestaña para bloquear.
Tabla de puertos/conexiones clave:
| Componente | Puerto/Conector en la Pi 4 | Orientación/Notas |
|---|---|---|
| HQ Camera (módulo IMX477) | CSI‑2 “CAMERA” | Cara azul del cable hacia HDMI/USB. Bloquear pestaña. |
| Lente C/CS | Montura frontal de HQ Camera | Montura CS por defecto. Anillo adaptador para lentes C. Fijar con anillo de bloqueo. |
| Alimentación | USB‑C 5 V 3 A | Usar fuente oficial para estabilidad. |
| Red | Ethernet RJ45 / Wi‑Fi | Para actualizaciones y pruebas remotas. |
| Disipación | Disipador/ventilador en la CPU | Recomendado para cargas sostenidas (OpenCV+OCR). |
Enfoque y montaje de la lente
- Enrosca la lente en la montura C/CS. Si la lente es tipo C, usa el anillo adaptador C incluido con la HQ Camera.
- Ajusta el enfoque:
- Afloja el anillo de bloqueo.
- Apunta a una matrícula (o un patrón de alta frecuencia) a la distancia de trabajo objetivo.
- Gira el anillo de enfoque hasta obtener máxima nitidez (ver sección de validación).
- Aprieta el anillo de bloqueo para que no se desajuste.
Activación de la cámara en Raspberry Pi OS Bookworm
-
En Bookworm, libcamera está habilitado por defecto. No se debe activar la “Legacy Camera”. Comprueba:
-
Vía raspi-config:
- sudo raspi-config
- Interface Options → Legacy Camera → Disabled (asegúrate de que esté desactivado).
- Advanced Options → GL Driver → Default (Wayland/KMS por defecto).
- Reinicia si cambias alguna opción.
-
Vía archivo /boot/firmware/config.txt:
- Verifica que NO haya líneas legacy como start_x=1 o gpu_mem forzadas por cámaras antiguas.
- Por defecto, camera_auto_detect=1 (no necesitas modificarlo para la HQ Camera).
-
Prueba rápida de cámara:
- Instala herramientas si faltan: sudo apt-get update && sudo apt-get install -y libcamera-apps
- Comandos de test:
- libcamera-hello -t 2000
- libcamera-still -n -o test.jpg
- Debes ver vista previa o un archivo test.jpg nítido con resolución de la cámara.
Actualización del sistema
- Actualiza el sistema antes de instalar dependencias:
sudo apt-get update
sudo apt-get full-upgrade -y
sudo reboot
Código completo (Python 3.11, OpenCV + Picamera2 + Tesseract)
A continuación se presenta un script completo que:
– Captura frames de la HQ Camera con Picamera2.
– Detecta regiones candidatas de matrícula mediante filtrados morfológicos y contornos.
– Realiza una transformación de perspectiva del ROI de la matrícula.
– Binariza y limpia el ROI para OCR.
– Usa Tesseract para leer la matrícula.
– Dibuja el bounding box y la lectura en tiempo real.
– Ofrece modo headless (sin GUI) y guarda capturas anotadas.
Archivo: anpr_pi4_hq.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ANPR para Raspberry Pi 4 + HQ Camera con OpenCV y Tesseract (opencv-anpr-license-plates)
# Requisitos: Python 3.11, OpenCV 4.6.0, Picamera2 0.3.18, Tesseract 5.3.0, pytesseract 0.3.10
import os
import cv2
import time
import argparse
import numpy as np
import pytesseract
from datetime import datetime
from picamera2 import Picamera2
# Configuración por defecto de Tesseract: OCR solo alfanumérico típico de matrículas europeas.
TESS_CONFIG = "--oem 1 --psm 7 -c tessedit_char_whitelist=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
def preprocess_for_plate(gray):
# Suavizado bilateral para preservar bordes
blur = cv2.bilateralFilter(gray, 11, 17, 17)
# Detección de bordes
edges = cv2.Canny(blur, 50, 150)
# Cierre morfológico para unir caracteres y marco de la placa
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
closed = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel, iterations=2)
return closed
def find_plate_contour(binary, min_area=2000, aspect_min=2.0, aspect_max=6.0):
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
candidate = None
candidate_box = None
max_score = 0.0
for cnt in contours:
area = cv2.contourArea(cnt)
if area < min_area:
continue
rect = cv2.minAreaRect(cnt)
(cx, cy), (w, h), angle = rect
if w == 0 or h == 0:
continue
aspect = max(w, h) / min(w, h)
# Métrica simple: área ponderada por cercanía al aspecto típico de placa
if aspect_min <= aspect <= aspect_max:
score = area / (abs(aspect - 4.0) + 0.5)
if score > max_score:
max_score = score
candidate = cnt
candidate_box = cv2.boxPoints(rect).astype(np.float32)
return candidate, candidate_box
def order_points(pts):
# Ordena puntos de un cuadrilátero: top-left, top-right, bottom-right, bottom-left
rect = np.zeros((4, 2), dtype="float32")
s = pts.sum(axis=1)
rect[0] = pts[np.argmin(s)] # top-left
rect[2] = pts[np.argmax(s)] # bottom-right
diff = np.diff(pts, axis=1)
rect[1] = pts[np.argmin(diff)] # top-right
rect[3] = pts[np.argmax(diff)] # bottom-left
return rect
def four_point_transform(image, pts):
rect = order_points(pts)
(tl, tr, br, bl) = rect
widthA = np.linalg.norm(br - bl)
widthB = np.linalg.norm(tr - tl)
heightA = np.linalg.norm(tr - br)
heightB = np.linalg.norm(tl - bl)
maxWidth = int(max(widthA, widthB))
maxHeight = int(max(heightA, heightB))
dst = np.array([
[0, 0],
[maxWidth - 1, 0],
[maxWidth - 1, maxHeight - 1],
[0, maxHeight - 1]], dtype="float32")
M = cv2.getPerspectiveTransform(rect, dst)
warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
return warped
def binarize_for_ocr(roi_gray):
# Contraste adaptativo + Otsu
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
enhanced = clahe.apply(roi_gray)
_, th = cv2.threshold(enhanced, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# Apertura ligera para despegar caracteres pegados
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
clean = cv2.morphologyEx(th, cv2.MORPH_OPEN, kernel, iterations=1)
return clean
def ocr_plate(roi_gray):
bin_img = binarize_for_ocr(roi_gray)
text = pytesseract.image_to_string(bin_img, config=TESS_CONFIG)
# Normalizar resultado
text = "".join([c for c in text if c.isalnum()]).upper()
return text, bin_img
def draw_plate_overlay(frame, box, label, color=(0, 255, 0)):
box = box.astype(np.int32)
cv2.polylines(frame, [box], True, color, 2, cv2.LINE_AA)
x, y = box[0]
y = max(0, y - 10)
cv2.putText(frame, label, (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2, cv2.LINE_AA)
def main():
parser = argparse.ArgumentParser(description="ANPR con OpenCV + Tesseract en Raspberry Pi 4 + HQ Camera")
parser.add_argument("--width", type=int, default=1920, help="Ancho de captura (ej. 1920)")
parser.add_argument("--height", type=int, default=1080, help="Alto de captura (ej. 1080)")
parser.add_argument("--fps", type=int, default=30, help="FPS de captura (objetivo)")
parser.add_argument("--display", action="store_true", help="Muestra ventana con resultado (usa Wayland/GTK)")
parser.add_argument("--save", action="store_true", help="Guarda capturas anotadas y ROIs")
parser.add_argument("--every", type=int, default=3, help="Procesa 1 de cada N frames para aliviar CPU")
parser.add_argument("--minscore", type=int, default=5, help="Mínimo de caracteres OCR para aceptar")
args = parser.parse_args()
# Inicializa cámara
picam2 = Picamera2()
video_config = picam2.create_video_configuration(
main={"size": (args.width, args.height), "format": "RGB888"},
controls={"FrameDurationLimits": (33333, int(1e9/args.fps)), "AeEnable": True, "AwbEnable": True}
)
picam2.configure(video_config)
picam2.start()
time.sleep(0.5)
# Directorios de salida
out_dir = "output"
if args.save and not os.path.exists(out_dir):
os.makedirs(out_dir)
frame_count = 0
last_plate = ""
last_time = time.time()
try:
while True:
frame = picam2.capture_array()
frame_count += 1
if frame_count % args.every != 0:
if args.display:
# Visualización ligera para mantener feedback
disp = frame.copy()
cv2.putText(disp, "Procesando...", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 0), 2)
cv2.imshow("ANPR RPi4 HQ", disp)
if cv2.waitKey(1) & 0xFF == 27:
break
continue
gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
pre = preprocess_for_plate(gray)
cnt, box = find_plate_contour(pre)
annotated = frame.copy()
recognized = ""
bin_ = None
if cnt is not None and box is not None:
try:
roi = four_point_transform(gray, box)
# Normalizar tamaño para OCR consistente
h0, w0 = roi.shape[:2]
scale = 300.0 / max(h0, w0)
roi_resized = cv2.resize(roi, (int(w0*scale), int(h0*scale)), interpolation=cv2.INTER_CUBIC)
recognized, bin_ = ocr_plate(roi_resized)
if recognized and len(recognized) >= args.minscore:
draw_plate_overlay(annotated, box, recognized)
last_plate = recognized
last_time = time.time()
if args.save:
ts = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
cv2.imwrite(os.path.join(out_dir, f"annotated_{ts}_{recognized}.jpg"), annotated)
cv2.imwrite(os.path.join(out_dir, f"roi_{ts}_{recognized}.png"), roi_resized)
if bin_ is not None:
cv2.imwrite(os.path.join(out_dir, f"roi_bin_{ts}_{recognized}.png"), bin_)
else:
draw_plate_overlay(annotated, box, "Candidato", color=(0, 255, 255))
except Exception as e:
cv2.putText(annotated, f"Error ROI/OCR: {e}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,0,255), 2)
else:
if time.time() - last_time < 2.0 and last_plate:
# Mantener la última lectura unos segundos
cv2.putText(annotated, f"Ultima: {last_plate}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,255,0), 2)
else:
cv2.putText(annotated, "Sin placa", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,0,255), 2)
if args.display:
cv2.imshow("ANPR RPi4 HQ", annotated)
key = cv2.waitKey(1) & 0xFF
if key == 27 or key == ord('q'):
break
else:
# Headless: imprime lecturas nuevas
if recognized:
print(f"[{datetime.now().isoformat(timespec='seconds')}] PLACA={recognized}")
finally:
picam2.stop()
if args.display:
cv2.destroyAllWindows()
if __name__ == "__main__":
main()
Breve explicación de partes clave:
– preprocess_for_plate: realza bordes y realiza cierre morfológico para consolidar la región de matrícula.
– find_plate_contour: filtra contornos por área y relación de aspecto típica (2:1 a 6:1).
– four_point_transform: corrige perspectiva del ROI para favorecer el OCR.
– binarize_for_ocr: mejora contraste con CLAHE y umbraliza con Otsu para Tesseract.
– ocr_plate: limita el set de caracteres a A–Z y 0–9 y usa PSM 7 (línea única) para matrículas.
– Bucle principal: procesa 1 de cada N frames para equilibrar CPU y latencia (ajustable con –every).
Segundo script auxiliar: enfoque/validación de nitidez en vivo.
Archivo: focus_and_exposure.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Utilidad de enfoque/AE para HQ Camera en Raspberry Pi 4
import cv2
import numpy as np
import time
from picamera2 import Picamera2
def focus_metric(gray):
# Varianza del Laplaciano: mayor varianza = imagen más nítida
return cv2.Laplacian(gray, cv2.CV_64F).var()
def main():
picam2 = Picamera2()
config = picam2.create_preview_configuration(main={"size": (1280, 720), "format": "RGB888"},
controls={"AeEnable": True, "AwbEnable": True})
picam2.configure(config)
picam2.start()
time.sleep(0.3)
print("Ajusta el enfoque de la lente manualmente. Observa la métrica de nitidez.")
try:
while True:
frame = picam2.capture_array()
gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
fm = focus_metric(gray)
cv2.putText(frame, f"Focus metric: {fm:.1f}", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,255,0), 2)
cv2.imshow("Focus Helper (ESC para salir)", frame)
if cv2.waitKey(1) & 0xFF == 27:
break
finally:
picam2.stop()
cv2.destroyAllWindows()
if __name__ == "__main__":
main()
Este segundo script te ayuda a ajustar la lente de la HQ Camera: gira el anillo de enfoque para maximizar el valor “Focus metric”.
Compilación/flash/ejecución
Se ejecuta en Python sin compilación nativa. Sigue los pasos exactamente:
1) Instalar dependencias de sistema
sudo apt-get update
sudo apt-get install -y \
python3-venv python3-pip python3-opencv python3-picamera2 libcamera-apps \
tesseract-ocr tesseract-ocr-eng \
python3-gpiozero python3-smbus python3-spidev \
pkg-config cmake g++ wget git
2) Crear entorno virtual con acceso a paquetes del sistema
Nota: usar –system-site-packages para que el venv vea python3-opencv y python3-picamera2 instalados por apt.
mkdir -p ~/anpr-raspi4-hq
cd ~/anpr-raspi4-hq
python3 -m venv --system-site-packages venv
source venv/bin/activate
3) Instalar dependencias Python del proyecto en el venv
pip install --upgrade pip
pip install pytesseract==0.3.10 imutils==0.5.4
4) Verificar toolchain y librerías
python -c "import sys,cv2,pytesseract; print(sys.version); print('OpenCV',cv2.__version__); print('tesseract',pytesseract.get_tesseract_version())"
libcamera-hello -t 1500
libcamera-still -n -o ~/anpr-raspi4-hq/test_hq.jpg
5) Copiar los scripts
- Guarda anpr_pi4_hq.py y focus_and_exposure.py en ~/anpr-raspi4-hq.
- Hazlos ejecutables si lo deseas: chmod +x anpr_pi4_hq.py focus_and_exposure.py
6) Ajuste de enfoque (opcional pero recomendado)
source ~/anpr-raspi4-hq/venv/bin/activate
python focus_and_exposure.py
- Gira el anillo de enfoque hasta maximizar la “Focus metric”.
7) Ejecutar ANPR
- Modo con ventana:
source ~/anpr-raspi4-hq/venv/bin/activate
python anpr_pi4_hq.py --display --width 1920 --height 1080 --fps 30 --every 3 --minscore 5
- Modo headless (solo consola, guarda capturas cuando detecta):
source ~/anpr-raspi4-hq/venv/bin/activate
python anpr_pi4_hq.py --width 1920 --height 1080 --fps 30 --every 3 --minscore 5 --save
- Para probar con una sola captura estática, crea un frame con libcamera-still e inyecta al pipeline con un script adicional si lo prefieres. Por simplicidad, usa el modo en vivo.
Validación paso a paso
1) Validar hardware de cámara:
– Ejecuta libcamera-hello -t 2000. Debes ver la vista previa. Si no hay display, libcamera-still -n -o prueba.jpg y revisa el archivo.
2) Validar enfoque:
– Ejecuta python focus_and_exposure.py.
– Acerca una matrícula (o imprime una demo con patrones de alta frecuencia).
– Ajusta el anillo de enfoque para maximizar “Focus metric” (un valor significativamente mayor a ~100–200 indica buena nitidez; depende del campo de visión).
3) Validar pipeline de captura:
– python anpr_pi4_hq.py –display
– Debes ver una ventana “ANPR RPi4 HQ” con estado:
– “Sin placa” cuando no hay matrículas en el encuadre.
– “Candidato” cuando se detecta un contorno con la relación de aspecto adecuada pero OCR insuficiente.
– Texto con la matrícula cuando OCR reconoce 5+ caracteres (configurable con –minscore).
4) Validar OCR:
– Acerca un vehículo con matrícula estándar (EU/ES, caracteres negros en fondo blanco). Iluminación uniforme ayuda.
– Verifica que en consola (modo headless) aparece:
– [YYYY-MM-DDTHH:MM:SS] PLACA=XXXXXXX
– En modo –save, revisa el directorio output:
– annotated_.jpg con la caja verde y el texto.
– roi_.png con el recorte de la matrícula.
– roi_bin_*.png con el binarizado usado por Tesseract.
5) Validar rendimiento:
– Con –every 3, la CPU debería mantenerse por debajo del 150% total en una Pi 4 (htop). Ajusta –every según el uso de CPU.
– Cambia resolución a 1280×720 para más FPS si lo necesitas:
– python anpr_pi4_hq.py –display –width 1280 –height 720 –fps 30 –every 2
6) Validar lectura consistente:
– Mueve el vehículo lentamente o mueve la cámara. Debes ver lecturas estables. Si hay parpadeos, incrementa –every o mejora iluminación.
7) Validar persistencia:
– Habilita –save y revisa que se generan archivos en output/ con timestamp y matrícula en el nombre.
Troubleshooting
1) Error: “Cannot find a camera” o libcamera-hello falla
– Verifica el cable CSI (orientación cara azul hacia puertos HDMI/USB).
– Revisa dmesg | grep imx477 para asegurar que el driver del sensor se carga.
– Asegúrate de NO tener “Legacy Camera” activado en raspi-config (debe estar Disabled).
– Actualiza y reinicia: sudo apt-get update && sudo apt-get full-upgrade -y && sudo reboot.
– Verifica grupo de usuario: id; el usuario debe pertenecer a video (sudo usermod -aG video $USER; cierra sesión o reinicia).
2) ImportError: cannot import name ‘Picamera2’ o módulo no encontrado
– Asegúrate de que python3-picamera2 está instalado (apt).
– Si usas venv, créalo con –system-site-packages y actívalo antes de ejecutar.
– Comprueba: python -c «import picamera2» (sin errores).
3) cv2.imshow no abre ventana o cuelga bajo Wayland
– Ejecuta en modo headless sin –display y usa –save para validar.
– Alternativamente, instala soporte X11 y exporta: export QT_QPA_PLATFORM=xcb antes de ejecutar (si tienes X11 disponible).
– Verifica que el usuario está en el grupo video y que no hay sesiones remotas con forwarding de X mal configuradas.
4) Tesseract no instalado o pytesseract no encuentra el binario
– Asegura apt: sudo apt-get install -y tesseract-ocr tesseract-ocr-eng
– Comprueba ruta: which tesseract → /usr/bin/tesseract
– En Python: import pytesseract; pytesseract.get_tesseract_version() debe devolver 5.3.0.
5) OCR devuelve cadenas vacías o erróneas
– Iluminación: evita sombras y reflejos. Usa iluminación frontal difusa.
– Enfoque: usa focus_and_exposure.py para maximizar nitidez.
– Ajusta –minscore y TESS_CONFIG (psm=7 está bien para una línea; prueba psm=8 si tus placas son de 2 líneas).
– Aumenta la escala del ROI antes de OCR (factor > 1.5) para letras pequeñas.
– Filtra ruido: modifica kernel morfológico o añade medianBlur.
6) Detección de placa inestable (contorno pierde seguimiento)
– Ajusta umbrales de Canny (50–150 → prueba 75–200).
– Cambia el rango de aspecto en find_plate_contour (aspect_min=2.0, aspect_max=6.0, adapta a tu país/placa).
– Reduce vibraciones con montaje más rígido; estabiliza exposición (fija AeEnable=False y controla ExposureTime/AnalogueGain si dominas Picamera2).
7) CPU alta / fps bajos
– Baja resolución a 1280×720 o incluso 960×540.
– Incrementa –every para procesar menos frames.
– Desactiva –display y usa headless.
– Asegúrate de disipación adecuada; la Pi 4 puede thermal throttling sin ventilador.
8) Artefactos nocturnos o placas sobreexpuestas
– Usa iluminación adicional suave.
– Reduce ganancia y exposición: configura Picamera2 con controles manuales (p.ej., AeEnable=False, ExposureTime y AnalogueGain concretos).
– Considera añadir un filtro polarizador en diurno para reducir reflejos.
Mejoras/variantes
- Detector especializado de matrículas con DNN:
- Sustituye la fase de contornos por un detector entrenado (p.ej., YOLOv5/YOLOv8 pequeño) para mayor robustez. Detecta bounding boxes y después aplica OCR.
-
En la Pi 4, usa modelos tiny/rono y resoluciones bajas para mantener FPS.
-
OCR entrenado para OCR alfanumérico de placas:
- Prueba OCR con CRNN/DeepText o EasyOCR si dispones de aceleración y memoria suficiente.
-
Entrena un clasificador específico con whitelist y fuentes similares a matrículas de tu país.
-
Cache de tracking:
- Implementa un tracker (KCF/CSRT) entre detecciones para estabilizar la caja y reducir llamadas a Tesseract.
-
Fusión por temporalidad: mayoría de votos sobre N frames consecutivos para validar la matrícula.
-
Integración con GPIO (gpiozero):
- Acciona una barrera o enciende un LED cuando se reconoce una matrícula autorizada.
-
Librerías ya instaladas: gpiozero, smbus2, spidev.
-
Grabación y evidencia:
- Guarda vídeo con annotate overlay usando GStreamer/ffmpeg.
-
Metadata en JSON por lectura (timestamp, confianza, ROI).
-
Calibración óptica:
-
Calibra la lente para corregir distorsión con un tablero de ajedrez y cv2.calibrateCamera, si trabajas con focales extremas.
-
Preprocesado adaptable:
- Ajuste dinámico del umbral de Canny según histograma local.
- Filtros orientados (Sobel en dirección horizontal dominante de caracteres).
Checklist de verificación
- [ ] Raspberry Pi 4 + HQ Camera montados y cable CSI correctamente orientado (cara azul hacia HDMI/USB).
- [ ] Raspberry Pi OS Bookworm 64‑bit actualizado y reiniciado.
- [ ] Legacy Camera desactivado en raspi-config; libcamera funcionando (libcamera-hello muestra imagen).
- [ ] Dependencias instaladas con apt: python3-opencv 4.6.0, python3-picamera2 0.3.18, tesseract-ocr 5.3.0.
- [ ] Entorno virtual creado con –system-site-packages y activado.
- [ ] Dependencias pip instaladas: pytesseract 0.3.10, imutils 0.5.4.
- [ ] focus_and_exposure.py ejecutado y enfoque optimizado (Focus metric alto).
- [ ] anpr_pi4_hq.py ejecuta correctamente: muestra “Candidato” y reconoce matrículas reales en condiciones adecuadas.
- [ ] En modo –save se generan annotated_.jpg y roi_.png en output/.
- [ ] Uso de CPU aceptable y sin thermal throttling (disipación/ventilación adecuada).
- [ ] Opcional: integración con GPIO lista para futuras variantes (gpiozero/smbus/spidev instalados).
Con este caso práctico, dispones de un pipeline completo de opencv-anpr-license-plates en Raspberry Pi 4 + HQ Camera, reproducible y extensible para escenarios reales, optimizado para Raspberry Pi OS Bookworm 64‑bit y Python 3.11, con herramientas y versiones concretas indicadas paso a paso.
Encuentra este producto y/o libros sobre este tema en Amazon
Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.



