Reconocimiento automático de matrículas basado en deep learning (pasos detallados + código fuente)

¡ Haga clic en la tarjeta a continuación para seguir la cuenta pública de " OpenCV y AI Deep Learning "!

 

¡Productos secos pesados ​​visuales/de imagen, entregados lo antes posible!

Fuente | Aprende OpenCV

Autor | Sanyam

Traducción | OpenCV y AI Deep Learning

Lectura guiada

Este artículo se centrará en la implementación integral de ALPR. Se centrará en dos procesos: detección de matrículas y OCR de matrículas detectadas. (Número público: OpenCV y AI Deep Learning)

 Introducción de antecedentes

    El aprendizaje profundo ha sido una de las tecnologías de más rápido crecimiento en el mundo moderno. El aprendizaje profundo se ha convertido en parte de nuestra vida diaria y está en todas partes, desde asistentes de voz hasta automóviles autónomos. Una de estas aplicaciones es el reconocimiento automático de matrículas (ALPR). Como sugiere el nombre, ALPR es una tecnología que utiliza el poder de la inteligencia artificial y el aprendizaje profundo para detectar y reconocer automáticamente los caracteres de las placas de los vehículos.

    Este artículo se centrará en la implementación integral de ALPR. Se centrará en dos procesos, [1] detección de matrículas, [2] OCR de matrículas detectadas. 

 Introducción a ALPR

    Imagina un hermoso verano, estás conduciendo por la autopista, tu canción favorita está sonando en la radio, cruzas el límite de velocidad y estás conduciendo a través de una zona de 70 km/h a 90 km/h para algunas cámaras, luego date cuenta tu error pero es demasiado tarde. Después de algunas semanas, recibirá un boleto con evidencia de la imagen de su automóvil. Debe estar preguntándose, ¿verifican manualmente cada imagen y envían un ticket?

    Por supuesto que no, eso fue enviado por el sistema ALPR. A partir de imágenes o videos capturados, ALPR detecta y extrae su número de placa y le envía una multa. Todo se basa en un sistema ALPR simple y unas pocas líneas de código.

    El Reconocimiento Automático de Matrículas (ALPR) o ANPR es la tecnología encargada de leer las matrículas de los vehículos en imágenes o secuencias de vídeo mediante el reconocimiento óptico de caracteres. Con los avances recientes en aprendizaje profundo y visión artificial, estas tareas se pueden realizar en milisegundos.

 Cómo funciona ALPR

    ALPR es una de las aplicaciones de visión artificial más utilizadas. Utiliza varios métodos como detección de objetos, OCR, segmentación de imágenes, etc. Para el hardware, un sistema ALPR solo necesita una cámara y una buena GPU. Para simplificar, esta publicación de blog se centrará en un proceso de dos pasos.

[1] Detección: Primero, una imagen o cuadro de una secuencia de video se pasa desde una cámara o un archivo almacenado a un algoritmo de detección, que detecta una matrícula y devuelve la ubicación del cuadro delimitador para esa matrícula.

[2] Reconocimiento: aplique OCR a la matrícula detectada, reconozca los caracteres de la matrícula y devuelva los caracteres en el mismo orden en formato de texto. La salida se puede almacenar en una base de datos o trazar en una imagen para su visualización. 

Repasemos cada paso en detalle.

 Usa YOLO V4 para detectar matrículas

    Este módulo de canalización se encarga de detectar matrículas a partir de imágenes o fotogramas de una secuencia de vídeo. 

    El proceso de detección se puede realizar con cualquier detector, ya sea un detector basado en áreas o un detector de un disparo. Esta publicación de blog se centrará en el detector de un solo disparo de YOLO v4, principalmente debido a su buena relación entre velocidad y precisión y su capacidad para detectar mejor objetos pequeños. YOLOv4 se implementará utilizando el marco Darknet.

red oscura

    Darknet es un marco de red neuronal de código abierto escrito en C y CUDA. YOLOv4 usa CSPDarknet53 CNN, lo que significa que su red troncal de detección de objetos usa Darknet53, con un total de 53 capas convolucionales. Darknet es muy fácil de instalar, usar y se puede hacer con solo unas pocas líneas de código.

git clone https://github.com/AlexeyAB/darknet

    Se instalará y compilará Darknet, y se configurarán algunos parámetros según las necesidades del entorno.

