Image

Real Time License Plate Detection And Using OCR Model

Automating the process of detecting and recognizing license plates is the goal of the cutting-edge computer vision project License Plate Detection with YOLOV8 and Recognition with OCR Models. YOLOv8, a cutting-edge real-time object identification system, is used in this research to precisely locate license plates in photos and video feeds.

With its dependable and effective approach to automating license plate detection and identification operations, License Plate Detection with YOLOV8 and identification with OCR Models is a state-of-the-art solution in the field of computer vision.

Explanation All Code

Step:1

The Python code given is for Google Colab, a cloud-based platform for Python programming. A user can easily access files in Colab environment through google drive. After logging in, the user's Google Drive may be accessed at '/content/drive' in the Colab environment. The line drive.mount('/content/drive') starts the mounting procedure. When working with files or datasets kept on Google Drive, this is useful.

from google.colab import drive
drive.mount('/content/drive')

By utilising Google Colab's os module, this Python function modifies the current working directory to "/content/drive/aionlinecourse/real_time_license_plate_detection_and_using_ocr" and prints the current working directory to confirm the modification. Accessing or saving files within the designated directory is accomplished by doing this.

import os
# Set the current directory
os.chdir("/content/drive/aionlinecourse/real_time_license_plate_detection_and_using_ocr")
# Verify the change in the current directory
os.getcwd()

After using %pip install ultralytics to install the ultralytics package, the code imports the module and uses ultralytics.checks() to verify the setup.

%pip install ultralytics
import ultralytics
ultralytics.checks()

Step:2

This code creates a YAML configuration file (data.yaml) with images resized to 640 pixels and uses YOLOv8 to train a model (yolov8n.pt) on a given dataset for 100 epochs through training.

!yolo train model=yolov8n.pt data= "/content/drive/aionlinecourse/real_time_license_plate_detection_and_using_ocr/dataset/data.yaml" epochs=100 imgsz=640

Important libraries for computer vision and image processing are imported into the code, such as PyTorch ('torch'), OpenCV (cv2), Ultralytics ('ultralytics'), and image modification and visualisation tools ('transforms', 'Image', 'pyplot').

import torch
import cv2
import ultralytics
from cv2 import imread
from ultralytics import YOLO
from torchvision import transforms
from PIL import Image
from matplotlib import pyplot as plt

This code loads a pretrained YOLOv8n model from the specified path using Ultralytics.

# Load a pretrained YOLOv8n model
model = YOLO('/content/drive/aionlinecourse/real_time_license_plate_detection_and_using_ocr/best.pt ')

This code uses the previously loaded YOLOv8n model to do inference on a image file ('car.png'). The results are stored in the 'results' variable.

# Run inference on '.jpg'
results = model('/content/drive/aionlinecourse/real_time_license_plate_detection_and_using_ocr/car.png') # results list

Step:3

Using a custom YOLOv8 model ('best.pt') on an image ('car.png'), this code makes predictions using the Ultralytics YOLO wrapper. The Matplotlib package and PIL are then used to store and display the results.


Model Loading: Opens the given path and loads a customized YOLOv8 model.

Prediction: Use the image ('car.png') to do inference, saving the outcome. Results should be preserved, according to the save=True parameter.

Display and Save Predictions: Saves the result image as 'results.jpg' then iterates through the results, displaying the predictions with Matplotlib. The forecasts are plotted using the plot() method.

from ultralytics import YOLO
model = YOLO('/content/drive/aionlinecourse/real_time_license_plate_detection_and_using_ocr/best.pt') # load a custom model
# Predict with the model
results = model('/content/drive/aionlinecourse/real_time_license_plate_detection_and_using_ocr/car.png',save=True)
for r in results:
im_array = r.plot() # plot a BGR numpy array of predictions
im = Image.fromarray(im_array[..., ::-1]) # RGB PIL image
im.show() # show image im.save('/content/drive/aionlinecourse/real_time_license_plate_detection_and_using_ocr/results.jpg') # predict on an image

The predicted image from the YOLOv8 model is loaded and shown using this code.

