OMES

🚗 Reconocimiento de Placas Vehiculares con Python: YOLO, OpenCV y PaddleOCR

🚗 ¡Hola, hola, Omesitos! En este post aprenderemos a reconocer el contenido de placas vehiculares. Para ello entrenaremos un modelo que detecte matriculas vehiculares usando YOLO. Una vez detectada la placa extraeremos el texto con PaddleOCR. ¡Así que vamos a empezar!

¿Cómo reconocer placas vehiculares con visión artificial en Python? 

Antes de pasar al código debemos tener en claro el proceso que vamos a llevar a cabo. Entonces, supongamos que tenemos la imagen de un auto en donde se vea la placa vehicular. ¿Qué tendríamos que hacer para extraer su texto? 

  1. Identificar el ROI o región de interés dentro de la imagen, es decir la placa vehicular. ¿Cómo lo haremos? Mediante un modelo de detección de objetos, que entrenaremos. 
  1. Una vez que tengamos la región de interés será necesario agrandar un poco ese área para prevenir cualquier error, por si el detector de placas vehiculares corta alguna sección de la placa.
  1. Como ya tenemos la región de interés, podemos aplicar reconocimiento óptico de caracteres u OCR, para ello usaremos Paddle OCR, ¿por qué? Es bastante bueno en sus resultados y nos ayuda si es que el texto tiene cierta inclinación. Aunque puede tener un tiempo de ejecución un poquito más grande que Tesseract OCR, por ejemplo. 
  1. Una vez que extraigamos el texto será necesario tratarlo un poco. Por la diferencia de placas entre distintos países, en donde se podría usar guiones, espacios o puntos entre los caracteres, nosotros extraeremos únicamente los números y letras. 

Ahora que tenemos el proceso claro, vamos a empezar con entrenar el modelo para detectar placas vehiculares. 

📌 Nota importante: Esta sección contiene únicamente la programación empleada. Si deseas una explicación paso a paso con mayor detalle, no olvides revisar el video completo disponible en el canal.

Detección de placas de autos con YOLO

Como lo había comentado previamente, necesitamos identificar el área de interés en donde esté presente la placa vehicular, entonces entrenaremos un modelo de detección de placas vehiculares (a partir de otro preentrenado para detección de distintos objetos) con ayuda de Ultralytics YOLO. Además trabajaremos con herramientas como Google Colab, para acelerar el entrenamiento.

A continuación, te comparto la programación utilizada durante el video.

1.  Instalación de librerías necesarias 🛠️

!pip install roboflow ultralytics

2. Descarga de Dataset desde Roboflow 🗂️

El Dataset seleccionado en esta ocasión es: license-plate Computer Vision Model. Este se encuentra en Roboflow y sus imágenes son similares a las de un control de entrada y salida de autos, por lo que puede ser útil si necesitas realizar un proyecto similar.

from roboflow import Roboflow

rf = Roboflow(api_key="YOUR-API-KEY")
project = rf.workspace("licenseplate-s6fjf").project("license-plate-xmnzu")
version = project.version(1)
dataset = version.download("yolov11")

3. Carga del modelo base YOLOv11 🤖

from ultralytics import YOLO

model = YOLO("yolo11m.pt")

4. Entrenamiento del modelo personalizado 🤖🏋️

data_path = "/content/license-plate-1/data.yaml"
results = model.train(data=data_path,
                      epochs=15,
                      imgsz=640)

5. ¡A hacer predicciones! 🤖

# Cargamos el modelo ya entrenado
custom_model = YOLO("/content/runs/detect/train/weights/best.pt")

# Realizamos predicciones sobre algunas imágenes
res = custom_model("/content/license-plate-1/test/images")
# Visualizamos los resultados de las detecciones
for r in res:
  r.show()

Aplicar el modelo y extraer el texto de las placas vehiculares

Una vez que tenemos el modelo de detección de placas vehiculares, pasaremos a aplicarlo sobre la imagen de entrada, luego identificamos la sección de interés agrandándola un poco para aplicar PaddleOCR y extraer el texto. Finalmente, para estandarizar el resultado del reconocimiento, solo se visualizará letras desde la A a la Z y números del 0 al 9.

# Importar librerías necesarias
from ultralytics import YOLO
from paddleocr import PaddleOCR
import cv2
import imutils
import re

# Cargar imagen de entrada
image = cv2.imread("./Inputs/image_001.jpg")

# Inicializar modelos
model = YOLO("best.pt") # Modelo YOLO entrenado para detectar placas vehiculares
ocr = PaddleOCR(use_angle_cls=True, lang='en') # OCR con corrección de inclinación

# Ejecutar YOLO sobre la imagen
results = model(image)
#print(results[0].boxes)

for result in results:
    # Filtrar solo las detecciones de clase "placa" (cls == 0)
    index_plates = (result.boxes.cls == 0).nonzero(as_tuple=True)[0]
    #print(index_plates)

    for idx in index_plates:
        # Obtener confianza de la caja
        conf = result.boxes.conf[idx].item()
        if conf > 0.7:
            # Obtener las coordenadas de la caja
            xyxy = result.boxes.xyxy[idx].squeeze().tolist()
            x1, y1 = int(xyxy[0]), int(xyxy[1])
            x2, y2 = int(xyxy[2]), int(xyxy[3])
            
            # Recortar imagen de la placa con padding
            plate_image = image[y1-15:y2+15, x1-15:x2+15]

            # Ejecutar OCR con PaddleOCR
            result_ocr = ocr.predict(cv2.cvtColor(plate_image, cv2.COLOR_BGR2RGB))
            #print(result_ocr)

            # Ordenar los textos detectados de izquierda a derecha
            boxes = result_ocr[0]['rec_boxes']
            texts = result_ocr[0]['rec_texts']
            left_to_right = sorted(zip(boxes, texts), key=lambda x: min(x[0][::2]))
            print(f"left_to_right:", left_to_right)
            
            # Filtrar por whitelist (solo letras mayúsculas y números)
            whitelist_pattern = re.compile(r'^[A-Z0-9]+$')
            left_to_right = ''.join([t for _, t in left_to_right])
            output_text = ''.join([t for t in left_to_right if whitelist_pattern.fullmatch(t)])
            print(f"output_text: {output_text}")
            
            # Visualización
            cv2.imshow("plate_image", plate_image)
            # Dibujar resultados sobre la imagen
            cv2.rectangle(image, (x1 - 10, y1 - 35), (x2 + 10, y2-(y2 -y1)), (0, 255, 0), -1)
            cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 0), 2)
            cv2.putText(image, output_text, (x1-7, y1-5), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 0), 2)
            
# Mostrar imagen final
cv2.imshow("Image", imutils.resize(image, width=720))
cv2.waitKey(0)
cv2.destroyAllWindows()

Y podremos obtener un resultados como los siguientes:

Y eso ha sido todo por este post, Omesito/a. 😊
¡Cuídate mucho y nos vemos en el siguiente tutorial!

Salir de la versión móvil