%cd darknetsed -i 's/OPENCV=0/OPENCV=1/' Makefilesed -i 's/GPU=0/GPU=1/' Makefilesed -i 's/CUDNN=0/CUDNN=1/' Makefilesed -i 's/CUDNN_HALF=0/CUDNN_HALF=1/' Makefilesed -i 's/LIBSO=0/LIBSO=1/' Makefile

    ¡Felicidades! Darknet ya está instalado.

    Aquí, algunos parámetros (como OpenCV, GPU, CUDA, etc.) se establecen en 1, es decir, se establecen en True, porque son necesarios para mejorar la eficiencia del código y ejecutar los cálculos más rápido.

conjunto de datos

    Los datos están en el corazón de cualquier aplicación de IA y son uno de los primeros y más importantes pasos. Para entrenar el detector YOLOv4, se utilizará el conjunto de datos Vehicle Open Image de Google. "Imágenes abiertas" de Google es un conjunto de datos de código abierto que contiene miles de imágenes de objetos anotados para la detección, segmentación y más de objetos. El conjunto de datos contiene 1500 imágenes de entrenamiento y 300 imágenes de validación en formato YOLO. El conjunto de datos se puede descargar desde aquí y colocar en una carpeta llamada datos. Echemos un vistazo al conjunto de datos.

import math# Creating a list of image files of the dataset.data_path = './data/obj/train/'files = os.listdir(data_path)img_arr = []
# Displaying 4 images only.num = 4
# Appending the array of images to a list.for fimg in files:    if fimg.endswith('.jpg'):      demo = img.imread(data_path+fimg)      img_arr.append(demo)      if len(img_arr) == num:        break
# Plotting the images using matplotlib._, axs = plt.subplots(math.floor(num/2), math.ceil(num/2), figsize=(50, 28))
axs = axs.flatten()
for cent, ax in zip(img_arr, axs):    ax.imshow(cent)plt.show()

Capacitación

    Para que el modelo aprenda, necesita ser entrenado en el conjunto de datos. Antes de iniciar el proceso de entrenamiento, es necesario modificar el archivo de configuración (.cfg). Los parámetros que deben modificarse son el tamaño del lote, la subdivisión, la clase, etc. Descargue el archivo de configuración desde aquí .

    Ahora que los datos están en su lugar y la configuración está completa, ¿cómo accederá el modelo a los datos? Se crean dos archivos, uno de los cuales contiene información para datos de entrenamiento, datos de prueba e información de clase. Llamémoslo obj.data (que se puede descargar desde aquí ) y otro es obj.names que contiene los nombres de todas las clases. Puede descargar obj.names desde aquí .

    El siguiente paso es descargar los pesos preentrenados para YOLOv4.

wget https://github.com/AlexeyAB/darknet/releases/download/darknet_yolo_v3_optimal/yolov4.conv.137

¡Ahora viene la gran parte del entrenamiento!

./darknet detector train data/obj.data cfg/yolov4-obj.cfg yolov4.conv.137 -dont_show -map

    Los parámetros incluyen el archivo obj.data, el archivo de configuración y los pesos preentrenados de yolov4, como se describió anteriormente.

  • -dont_show se pasa cuando no queremos mostrar la salida. Además, debe pasar esto cuando ejecute el código en el cuaderno de google colab, ya que no es compatible con la salida de GUI, no pasarlo causará un error.

  • -map se pasa para calcular el mAP predicho después de algunas iteraciones.

    Esperemos unas horas, ¡hurra! El modelo ahora está entrenado. Si desea omitir el proceso de capacitación, también puede descargar modelos capacitados o ajustados desde aquí.

evaluar

    Es importante juzgar qué tan bien se desempeña un modelo entrenado en datos no vistos. Esta es una excelente manera de saber si el modelo está funcionando bien o se está sobreajustando. Para las tareas de detección de objetos, una de las métricas es la precisión media, o mAp para abreviar. En una explicación avanzada, el cuadro delimitador predicho se compara con el cuadro delimitador detectado y se devuelve una puntuación denominada mAP.

    Este código guarda automáticamente el gráfico de progreso del entrenamiento, que es como se desempeñó nuestro modelo, logrando un 90 % de mAP después de 3000 épocas en 5,3 horas.

razonamiento

    Ahora el detector de matrículas está completamente entrenado. Es hora de usarlo. Para ello crearemos una función llamada yolo_det(), esta función se encarga de detectar el cuadro delimitador de la matrícula a partir de la imagen del vehículo de entrada.

