5 Best Ways to Detect License Plates Using OpenCV Python

πŸ’‘ Problem Formulation: In this article, we address the challenge of detecting and recognizing vehicle license plates from images using Python and OpenCV. This task finds applications in traffic monitoring, automatic toll collection, and security systems. We aim to transform an input – a photograph of a vehicle – into a desired output, which is the vehicle’s license plate as a text string. To achieve this, we’ll explore various methods of isolating the plate region and processing it for number recognition.

Method 1: Basic Contour Detection

Contour detection is fundamental in computer vision for recognizing shapes and objects. Using OpenCV’s contour detection functions, we search for rectangle shapes within the image that closely approximate the dimensions of a standard license plate. Such an approach heavily relies on pre-processing steps like grayscale conversion and edge detection.

Here’s an example:

import cv2

# Read the image file
image = cv2.imread('car.jpg')

# Convert to grayscale
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# Apply Canny Edge Detection
edges = cv2.Canny(gray_image, 150, 250)

# Find contours
contours, _ = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# Assume largest rectangle contour is the plate
contours = sorted(contours, key=cv2.contourArea, reverse=True)[:10]
plate = None
for cnt in contours:
    # Approximate the contour to a polygon
    approx = cv2.approxPolyDP(cnt, 0.01 * cv2.arcLength(cnt, True), True)
    # Plates are generally rectangle
    if len(approx) == 4:
        plate = approx
        break

# Draw the contour on the original image
cv2.drawContours(image, [plate], -1, (0,255,0), 3)

cv2.imshow('License Plate', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

The output of this code snippet would be the original image with the detected license plate highlighted by a green contour.

In this example, we first convert the input image to grayscale and then apply Canny edge detection. We search for contours and assume the license plate to be one of the largest rectangular contours. We then draw this contour on the image, helping us visually confirm the detection.

Method 2: Morphological Operations for Enhancing Features

Morphological transformations are particular operations applied to binary images which can help in noise reduction and feature enhancement. This method is effective when we need to focus on the structural shape of the license plate and its characters without environmental noise.

Here’s an example:

import cv2
import numpy as np

# Read the image file
image = cv2.imread('car.jpg')

# Convert to grayscale and apply Gaussian blur
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (7,7), 0)

# Apply morphological operations
thresh = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                               cv2.THRESH_BINARY_INV,11,2)
rect_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
dilation = cv2.dilate(thresh, rect_kernel, iterations=1)