The image is read using OpenCV's imread function, and it is shown using Matplotlib. The YOLOv8 model is run, and the anticipated image is stored in the supplied directory. For improved visualization, the axes of the presented image are hidden.

# Load the predicted image
predicted_img_path = '/content/drive/aionlinecourse/real_time_license_plate_detection_and_using_ocr/runs/detect/predict/car.png'
predicted_img = imread(predicted_img_path)
# Display the image
plt.figure(figsize=(8, 8))
plt.imshow(predicted_img)
plt.axis('off') # Hide axes
plt.show()

This code detects objects in a video file ('sample.mp4') using a customised YOLOv8 model ('best.pt') from Ultralytics. Throughout the inference procedure, the outcomes are kept.

Using the chosen YOLOv8 model, this script processes the input video's frames using the Ultralytics YOLO wrapper. The prediction process records the results. Depending on your project's unique requirements and pathways, adjustments might be necessary.

from ultralytics import YOLO
model = YOLO('/content/drive/aionlinecourse/real_time_license_plate_detection_and_using_ocr/best.pt') # Load a custom model
# Predict with the model on a video
results = model('/content/drive/aionlinecourse/real_time_license_plate_detection_and_using_ocr/sample.mp4', save=True)

Step:4

Looks like you're using pip to install multiple Python packages. Installing a particular package is represented by each line.

!pip install easyocr
!pip install scikit-image==0.17.2
!pip install lap
!pip install filterpy
!pip install pandas
!pip install opencv-python
!pip install numpy
!pip install scipy
!pip install easyocr
!pip install filterpy
!pip install utils

The 'easyocr' module is imported for optical character recognition, and the 'string' module is imported for string operations.

import string
import easyocr

Starts an OCR (optical character recognition) reader without GPU acceleration by utilising the 'easyocr' library for the English language.

# Initialize the OCR reader
reader = easyocr.Reader(['en'], gpu=False)

For character conversion, defines two dictionaries, then outputs the contents of each dictionary.

Character conversion in text processing activities is facilitated by these dictionaries (dict_char_to_int and dict_int_to_char), which map certain characters to their equivalent replacements.

# Mapping dictionaries for character conversion
dict_char_to_int = {'O': '0', 'I': '1', 'J': '3', 'A': '4', 'G': '6', 'S': '5'}
dict_int_to_char = {'0': 'O', '1': 'I', '3': 'J', '4': 'A', '6': 'G', '5': 'S'}
# Print the contents of the dictionaries
print("Dictionary char_to_int:", dict_char_to_int)
print("Dictionary int_to_char:", dict_int_to_char)

'write_csv' is a Python function that saves the output of a process that detects license plates and cars into a CSV file. Both **output_path (the path where the CSV file will be saved) and results (a dictionary holding detection results) are the two parameters that the function requires.


This is a synopsis of the functions capabilities:

  • Allows writing to the CSV file.
  • Enters the names of the columns in the header row.
  • For each identified car and license plate, iterates over the detection results and writes the pertinent data to the CSV file.
  • Closes the CSV document.

Note: The code operates under the assumption that the 'results' dictionary has a particular structure, with nested keys standing in for license plates, cars, and frames.

def write_csv(results, output_path):
with open(output_path, 'w') as f:
f.write('{},{},{},{},{},{},{}\n'.format('frame_nmr', 'car_id', 'car_bbox',
'license_plate_bbox', 'license_plate_bbox_score', 'license_number',
'license_number_score'))
for frame_nmr in results.keys():
for car_id in results[frame_nmr].keys():
print(results[frame_nmr][car_id])
if 'car' in results[frame_nmr][car_id].keys() and \
'license_plate' in results[frame_nmr][car_id].keys() and \
'text' in results[frame_nmr][car_id]['license_plate'].keys():
f.write('{},{},{},{},{},{},{}\n'.format(frame_nmr,
car_id,
'[{} {} {} {}]'.format(
results[frame_nmr][car_id]['car']['bbox'][0],
results[frame_nmr][car_id]['car']['bbox'][1],
results[frame_nmr][car_id]['car']['bbox'][2],
results[frame_nmr][car_id]['car']['bbox'][3]),
'[{} {} {} {}]'.format(
results[frame_nmr][car_id]['license_plate']['bbox'][0],
results[frame_nmr][car_id]['license_plate']['bbox'][1],
results[frame_nmr][car_id]['license_plate']['bbox'][2],
results[frame_nmr][car_id]['license_plate']['bbox'][3]),
results[frame_nmr][car_id]['license_plate']['bbox_score'],
results[frame_nmr][car_id]['license_plate']['text'],
results[frame_nmr][car_id]['license_plate']['text_score'])
)
f.close()