def yolo_det(frame, config_file, data_file, batch_size, weights, threshold, output, network, class_names, class_colors, save = False, out_path = ''):
  prev_time = time.time()    # Preprocessing the input image.  width = darknet.network_width(network)  height = darknet.network_height(network)  darknet_image = darknet.make_image(width, height, 3)  image_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)  image_resized = cv2.resize(image_rgb, (width, height))    # Passing the image to the detector and store the detections  darknet.copy_image_from_bytes(darknet_image, image_resized.tobytes())  detections = darknet.detect_image(network, class_names, darknet_image, thresh=threshold)  darknet.free_image(darknet_image)
  # Plotting the deetections using darknet in-built functions  image = darknet.draw_boxes(detections, image_resized, class_colors)  print(detections)  if save:    im = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)    file_name = out_path + '-det.jpg'    cv2.imwrite(os.path.join(output, file_name), im)
  # Calculating time taken and FPS for detection  det_time = time.time() - prev_time  fps = int(1/(time.time() - prev_time))  print("Detection time: {}".format(det_time))    # Resizing predicted bounding box from 416x416 to input image resolution  out_size = frame.shape[:2]  in_size = image_resized.shape[:2]  coord, scores = resize_bbox(detections, out_size, in_size)  return coord, scores, det_time

 Reconocimiento de texto de matrícula

    Ahora que hemos entrenado nuestro detector de matrículas personalizado, es hora de pasar al segundo paso de ALPR, el reconocimiento de texto.

El reconocimiento de texto es el proceso de identificar texto de una escena mediante la comprensión y el análisis de sus patrones subyacentes. También se conoce como reconocimiento óptico de caracteres u OCR. También se puede utilizar para diversas aplicaciones, como lectura de documentos, recuperación de información, identificación de productos en estantes y más. El OCR se puede entrenar o utilizar como un modelo previamente entrenado. En este artículo, se utilizará un modelo de OCR previamente entrenado.

PádelOCR

    PaddleOCR es uno de esos marcos o herramientas para OCR. PaddleOCR proporciona a los usuarios una práctica herramienta OCR multilingüe que ayuda a los usuarios a aplicar y entrenar diferentes modelos en unas pocas líneas de código. PaddleOCR proporciona muchos modelos en su conjunto de herramientas, incluido PP-OCR, una serie de OCR preentrenado de alta calidad, algoritmos de última generación como SRN y algoritmos de OCR populares como CRNN. 

    PaddleOCR también ofrece diferentes tipos de modelos, ya sean ligeros (modelos que ocupan menos memoria) o pesados ​​(modelos que ocupan mucha memoria), así como pesos preentrenados disponibles de forma gratuita. 

Comparación OCR

    Como se mencionó en la sección anterior, PaddleOCR proporciona una variedad de modelos, y siempre es una buena práctica comparar qué modelo funciona bien en términos de precisión y velocidad.

    Los modelos se probaron en el conjunto de datos IC15, un conjunto de datos de texto de escena adjunto que contiene solo palabras en inglés. Contiene 1000 imágenes de entrenamiento, pero se prueba en 500 de ellas al azar. El modelo se prueba usando una medida de similitud de cuerdas llamada distancia de Levenshtein. La distancia de Levenshtein es el cambio necesario para implementar una cadena en otra. Cuanto menor sea la distancia, mejor será el modelo. Se prueban tres modelos en el conjunto de datos IC15 utilizando la distancia de Levenshtein en una GPU Tesla K80.

La atención se centrará en el ligero PPOCRv2 (11,6 millones). Logra un buen equilibrio entre velocidad, precisión y es muy liviano (es decir, ocupa muy poca memoria). También proporciona soporte para inglés y chino. Consulte aquí el código de comparación de OCR .

implementación de reconocimiento óptico de caracteres

    Ahora es el momento de implementar el modelo de OCR seleccionado. PaddleOCR se implementará en unas pocas líneas de código y funcionará de maravilla para nuestro sistema ALPR.

    Primero, instalemos los kits de herramientas y las dependencias necesarias. Estas dependencias y herramientas nos ayudarán a acceder a todos los archivos y scripts necesarios para la implementación de OCR. ​​​​​​​

pip install paddlepaddle-gpupip install "paddleocr>=2.0.1"

    Después de la instalación, se debe inicializar el OCR de acuerdo con nuestros requisitos. ​​​​​​​

