Let’s start by opening our image, we’ll take this beautiful RWB 911 as the main example here.
We will need to open it as PIL image first, and then we can convert it to the OpenCV format:
from PIL import Image as imageMain
from PIL.Image import Image
import cv2
import numpyimagePath = '../sample-images/1.jpg'
imagePil = imageMain.open(imagePath)
imageCv = cv2.cvtColor(numpy.array(imagePil), cv2.COLOR_RGB2BGR)
cv2.imshow('Original Image', imageCv)
Now we’ll need to apply some pre-processing in OpenCV to make contour detection work. Namely we convert the image to gray scale, apply bilateral filter with cv2.bilateralFilter and Gausian blur with cv2.GaussianBlur:
gray = cv2.cvtColor(imageCv, cv2.COLOR_BGR2GRAY)
cv2.imshow('Gray Scaled', gray)bilateral = cv2.bilateralFilter(gray, 11, 17, 17)
cv2.imshow('After Bilateral Filter', bilateral)
blur = cv2.GaussianBlur(bilateral, (5, 5), 0)
cv2.imshow('After Gausian Blur', blur)
With this pre-processing completed, we can do a canny edge detection using cv2.Canny, find all contours with cv2.findContours, and examine 30 largest ones:
edged = cv2.Canny(blur, 170, 200)
cv2.imshow('After Canny Edge', edged)contours, hierarchy = cv2.findContours(edged, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
contours = sorted(contours, key = cv2.contourArea, reverse = True)[:30]
tempContours1 = cv2.drawContours(imageCv.copy(), contours, -1, (255, 0, 0), 2)
cv2.imshow('Detected Contours', tempContours1)
Now we can find only contours that are shaped like rectangles. To do that we go through each contour, calculate the perimeter with cv2.arcLength, and approximate the contour using cv2.approxPolyDP, with approximation accuracy (maximum distance between the original contour and its approximation) taken as 2% of perimeter. If the resulting approximated figure has exactly 4 points (i.e. resembles a rectangle) it might be our license plate. And since we start from the largest contour — license plate should be the first rectangle contour we found:
rectangleContours = []
for contour in contours:
perimeter = cv2.arcLength(contour, True)
approximationAccuracy = 0.02 * perimeter
approximation = cv2.approxPolyDP(contour, approximationAccuracy, True)
if len(approximation) == 4:
rectangleContours.append(contour)plateContour = rectangleContours[0]
tempContours2 = cv2.drawContours(imageCv.copy(), [plateContour], -1, (255, 0, 0), 2)
cv2.imshow('Detected Plate Contour', tempContours2)
Let’s go though 2 more example images to make sure this approach is feasible. It seems to work fine for our purposes, even switching from Japanese to European license plate didn’t cause any issues.:
Credit: BecomingHuman By: Leo Ertuna