'license_complies_format', checks if a given license plate text complies with a specific format. The function takes a text string as input and returns 'True' if the text adheres to the specified format, and 'False' otherwise.


Here's a summary of the conditions for compliance:

    • The text length must be 7 characters.
      • The first two characters must be uppercase letters or their corresponding numeric replacements.
        • The third and fourth characters must be digits or their corresponding character replacements.
          • The fifth and sixth characters must be uppercase letters or their corresponding numeric replacements.

            • The seventh character must be an uppercase letter or its corresponding numeric replacement.

            This function utilizes conditions based on the length and specific character requirements to determine if the given license plate text complies with the format.


            def license_complies_format(text):
            if len(text) != 7:
            return False
            if (text[0] in string.ascii_uppercase or text[0] in dict_int_to_char.keys()) and \
            (text[1] in string.ascii_uppercase or text[1] in dict_int_to_char.keys()) and \
            (text[2] in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] or text[2] in dict_char_to_int.keys()) and \
            (text[3] in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] or text[3] in dict_char_to_int.keys()) and \
            (text[4] in string.ascii_uppercase or text[4] in dict_int_to_char.keys()) and \
            (text[5] in string.ascii_uppercase or text[5] in dict_int_to_char.keys()) and \
            (text[6] in string.ascii_uppercase or text[6] in dict_int_to_char.keys()):
            return True
            else:
            return False

            'format_license', depending on predefined mappings, transforms the text of a license plate into a formatted version. The conversion is carried out by the function using dictionaries ('dict_int_to_char' and 'dict_char_to_int').


            This is a synopsis of the functions capabilities:

            • Formats the formatted license plate into an empty string 'license_plate_'.
            • Specifies conversion rules for every point in the license plate text by defining a mapping dictionary.
            • Applying the conversion based on the mapping, iteratively goes through the characters in the input text.
            • Gives back the 'license_plate_' in format.

            This function returns the formatted result only after ensuring that every character in the license plate text has been transformed correctly using the designated mappings.

            def format_license(text):
            license_plate_ = ''
            mapping = {0: dict_int_to_char, 1: dict_int_to_char, 4: dict_int_to_char, 5: dict_int_to_char, 6: dict_int_to_char,
            2: dict_char_to_int, 3: dict_char_to_int}
            for j in [0, 1, 2, 3, 4, 5, 6]:
            if text[j] in mapping[j].keys():
            license_plate_ += mapping[j][text[j]]
            else:
            license_plate_ += text[j]
            return license_plate_

            Utilising an OCR reader ('reader'), the function 'read_license_plate' extracts text from a cropped image of a license plate ('license_plate_crop'). The formatted license plate and the associated score are then returned after it has determined whether the extracted text conforms with the required license plate format.


            What the function does is summarised as follows:

            • Extracts text, bounding boxes, and scores from the license plate image using the OCR reader ('reader.readtext').
            • Cycles over the text results that were detected.
            • Removes spaces from the text and converts it to uppercase.
            • Verifies that the processed text follows the format ('license_complies_format') for license plates. The formatted license plate and the associated score will be returned if the text follows the format.
            • Returns 'None, None' in the event that no compliant license plate is identified.

            The purpose of this function is to use an OCR detection on a cropped license plate image to extract and process license plate information.

            def read_license_plate(license_plate_crop):
            detections = reader.readtext(license_plate_crop)
            for detection in detections:
            bbox, text, score = detection
            text = text.upper().replace(' ', '')
            if license_complies_format(text):
            return format_license(text), score
            return None, None

            The function 'get_car', requires a list of vehicle track IDs ('vehicle_track_ids') and the bounding box for the license plate ('license_plate'). Using the coordinates of the license plate, it looks through the list for a matching automobile bounding box and, if one is found, produces the bounding box for that car.


            What the function does is summarised as follows:

            • Takes the coordinates ('x1, y1, x2, y2, score, class_id') out of the bounding box of the license plate.
            • Iterates through the list ('vehicle_track_ids') of vehicle track IDs.
            • Looks for matches by comparing the coordinates of the license plate with each car bounding box in the list.
            • Returns the bounding box of the matching automobile if a match is discovered.
            • Returns a default value (-1) indicating that no comparable automobile was discovered if no match is found.

            This feature links a list of vehicle track IDs to a bounding box for a license plate and the matching bounding box for an automobile.

            def get_car(license_plate, vehicle_track_ids):
            x1, y1, x2, y2, score, class_id = license_plate
            foundIt = False
            for j in range(len(vehicle_track_ids)):
            xcar1, ycar1, xcar2, ycar2, car_id = vehicle_track_ids[j]
            if x1 > xcar1 and y1 > ycar1 and x2 < xcar2 and y2 < ycar2:
            car_indx = j
            foundIt = True
            break
            if foundIt:
            return vehicle_track_ids[car_indx]
            return -1, -1, -1, -1, -1

            Step:5

            This little piece of code imports the SORT algorithm ('Sort'), OpenCV ('cv2'), and the YOLO module from Ultralytics ('YOLO') . An empty dictionary ('results') and a SORT tracker object ('mot_tracker') are initialised.

            from ultralytics import YOLO
            import cv2
            # import util
            from sort.sort import *
            # from util import get_car, read_license_plate, write_csv
            results = {}
            mot_tracker = Sort()

            For both general object detection ('coco_model') and license plate detection ('license_plate_detector'), Snippet loads YOLO models. It also specifies a list of vehicle classes ('vehicles') and loads a video file ('v.mp4') using OpenCV.

            This code uses the designated YOLO models and vehicle classes to build up the environment for object identification and tracking in a movie.


            # load models
            coco_model = YOLO('/content/drive/aionlinecourse/real_time_license_plate_detection_and_using_ocr/yolov8n.pt')
            license_plate_detector = YOLO('/content/drive/aionlinecourse/real_time_license_plate_detection_and_using_ocr/best.pt')
            # load video
            cap = cv2.VideoCapture('/content/drive/aionlinecourse/real_time_license_plate_detection_and_using_ocr/v.mp4')
            vehicles = [2, 3, 5, 7]

            This code scans a video for frames, recognises and tracks moving objects, recognises license plates, links license plates to follow-along vehicles, and handles license plate data. Thereafter, a CSV file contains the results.



            Frame Reading:

            • The video's frames are read (cap).
            • Starts a dictionary with the results to keep the tracking and detection information.

            Vehicle Detection and Tracking:

            • Identifies cars in the frame using the COCO model (coco_model).
            • Based on predefined classes (vehicles), filters contain just the cars in the detected boxes.
            • Updates the monitoring of vehicles using the SORT tracker (mot_tracker).

            License Plate Detection and Processing:

            • Detects license plates using the license plate detection model (license_plate_detector).
            • Associates used the get_car method to match license plates to tracked vehicles.
            • Crops, converts to grayscale, and thresholds each license plate.
            • Uses the read_license_plate function to obtain the license plate number and its score.

            Results Storage:

            • Keeps the results in the results dictionary, together with the car and license plate details.

            Results Writing:

            • Uses the write_csv function to write the outcomes to a CSV file.

            It appears that this code implements a full pipeline for tracking, license plate recognition, vehicle detection, and result storage.

            # read frames
            frame_nmr = -1
            ret = True
            while ret:
            frame_nmr += 1
            ret, frame = cap.read()
            if ret:
            results[frame_nmr] = {}
            # detect vehicles
            detections = coco_model(frame)[0]
            detections_ = []
            for detection in detections.boxes.data.tolist():
            x1, y1, x2, y2, score, class_id = detection
            if int(class_id) in vehicles:
            detections_.append([x1, y1, x2, y2, score])
            # track vehicles
            track_ids = mot_tracker.update(np.asarray(detections_))
            # detect license plates
            license_plates = license_plate_detector(frame)[0]
            for license_plate in license_plates.boxes.data.tolist():
            x1, y1, x2, y2, score, class_id = license_plate
            # assign license plate to car
            xcar1, ycar1, xcar2, ycar2, car_id = get_car(license_plate, track_ids)
            if car_id != -1:
            # crop license plate
            license_plate_crop = frame[int(y1):int(y2), int(x1): int(x2), :]
            # process license plate
            license_plate_crop_gray = cv2.cvtColor(license_plate_crop, cv2.COLOR_BGR2GRAY)
            _, license_plate_crop_thresh = cv2.threshold(license_plate_crop_gray, 64, 255, cv2.THRESH_BINARY_INV)
            # read license plate number
            license_plate_text, license_plate_text_score = read_license_plate(license_plate_crop_thresh)
            if license_plate_text is not None:
            results[frame_nmr][car_id] = {'car': {'bbox': [xcar1, ycar1, xcar2, ycar2]},
            'license_plate': {'bbox': [x1, y1, x2, y2],
            'text': license_plate_text,
            'bbox_score': score,
            'text_score': license_plate_text_score}}
            # write results
            write_csv(results, './test.csv')

            Step:6

            This little piece of code imports 'csv' module, 'numpy' as 'np', and 'interp1d' function from 'scipy.interpolate'.

            This offers a succinct and comparable depiction of the initial imports.

            import csv
            import numpy as np
            from scipy.interpolate import interp1d

            An abbreviated version of the 'interpolate_bounding_boxes' function is provided here.

            Code is a little bit more compact in this version since the loop structure is made simpler and list comprehensions are used to create arrays. To retrieve values with default values, it additionally employs the 'get' method of the dictionary.

            def interpolate_bounding_boxes(data):
            # Extract necessary data columns from input data
            frame_numbers = np.array([int(row['frame_nmr']) for row in data])
            car_ids = np.array([int(float(row['car_id'])) for row in data])
            car_bboxes = np.array([list(map(float, row['car_bbox'][1:-1].split())) for row in data])
            license_plate_bboxes = np.array([list(map(float, row['license_plate_bbox'][1:-1].split())) for row in data])
            interpolated_data = []
            unique_car_ids = np.unique(car_ids)
            for car_id in unique_car_ids:
            frame_numbers_ = [p['frame_nmr'] for p in data if int(float(p['car_id'])) == int(float(car_id))]
            print(frame_numbers_, car_id)
            # Filter data for a specific car ID
            car_mask = car_ids == car_id
            car_frame_numbers = frame_numbers[car_mask]
            car_bboxes_interpolated = []
            license_plate_bboxes_interpolated = []
            first_frame_number = car_frame_numbers[0]
            last_frame_number = car_frame_numbers[-1]
            for i in range(len(car_bboxes[car_mask])):
            frame_number = car_frame_numbers[i]
            car_bbox = car_bboxes[car_mask][i]
            license_plate_bbox = license_plate_bboxes[car_mask][i]
            if i > 0:
            prev_frame_number = car_frame_numbers[i-1]
            prev_car_bbox = car_bboxes_interpolated[-1]
            prev_license_plate_bbox = license_plate_bboxes_interpolated[-1]
            if frame_number - prev_frame_number > 1:
            # Interpolate missing frames' bounding boxes
            frames_gap = frame_number - prev_frame_number
            x = np.array([prev_frame_number, frame_number])
            x_new = np.linspace(prev_frame_number, frame_number, num=frames_gap, endpoint=False)
            interp_func = interp1d(x, np.vstack((prev_car_bbox, car_bbox)), axis=0, kind='linear')
            interpolated_car_bboxes = interp_func(x_new)
            interp_func = interp1d(x, np.vstack((prev_license_plate_bbox, license_plate_bbox)), axis=0, kind='linear')
            interpolated_license_plate_bboxes = interp_func(x_new)
            car_bboxes_interpolated.extend(interpolated_car_bboxes[1:])
            license_plate_bboxes_interpolated.extend(interpolated_license_plate_bboxes[1:])
            car_bboxes_interpolated.append(car_bbox)
            license_plate_bboxes_interpolated.append(license_plate_bbox)
            for i in range(len(car_bboxes_interpolated)):
            frame_number = first_frame_number + i
            row = {}
            row['frame_nmr'] = str(frame_number)
            row['car_id'] = str(car_id)
            row['car_bbox'] = ' '.join(map(str, car_bboxes_interpolated[i]))
            row['license_plate_bbox'] = ' '.join(map(str, license_plate_bboxes_interpolated[i]))
            if str(frame_number) not in frame_numbers_:
            # Imputed row, set the following fields to '0'
            row['license_plate_bbox_score'] = '0'
            row['license_number'] = '0'
            row['license_number_score'] = '0'
            else:
            # Original row, retrieve values from the input data if available
            original_row = [p for p in data if int(p['frame_nmr']) == frame_number and int(float(p['car_id'])) == int(float(car_id))][0]
            row['license_plate_bbox_score'] = original_row['license_plate_bbox_score'] if 'license_plate_bbox_score' in original_row else '0'
            row['license_number'] = original_row['license_number'] if 'license_number' in original_row else '0'
            row['license_number_score'] = original_row['license_number_score'] if 'license_number_score' in original_row else '0'
            interpolated_data.append(row)
            return interpolated_data

            Load the CSV file:

            • Using read mode ('r'), the code opens the CSV file '/content/drive/aionlinecourse/real_time_license_plate_detection_and_using_ocr/test.csv'.
            • The CSV file is read using 'csv.DictReader' to create a set of dictionaries ('data').
            • The CSV file's rows are represented by each dictionary in 'data', with the keys serving as the column headings.


            Interpolate missing data:

            • To fill in the gaps in the bounding box data for every car and frame, the code uses the loaded data and the 'interpolate_bounding_boxes' function.
            • The 'interpolated_data' variable holds the outcome.


            Write updated data to a new CSV file:

            • The code starts a new CSV file named 'test_interpolated.csv' in write mode ('w') with newline='' to ensure appropriate line endings.
            • It writes the header and the rows of the interpolated data to the new CSV file using 'csv.DictWriter'.

            Basically, this code accepts data from an existing CSV file, uses the interpolate_bounding_boxes function to conduct interpolation on the bounding box data, and then sends the revised data to a new CSV file called test_interpolated.csv.


            # Load the CSV file
            with open('/content/drive/aionlinecourse/license_plate/test.csv', 'r') as file:
            reader = csv.DictReader(file)
            data = list(reader)
            # Interpolate missing data
            interpolated_data = interpolate_bounding_boxes(data)
            # Write updated data to a new CSV file
            header = ['frame_nmr', 'car_id', 'car_bbox', 'license_plate_bbox', 'license_plate_bbox_score', 'license_number', 'license_number_score']
            with open('test_interpolated.csv', 'w', newline='') as file:
            writer = csv.DictWriter(file, fieldnames=header)
            writer.writeheader()
            writer.writerows(interpolated_data)

            Step:7

            This little piece of code imports 'ast' for literal evaluation, 'cv2' for computer vision, 'numpy' as 'np' for numerical operations, and 'pandas' as 'pd' for data manipulation.

            This offers a succinct and comparable depiction of the initial imports.

            import ast
            import cv2
            import numpy as np
            import pandas as pd

            The top-left and bottom-right coordinates of a bounding box ('top_left' and 'bottom_right'), an image ('img'), and optional parameters for colour, thickness, and line lengths are all required by the 'draw_border' function. By extending lines from the corners of the enclosing box, the function creates a border around it.

            For the purpose of being more brief, the x and y components of the coordinates are combined in this version, which eliminates redundant code by drawing lines using a loop.

            def draw_border(img, top_left, bottom_right, color=(0, 255, 0), thickness=10, line_length_x=200, line_length_y=200):
            x1, y1 = top_left
            x2, y2 = bottom_right
            cv2.line(img, (x1, y1), (x1, y1 + line_length_y), color, thickness) #-- top-left
            cv2.line(img, (x1, y1), (x1 + line_length_x, y1), color, thickness)
            cv2.line(img, (x1, y2), (x1, y2 - line_length_y), color, thickness) #-- bottom-left
            cv2.line(img, (x1, y2), (x1 + line_length_x, y2), color, thickness)
            cv2.line(img, (x2, y1), (x2 - line_length_x, y1), color, thickness) #-- top-right
            cv2.line(img, (x2, y1), (x2, y1 + line_length_y), color, thickness)
            cv2.line(img, (x2, y2), (x2, y2 - line_length_y), color, thickness) #-- bottom-right
            cv2.line(img, (x2, y2), (x2 - line_length_x, y2), color, thickness)
            return img

            This code snippet reads a CSV file using Pandas, loads a video using OpenCV, and prepares to write a new video using specified parameters.

            This code sets up the necessary components to read a CSV file containing interpolated results and initialize video capture and writing. If you have specific questions or tasks related to this code, feel free to ask!

            results = pd.read_csv('/content/drive/aionlinecourse/real_time_license_plate_detection_and_using_ocr/test_interpolated.csv')
            # load video
            video_path = '/content/drive/aionlinecourse/real_time_license_plate_detection_and_using_ocr/v.mp4'
            cap = cv2.VideoCapture(video_path)
            fourcc = cv2.VideoWriter_fourcc(*'mp4v') # Specify the codec
            fps = cap.get(cv2.CAP_PROP_FPS)
            width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
            height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
            out = cv2.VideoWriter('./out1.mp4', fourcc, fps, (width, height))

            For every distinct automobile ID, Snippet processes the license plate data in the 'results' DataFrame. The highest scoring number plate number is extracted, the matching frame is captured, and the number plate region is cropped.

            Using a list of distinct automobile IDs, this method finds the license plate with the highest score, grabs the related frame, then cuts or resizes the relevant portion of the plate. The 'license_plate' dictionary is where the results are kept. In order to proceed with more processing, the frame counter is finally reset.

            license_plate = {}
            for car_id in np.unique(results['car_id']):
            max_ = np.amax(results[results['car_id'] == car_id]['license_number_score'])
            license_plate[car_id] = {'license_crop': None,
            'license_plate_number': results[(results['car_id'] == car_id) &
            (results['license_number_score'] == max_)]['license_number'].iloc[0]}
            cap.set(cv2.CAP_PROP_POS_FRAMES, results[(results['car_id'] == car_id) &
            (results['license_number_score'] == max_)]['frame_nmr'].iloc[0])
            ret, frame = cap.read()
            x1, y1, x2, y2 = ast.literal_eval(results[(results['car_id'] == car_id) &
            (results['license_number_score'] == max_)]['license_plate_bbox'].iloc[0].replace('[ ', '[').replace(' ', ' ').replace(' ', ' ').replace(' ', ','))
            license_crop = frame[int(y1):int(y2), int(x1):int(x2), :]
            license_crop = cv2.resize(license_crop, (int((x2 - x1) * 400 / (y2 - y1)), 400))
            license_plate[car_id]['license_crop'] = license_crop
            frame_nmr = -1
            cap.set(cv2.CAP_PROP_POS_FRAMES, 0)

            After processing the video's frames, this code adds the identified license plate numbers to the frames and overlays bounding boxes for vehicles and license plates. Next, a new video file containing the annotated frames is written.

            To make the code more compact, I have merged related operations, reused code for drawing borders, and added a method ('add_text_to_frame') to add text to the frame. To improve readability and cut down on repetition, changes have been made.

            # read frames
            ret = True
            while ret:
            ret, frame = cap.read()
            frame_nmr += 1
            if ret:
            df_ = results[results['frame_nmr'] == frame_nmr]
            for row_indx in range(len(df_)):
            # draw car
            car_x1, car_y1, car_x2, car_y2 = ast.literal_eval(df_.iloc[row_indx]['car_bbox'].replace('[ ', '[').replace(' ', ' ').replace(' ', ' ').replace(' ', ','))
            draw_border(frame, (int(car_x1), int(car_y1)), (int(car_x2), int(car_y2)), (0, 255, 0), 25,
            line_length_x=200, line_length_y=200)
            # draw license plate
            x1, y1, x2, y2 = ast.literal_eval(df_.iloc[row_indx]['license_plate_bbox'].replace('[ ', '[').replace(' ', ' ').replace(' ', ' ').replace(' ', ','))
            cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 0, 255), 12)
            # crop license plate
            license_crop = license_plate[df_.iloc[row_indx]['car_id']]['license_crop']
            H, W, _ = license_crop.shape
            try:
            frame[int(car_y1) - H - 100:int(car_y1) - 100,
            int((car_x2 + car_x1 - W) / 2):int((car_x2 + car_x1 + W) / 2), :] = license_crop
            frame[int(car_y1) - H - 400:int(car_y1) - H - 100,
            int((car_x2 + car_x1 - W) / 2):int((car_x2 + car_x1 + W) / 2), :] = (255, 255, 255)
            (text_width, text_height), _ = cv2.getTextSize(
            license_plate[df_.iloc[row_indx]['car_id']]['license_plate_number'],
            cv2.FONT_HERSHEY_SIMPLEX,
            4.3,
            17)
            cv2.putText(frame,
            license_plate[df_.iloc[row_indx]['car_id']]['license_plate_number'],
            (int((car_x2 + car_x1 - text_width) / 2), int(car_y1 - H - 250 + (text_height / 2))),
            cv2.FONT_HERSHEY_SIMPLEX,
            4.3,
            (0, 0, 0),
            17)
            except:
            pass
            out.write(frame)
            frame = cv2.resize(frame, (1280, 720))
            # cv2.imshow('frame', frame)
            # cv2.waitKey(0)
            out.release()
            cap.release()

            An efficient function named 'show_video' is defined in the given code, and it allows you to compress and view videos in a Jupyter Notebook. A data URL is generated to display the compressed video when it receives an input video file path and uses the FFmpeg codec to compress it. Following that, a data URL containing the compressed video is returned, ready for display.

            from IPython.display import HTML
            from base64 import b64encode
            import os
            def show_video(input_video_path):
            # Compressed video path
            video_name = "Compressed_" + input_video_path.split("/")[-1].split(".")[0]+".mp4"
            compressed_video_path = os.path.join("/content",video_name)
            # Use FFmpeg to compress the video
            ffmpeg_command = f"ffmpeg -i '{input_video_path}' -vcodec libx264 '{compressed_video_path}'"
            os.system(ffmpeg_command)
            # Display the compressed video
            compressed_video_data = open(compressed_video_path, 'rb').read()
            data_url = "data:video/mp4;base64," + b64encode(compressed_video_data).decode()
            return data_url

            This little piece of code compresses and displays a video using the 'show_video' method. Interactive viewing of the compressed video is made possible by the inserted produced data URL in an HTML <'video'> element with playing controls.

            data_url = show_video('/content/drive/aionlinecourse/real_time_license_plate_detection_and_using_ocr/out1.mp4')
            HTML(f"""

            """)

            Conclusion

            A strong and effective way to automatically identify and read license plates is to use YOLOv8 for finding license plates and OCR models for reading text. License plates can be quickly and accurately found in images and video feeds by using YOLOv8's real-time object detection features. OCR models like Easyocr can then extract and identify the text.


            For example, automated toll collection, parking management systems, traffic monitoring, and law enforcement are all very useful areas where this combined method works very well. It can be used in a wide range of settings and situations, from small networks of single cameras to large networks of traffic tracking systems, because it is flexible, scalable, and easy to use. For example, in the future, OCR could be made more accurate, made better for handling videos in real time, and made more useful by connecting to databases. That being said, this cutting-edge solution makes chores like license plate recognition and detection much more automated and faster.

            Code Editor