from paddleocr import PaddleOCRocr = PaddleOCR(lang='en',rec_algorithm='CRNN')

    Usando PaddleOCR inicializamos OCR, toma varios parámetros, ellos son:

    • lang: especifica el idioma a reconocer

    • det_algorithm: especifica el algoritmo de detección de texto que se utilizará 

    • Rec_algorithm: especifica el algoritmo de reconocimiento que se utilizará

    Para ALPR, solo se pasan dos parámetros, el idioma y el algoritmo de reconocimiento. Aquí, usamos lang para inglés y el algoritmo de reconocimiento CRNN, también conocido como PPOCRv2 en este kit de herramientas.

    Este OCR se puede utilizar con una sola línea de código.

result = ocr.ocr(cr_img, cls=False, det=False)

    Aquí, cr_img es la imagen cls pasada a OCR, y det es un parámetro establecido en falso, ya que el detector de texto y el clasificador de ángulo de texto no son necesarios en nuestra tubería ALPR.

razonamiento

    Ahora que el detector de matrículas está completamente entrenado, OCR está listo. Es hora de ponerlo todo junto y ponerlo a trabajar. Para hacer esto, crearemos algunas funciones auxiliares para acceder a todas las funciones a la vez.

    Primero, crearemos una función que se encargue de recortar la imagen tomando la imagen y las coordenadas como argumentos, la llamaremos crop(). ​​​​​​​

def crop(image, coord):  # Cropping is done by -> image[y1:y2, x1:x2].  cr_img = image[coord[1]:coord[3], coord[0]:coord[2]]  return cr_img

Prueba de imagen

    Para realizar ANPR en una imagen, crearemos una función final como test_img() para realizar detección, recorte, OCR y trazado de salida en un solo lugar.

    Antes de eso, inicializaremos algunas variables que serán útiles a lo largo de esta publicación de blog. ​​​​​​​

# Variables storing colors and fonts.font = cv2.FONT_HERSHEY_SIMPLEXblue_color = (255,0,0)white_color = (255,255,255)black_color = (0,0,0)green_color = (0,255,0)yellow_color = (178, 247, 218)​​​​​​
def test_img(input, config_file, weights, out_path):  # Loading darknet network and classes along with the bbox colors.  network, class_names, class_colors = darknet.load_network(            config_file,            data_file,            weights,            batch_size= batch_size        )    # Reading the image and performing YOLOv4 detection.   img = cv2.imread(input)  bboxes, scores, det_time = yolo_det(img, config_file, data_file, batch_size, weights, thresh, out_path, network, class_names, class_colors)
  # Extracting or cropping the license plate and applying the OCR.  for bbox in bboxes:    cr_img = crop(img, bbox)    result = ocr.ocr(cr_img, cls=False, det=False)    ocr_res = result[0][0]    rec_conf = result[0][1]
    # Plotting the predictions using OpenCV.    (label_width,label_height), baseline = cv2.getTextSize(ocr_res , font, 2, 3)    top_left = tuple(map(int,[int(bbox[0]),int(bbox[1])-(label_height+baseline)]))    top_right = tuple(map(int,[int(bbox[0])+label_width,int(bbox[1])]))    org = tuple(map(int,[int(bbox[0]),int(bbox[1])-baseline]))
    cv2.rectangle(img, (int(bbox[0]), int(bbox[1])), (int(bbox[2]), int(bbox[3])), blue_color, 2)    cv2.rectangle(img, top_left, top_right, blue_color,-1)    cv2.putText(img, ocr_res, org, font, 2, white_color,3)
  # Writing output image.  file_name = os.path.join(out_path, 'out_' + input.split('/')[-1])  cv2.imwrite(file_name, img)

¡Felicidades! ! La canalización para ejecutar ALPR en la imagen se creó correctamente. Intentémoslo en una imagen aleatoria.

Primero, importaremos algunas bibliotecas y las funciones y métodos necesarios para aplicar ANPR. ​​​​​​​

# Importing libraries and required functionalities.# DeepSORT imports.%cd ./deep_sortfrom application_util import preprocessingfrom deep_sort import nn_matchingfrom deep_sort.detection import Detectionfrom deep_sort.tracker import Trackerfrom tools_deepsort import generate_detections as gdetimport uuid
# Required libraries.import osimport globimport randomimport timeimport cv2import numpy as npimport darknetimport subprocessimport sysfrom PIL import Imageimport matplotlibimport matplotlib.pyplot as plt%matplotlib inline