# Find contours and filter for license plate
contours, _ = cv2.findContours(dilation, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
license_plate = None
for cnt in contours:
    x, y, w, h = cv2.boundingRect(cnt)
    if w/h > 2:
        license_plate = image[y:y+h, x:x+w]
        break

cv2.imshow('Detected License Plate', license_plate)
cv2.waitKey(0)
cv2.destroyAllWindows()

The output is an image segment of just the detected license plate, with most irrelevant details in the background removed.

This code demonstrates the application of adaptive thresholding and dilation to the grayscale image. These steps highlight the license plate area making it more prominent for contour detection. The final step isolates the plate’s area and displays it.

Method 3: Using Haar Cascades for Detection

Haar cascades are a machine learning-based approach to object detection. With a pre-trained license plate Haar cascade classifier, we can effectively pinpoint the plate’s location in the image. This method provides a quick and simple means for license plate detection with minimal pre-processing required.

Here’s an example:

import cv2

# Load the pre-trained Haar cascade for license plates
license_cascade = cv2.CascadeClassifier('haarcascade_russian_plate_number.xml')

# Read the image file
image = cv2.imread('car.jpg')

# Convert to grayscale
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# Detect license plate
plates = license_cascade.detectMultiScale(gray_image, 1.1, 10)

# Draw rectangle around the detected plates
for (x,y,w,h) in plates:
    cv2.rectangle(image, (x,y), (x+w, y+h), (0,255,0), 3)

cv2.imshow('Detected License Plates', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

The output is the image with the detected license plate(s) enclosed in green rectangles.

In this example, the image is first converted to grayscale, then the pre-trained Haar cascade classifier is utilized to detect the license plate. Detected plates are then highlighted via green rectangles. This method is generally efficient, but the accuracy depends on the quality of the classifier.

Method 4: Deep Learning with YOLO (You Only Look Once)

Deep learning models like YOLO can achieve high accuracy in object detection tasks. They work by dividing the image into a grid and predicting bounding boxes and probabilities for each grid cell. YOLO is known for its speed and accuracy, making it suitable for real-time applications.

Here’s an example:

import cv2
import numpy as np

# Load the pre-trained YOLO model
net = cv2.dnn.readNet('yolov3.weights', 'yolov3.cfg')
layer_names = net.getLayerNames()
output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()]

# Load the image
image = cv2.imread('car.jpg')
height, width, _ = image.shape

# Convert image to blob
blob = cv2.dnn.blobFromImage(image, 0.00392, (416, 416), (0,0,0), True, crop=False)

# Detect objects
net.setInput(blob)
outs = net.forward(output_layers)

# Show information on the screen
class_ids = []
confidences = []
boxes = []
for out in outs:
    for detection in out:
        scores = detection[5:]
        class_id = np.argmax(scores)
        confidence = scores[class_id]
        if confidence > 0.5:
            # Object detected
            center_x = int(detection[0] * width)
            center_y = int(detection[1] * height)
            w = int(detection[2] * width)
            h = int(detection[3] * height)

            # Rectangle coordinates
            x = int(center_x - w / 2)
            y = int(center_y - h / 2)

            boxes.append([x, y, w, h])
            confidences.append(float(confidence))
            class_ids.append(class_id)

# Apply non-max suppression to remove overlapping boxes
indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)

# Draw bounding box
for i in range(len(boxes)):
    if i in indexes:
        x, y, w, h = boxes[i]
        cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)

cv2.imshow('Image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

The output would be the original image with detected license plates bounded by rectangles.

In this method, we leverage the YOLO model pre-trained on a dataset that includes license plates. After converting the image into a suitable format (blob), predictions are made, and bounding boxes are determined for the license plates. The model might require substantial computational resources, but it’s one of the most robust detection methods.

Bonus One-Liner Method 5: Simple Image Processing

Sometimes a one-liner can be surprisingly effective. Simple image processing techniques, like thresholding combined with contour detection, can be used to extract potential license plate regions through a single command. This method is a very quick and dirty approach and may not work consistently across diverse datasets but can work well under controlled conditions.

Here’s an example:

import cv2

# Read the image file
image = cv2.imread('car.jpg')

# One-liner to detect contours
contours, _ = cv2.findContours(cv2.Canny(cv2.cvtColor(image, cv2.COLOR_BGR2GRAY), 100, 200), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

cv2.drawContours(image, contours, -1, (0,255,0), 3)
cv2.imshow('Contoured Image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

The output is the original image overlaid with green contours of detected shapes, which may include the license plate.

This approach encapsulates image conversion to grayscale, Canny edge detection, and contour extraction in a succint manner. It’s an extremely concise way to visualize potential plate regions but lacks the sophistication of other methods and may lead to many false positives.

Summary/Discussion

  • Method 1: Basic Contour Detection. Simple and requires minimal processing. However, it’s not very robust to variations in plate sizes or orientations.
  • Method 2: Morphological Operations. Enhances the structure of the plate for better detection. Can be sensitive to parameter tuning and environmental conditions.
  • Method 3: Haar Cascades. Quick and reliable if the quality of the pre-trained classifier is high. Limited by the quality and generalizability of the training data.
  • Method 4: Deep Learning with YOLO. Highly accurate and suitable for real-time applications. Requires computational resources and pre-processing of input data.
  • Bonus Method 5: Simple Image Processing. Extremely swift but can be unreliable due to the risk of false positives and the oversimplification of the detection process.