# Darknet object detector imports.%cd ./darknetfrom darknet_images import load_imagesfrom darknet_images import image_detection

​​​​​​​

# Declaring important variables.# Path of Configuration file of YOLOv4.config_file = './darknet/cfg/yolov4-obj.cfg'# Path of obj.data file.data_file = './darknet/data/obj.data'# Batch size of data passed to the detector.batch_size = 1# Path to trained YOLOv4 weights.weights = './checkpoint/yolov4-obj_best.weights'# Confidence threshold.thresh = 0.6​​​​​​​
# Calling the function.input_dir = 'car-img.jpg'out_path = '/content/'test_img(input_dir, config_file, weights,out_path)

    Ahora mostraremos el resultado final.

​​​​​​​

out_img = cv2.imread('./out_car-img.jpg')cv2.imshow(out_img)

Cargando... Recarga cancelada

Prueba de vídeo

    Después de probar nuestro ALPR en imágenes, podemos aplicarlo de manera similar a los videos. Para el video, solo aplicamos la canalización ALPR cuadro por cuadro de manera similar a las imágenes. Sumerjámonos en ello. ​​​​​​​

def test_vid(vid_dir, config_file, weights,out_path):  # Declaring variables for video processing.  cap = cv2.VideoCapture(vid_dir)  codec = cv2.VideoWriter_fourcc(*'XVID')  width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))  height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))  fps = int(cap.get(cv2.CAP_PROP_FPS))  file_name = os.path.join(out_path, 'out_' + vid_dir.split('/')[-1])  out = cv2.VideoWriter(file_name, codec, fps, (width, height))    # Frame count variable.  ct = 0    # Loading darknet network and classes along with the bbox colors.  network, class_names, class_colors = darknet.load_network(          config_file,          data_file,          weights,          batch_size= batch_size      )    # Reading video frame by frame.  while(cap.isOpened()):    ret, img = cap.read()    if ret == True:        print(ct)
        # Noting time for calculating FPS.        prev_time = time.time()
        # Performing the YOLOv4 detection.        bboxes, scores, det_time = yolo_det(img, config_file, data_file, batch_size, weights, thresh, out_path, network, class_names, class_colors)                # Extracting or cropping the license plate and applying the OCR.        if list(bboxes):          for bbox in bboxes:            cr_img, cord = crop(img, bbox)                        result = ocr.ocr(cr_img, cls=False, det=False)
            ocr_res = result[0][0]            rec_conf = result[0][1]
            # Plotting the predictions using OpenCV.            txt = ocr_res            (label_width,label_height), baseline = cv2.getTextSize(ocr_res , font,2,3)            top_left = tuple(map(int,[int(bbox[0]),int(bbox[1])-(label_height+baseline)]))            top_right = tuple(map(int,[int(bbox[0])+label_width,int(bbox[1])]))            org = tuple(map(int,[int(bbox[0]),int(bbox[1])-baseline]))
            cv2.rectangle(img, (int(bbox[0]), int(bbox[1])), (int(bbox[2]), int(bbox[3])), blue_color, 2)            cv2.rectangle(img, top_left, top_right, blue_color, -1)            cv2.putText(overlay_img,txt, org, font, 2, white_color, 3)            #cv2.imwrite('/content/{}.jpg'.format(ct), img)
          # Calculating time taken and FPS for the whole process.          tot_time = time.time() - prev_time          fps = 1/tot_time                    # Writing information onto the frame and saving it to be processed in a video.          cv2.putText(img, 'frame: %d fps: %s' % (ct, fps),                  (0, int(100 * 1)), cv2.FONT_HERSHEY_PLAIN, 5, (0, 0, 255), thickness=2)          out.write(img)                ct = ct + 1    else:      break

Es hora de probarlo en videos aleatorios. Puedes descargarlo desde aquí . ​​​​​​​

# Calling the function.input_dir = './Pexels Videos 2103099.mp4'out_path = '/content/'test_vid(input_dir, config_file, weights,out_path)

Mostrar salida (para cuadernos jupyter o colab). La salida se puede ver aquí . ​​​​​​​

from IPython.display import HTMLfrom base64 import b64encode
# Input video path.save_path = './out_Pexels Videos 2103099.mp4'
# Compressed video path.compressed_path = "./compressed.mp4"
#compressing the size of video to avoid crashing.os.system(f"ffmpeg -i {save_path} -vcodec libx264 {compressed_path}")
# Show video.mp4 = open(compressed_path,'rb').read()data_url = "data:video/mp4;base64," + b64encode(mp4).decode()HTML("""<video width=400 controls>      <source src="%s" type="video/mp4"></video>""" % data_url)

Integración de rastreadores

Como habrás visto en el apartado anterior, la salida de vídeo no es muy precisa y tiene muchos problemas.

    • estar nervioso

    • Fluctuaciones en la salida de OCR

    • pérdida de detección

Para abordar este problema, esta sección propone una solución para integrar el rastreador con el sistema ALPR. Pero, ¿cómo resuelve estos problemas el uso de rastreadores? vamos a ver.

El papel de los rastreadores en ALPR 

    Como se mencionó anteriormente, cuando se ejecuta ALPR en video, hay algunos problemas que hacen que ALPR sea menos preciso. Pero estos problemas se pueden corregir si se utilizan rastreadores. Los rastreadores se utilizan generalmente por las siguientes razones:

    • Funciona cuando falla la detección de objetos

    • Asignar identificación

    • ruta de seguimiento

    Todos los problemas que enfrenta ALPR, el rastreador solo se usa debido a estos problemas. El rastreador se utilizará para obtener el mejor resultado de OCR para una placa detectada específica.

    Una vez que se implementa el rastreador, devuelve las coordenadas y la identificación del cuadro delimitador, se aplicará OCR a cada cuadro delimitador y la salida se almacenará con la identificación. Para reducir el problema de fluctuación de la salida de OCR, se recopilan todos los cuadros delimitadores con la misma identificación hasta el marco actual, y el cuadro delimitador con la mayor confianza de OCR se reserva y se muestra para esa identificación. Cuando se implemente, el proceso será más claro.

Implementación del rastreador.

Para esto, creemos una nueva función auxiliar get_best_ocr() para implementar la lógica discutida en la sección anterior. ​​​​​​​

def get_best_ocr(preds, rec_conf, ocr_res, track_id):  for info in preds:    # Check if it is the current track id.    if info['track_id'] == track_id:      # Check if the ocr confidence is highest or not.      if info['ocr_conf'] < rec_conf:        info['ocr_conf'] = rec_conf        info['ocr_txt'] = ocr_res      else:        rec_conf = info['ocr_conf']        ocr_res = info['ocr_txt']      break  return preds, rec_conf, ocr_res

Finalmente, veremos la siguiente función que ejecuta ALPR en video y se llama tracker_test_vid() Será como test_vid() , usando el rastreador implementado con ella. Esta publicación de blog se centrará en el uso de DeepSORT como rastreador porque es liviano y fácil de usar, y también proporciona descriptores de apariencia con solo unas pocas líneas de código. Usaremos un modelo de métrica de asociación profunda preentrenado llamado mars-small128.pb, que se puede descargar desde aquí . ​​​​​​​

def tracker_test_vid(vid_dir, config_file, weights,out_path):  # Declaring variables for video processing.  cap = cv2.VideoCapture(vid_dir)  codec = cv2.VideoWriter_fourcc(*'XVID')  width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))  height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))  fps = int(cap.get(cv2.CAP_PROP_FPS))  file_name = os.path.join(out_path, 'out_' + vid_dir.split('/')[-1])
  out = cv2.VideoWriter(file_name, codec, fps, (width, height))
  # Declaring variables for tracker.  max_cosine_distance = 0.4  nn_budget = None    # Initializing tracker  model_filename = './model_data/mars-small128.pb'  encoder = gdet.create_box_encoder(model_filename, batch_size=1)  metric = nn_matching.NearestNeighborDistanceMetric("cosine", max_cosine_distance, nn_budget)  tracker = Tracker(metric)    # Initializing some helper variables.  ct = 0  preds = []  total_obj = 0  rec_tot_time = 1  alpha = 0.5    # Loading darknet network and classes along with the bbox colors.  network, class_names, class_colors = darknet.load_network(          config_file,          data_file,          weights,          batch_size= batch_size      )    # Reading video frame by frame.  while(cap.isOpened()):    ret, img = cap.read()    if ret == True:
        h, w = img.shape[:2]        print(ct)                w_scale = w/1.55        h_scale = h/17        top_left = (int(w_scale) + 10 + label_width, int(h_scale))
        # Method to blend two images, here used to make the information box transparent.        overlay_img = img.copy()        cv2.rectangle(img, (w_scale, 0), (w, int(h_scale*3.4)), black_color, -1)        cv2.addWeighted(img, alpha, overlay_img, 1 - alpha, 0, overlay_img)
        # Noting time for calculating FPS.        prev_time = time.time()
        # Performing the YOLOv4 detection.        bboxes, scores, det_time = yolo_det(img, config_file, data_file, batch_size, weights, thresh, out_path, network, class_names, class_colors)                if list(bboxes):          # Getting appearance features of the object.          features = encoder(img, bboxes)          # Storing all the required info in a list.          detections = [Detection(bbox, score, feature) for bbox, score, feature in zip(bboxes, scores, features)]
          # Applying tracker.          # The tracker code flow: kalman filter -> target association(using hungarian algorithm) and appearance descriptor.          tracker.predict()          tracker.update(detections)          track_time = time.time() - prev_time                    # Checking if tracks exist.          for track in tracker.tracks:            if not track.is_confirmed() or track.time_since_update > 1:                continue
            # Changing track bbox to top left, bottom right coordinates            bbox = list(track.to_tlbr())                        for i in range(len(bbox)):              if bbox[i] < 0:                bbox[i] = 0
            # Extracting or cropping the license plate and applying the OCR.            cr_img = crop(img, bbox)                        rec_pre_time = time.time()            result = ocr.ocr(cr_img, cls=False, det=False)            rec_tot_time = time.time() - rec_pre_time
            ocr_res = result[0][0]            rec_conf = result[0][1]                        if rec_conf == 'nan':              rec_conf = 0
            # Storing the ocr output for corresponding track id.            output_frame = {"track_id":track.track_id, "ocr_txt":ocr_res, "ocr_conf":rec_conf}                        # Appending track_id to list only if it does not exist in the list.            if track.track_id not in list(set(ele['track_id'] for ele in preds)):              total_obj = total_obj + 1              preds.append(output_frame)            # Looking for the current track in the list and updating the highest confidence of it.            else:              preds, rec_conf, ocr_res = get_best_ocr(preds, rec_conf, ocr_res, track.track_id)              # Plotting the predictions using OpenCV.            txt = str(track.track_id) + '. ' + ocr_res            (label_width,label_height), baseline = cv2.getTextSize(ocr_res , font,2,3)            top_left = tuple(map(int,[int(bbox[0]),int(bbox[1])-(label_height+baseline)]))            top_right = tuple(map(int,[int(bbox[0])+label_width,int(bbox[1])]))            org = tuple(map(int,[int(bbox[0]),int(bbox[1])-baseline]))
            cv2.rectangle(img, (int(bbox[0]), int(bbox[1])), (int(bbox[2]), int(bbox[3])), blue_color, 2)            cv2.rectangle(img, top_left, top_right, blue_color, -1)            cv2.putText(overlay_img,txt, org, font, 2, white_color, 3)            #cv2.imwrite('/content/{}.jpg'.format(ct), img)
          # Calculating time taken and FPS for the whole process.          tot_time = time.time() - prev_time          fps = 1/tot_time                    # Writing information onto the frame and saving the frame to be processed into a video with title and values of different colors.          if w < 2000:            size = 1          else:            size = 2
          # Plotting frame count information on the frame.          (label_width,label_height), baseline = cv2.getTextSize('Frame count:' , font,size,2)          cv2.putText(overlay_img, 'Frame count:', top_left, font, size, green_color, thickness=2)          cv2.putText(overlay_img,'%d ' % (ct), top_left, font, size, yellow_color, thickness=2)
          (label_width,label_height), baseline = cv2.getTextSize('Frame count:' + ' ' + str(ct) , font, size,2)          cv2.putText(overlay_img, 'Total FPS:' , top_left, font, size, green_color, thickness=2)
          (label_width,label_height), baseline = cv2.getTextSize('Frame count:' + ' ' + str(ct) + 'Total FPS:' , font, size,2)          cv2.putText(overlay_img, '%s' % (int(fps)), top_left, font, size, yellow_color, thickness=2)
          # Plotting Total FPS of ANPR information on the frame.          cv2.putText(overlay_img, 'Detection FPS:' ,(top_left[0], int(h_scale*1.7)), font, size, green_color, thickness=2)          (label_width,label_height), baseline = cv2.getTextSize('Detection FPS:', font,size,2)          cv2.putText(overlay_img, '%d' % ((int(1/det_time))),(top_left[0], int(h_scale*1.7)), font, size, yellow_color, thickness=2)
          # Plotting Recognition/OCR FPS of ANPR on the frame.          cv2.putText(overlay_img, 'Recognition FPS:',(top_left[0], int(h_scale*2.42)), font, size, (green_color, thickness=2)          (label_width,label_height), baseline =                     cv2.getTextSize('Recognition FPS:', font,size,2)          cv2.putText(overlay_img, '%s' % ((int(1/rec_tot_time))),(top_left[0], int(h_scale*2.42)), font, size, yellow_color, thickness=2)          cv2.imwrite('/content/{}.jpg'.format(ct), overlay_img)          out.write(overlay_img)                # Increasing frame count.        ct = ct + 1    else:      break

Ejecútelo de manera similar a la sección anterior. ​​​​​​​

# Calling the function.input_dir = './Pexels Videos 2103099.mp4'out_path = '/content/'tracker_test_vid(input_dir, config_file, weights,out_path)

La salida se puede mostrar como se muestra antes. Este es el resultado final , ya que se puede ver claramente que todos los problemas discutidos se reducen considerablemente, ALPR parece ser bastante preciso y funciona bien a 14-15 FPS.

En conclusión

    En esta publicación de blog, construimos un sistema ALPR o ANPR de 14 a 15 FPS. Aquí, nos enfocamos en un proceso de dos pasos: i) detector de matrículas, ii) extracción del detector de matrículas y OCR. 

    Durante este proceso, muchas preguntas pueden surgir en su cerebro, como por ejemplo, ¿cómo acelerar? ¿Cómo mejorar la precisión? ¿Cómo responderá el rastreador a la oclusión? etc Una forma es tratar de averiguarlo por sí mismo.

Aquí, las matrículas se entrenaron con un 90 % de precisión. Si la velocidad es el objetivo principal de un detector de matrículas, se prefiere YOLO-tiny, que proporciona una mejor velocidad que YOLOv4, pero con una compensación en la precisión. 

    Además, el PP-OCR de PaddleOCR funciona perfectamente, es liviano y muy preciso, con un buen equilibrio entre precisión y velocidad. PaddleOCR proporciona varios modelos, como SRN, PPOCR pesado, etc., que se pueden usar o incluso entrenar desde cero para lograr los resultados deseados.

    Pero el enfoque ideal para nuestro ALPR es usar un rastreador, que mantiene los mejores resultados de OCR. Varios otros rastreadores como el rastreador OpenCV, CenterTrack, Tracktor, etc. que resuelven diferentes problemas de alto nivel como oclusión, Re-id, etc.

    Siéntase libre de explorar referencias, ajustar su entrada y encontrar más formas de hacer que las tareas sean más desafiantes.

La dirección del código de este artículo:

https://github.com/spmallick/learnopencv/tree/master/ALPR

Referirse a

YOLOv4:  https://github.com/AlexeyAB/darknet

Conjunto de datos de matrícula: 

https://storage.googleapis.com/openimages/web/index.html

PaddleOCR:  https://github.com/PaddlePaddle/PaddleOCR

Vídeo de prueba: 

https://www.pexels.com/video/traffic-flow-in-the-highway-2103099/

Clasificación profunda: https://github.com/nwojke/deep_sort

Descarga 1: Manual de funciones comunes de Pytoch

Responda en el fondo del número " OpenCV and AI Depth ": manual de funciones de Pytorch , puede aprender y descargar el primer manual común de funciones de Pytorch en toda la red, incluida la introducción de tensores , la introducción de funciones básicas, funciones de procesamiento de datos, funciones de optimización, CUDA programación, multiprocesamiento , etc. diez El contenido del capítulo cuatro.

Descarga 2: 145 Código de aplicación de ejemplo de OpenCV

Responda en el fondo de la cuenta oficial " OpenCV and AI Deepening": OpenCV145 , puede aprender y descargar 145 códigos de aplicación de ejemplo de OpenCV ( Implementación de lenguaje dual Python y C++ ).

Supongo que te gusta

Origin blog.csdn.net/stq054188/article/details/123720323
Recomendado
Clasificación