# Python Project – Automatic License Number Plate Recognition using Deep Learning

Deep Learning Project – Automatic License Number Plate Detection and Recognition

This project aims to recognize license number plates. In order to detect license number plates, we will use OpenCV to identify number plates and python pytesseract to extract characters and digits from the number plates. Keeping you updated with latest technology trends, Join TechVidvan on Telegram

## Automatic License Number Plate Recognition

OpenCV is an open-source machine learning library and provides a common infrastructure for computer vision. Whereas Pytesseract is a Tesseract-OCR Engine to read image types and extract the information present in the image.

### Install OpenCV and Pytesseract pip3 python package:

pip3 install opencv-python
pip3 install pytesseract


In this python project, to identify the number plate in the input image, we will use following features of openCV:

• Gaussian Blur: Here we use a Gaussian kernel to smoothen the image. This technique is highly effective to remove Gaussian noise. OpenCV provides a cv2.GaussianBlur() function for this task.
• Sobel: Here we calculate the derivatives from the image. This feature is important for many computer vision tasks. Using derivatives we calculate the gradients, and a high change in gradient indicates a major change in the image. OpenCV provides a cv2.Sobel() function to calculate Sobel operators.
• Morphological Transformation: These are the operations based on image shapes and are performed on binary images. The basic morphological operations are Erosion, Dilation, Opening, Closing. The different functions provided in OpenCV are:
• cv2.erode()
• cv2.dilate()
• cv2.morphologyEx()
• Contours: Contours are the curves containing all the continuous points of same intensity. These are very useful tools for object recognition. OpenCV provides cv2.findContours() functions for this feature.

Now, let’s dive into the number plate recognition code. Follow the steps below:

1. Imports:

For this project we need numpy and pillow python libraries with openCV and pytesseract

import numpy as np
import cv2
from PIL import Image
import pytesseract as tess


2. Now we will define three functions, to find the unnecessary contours that openCV may identify but it does not have probability of being a number plate.

2.1. The first function to check the area range and width-height ratio:

def ratioCheck(area, width, height):
ratio = float(width) / float(height)
if ratio < 1:
ratio = 1 / ratio
if (area < 1063.62 or area > 73862.5) or (ratio < 3 or ratio > 6):
return False
return True


2.2. The second function to check average of image matrix:

def isMaxWhite(plate):
avg = np.mean(plate)
if(avg>=115):
return True
else:
return False


2.3. The third function to check the rotation of contours:

def ratio_and_rotation(rect):
(x, y), (width, height), rect_angle = rect

if(width>height):
angle = -rect_angle
else:
angle = 90 + rect_angle

if angle>15:
return False

if height == 0 or width == 0:
return False

area = height*width
if not ratioCheck(area,width,height):
return False
else:
return True


3. Now we will write a function to clean the identified number plate for preprocessing before feeding to pytesseract:

def clean2_plate(plate):
gray_img = cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY)

_, thresh = cv2.threshold(gray_img, 110, 255, cv2.THRESH_BINARY)
if cv2.waitKey(0) & 0xff == ord('q'):
pass
num_contours,hierarchy = cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

if num_contours:
contour_area = [cv2.contourArea(c) for c in num_contours]
max_cntr_index = np.argmax(contour_area)

max_cnt = num_contours[max_cntr_index]
max_cntArea = contour_area[max_cntr_index]
x,y,w,h = cv2.boundingRect(max_cnt)

if not ratioCheck(max_cntArea,w,h):
return plate,None

final_img = thresh[y:y+h, x:x+w]
return final_img,[x,y,w,h]

else:
return plate, None


4. In this step, we will take an image input. We will perform Gaussian Blur, Sobel and morphological operations. After we find contours in the image and loop through each contour to identify the number plate. We will then clean the image contour and feed it to pytesseract to recognize the number and characters.

img = cv2.imread("testData/sample15.jpg")
print("Number  input image...",)
cv2.imshow("input",img)

if cv2.waitKey(0) & 0xff == ord('q'):
pass
img2 = cv2.GaussianBlur(img, (3,3), 0)
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

img2 = cv2.Sobel(img2,cv2.CV_8U,1,0,ksize=3)
_,img2 = cv2.threshold(img2,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

element = cv2.getStructuringElement(shape=cv2.MORPH_RECT, ksize=(17, 3))
morph_img_threshold = img2.copy()
cv2.morphologyEx(src=img2, op=cv2.MORPH_CLOSE, kernel=element, dst=morph_img_threshold)
num_contours, hierarchy= cv2.findContours(morph_img_threshold,mode=cv2.RETR_EXTERNAL,method=cv2.CHAIN_APPROX_NONE)
cv2.drawContours(img2, num_contours, -1, (0,255,0), 1)

for i,cnt in enumerate(num_contours):

min_rect = cv2.minAreaRect(cnt)

if ratio_and_rotation(min_rect):

x,y,w,h = cv2.boundingRect(cnt)
plate_img = img[y:y+h,x:x+w]
print("Number  identified number plate...")
cv2.imshow("num plate image",plate_img)
if cv2.waitKey(0) & 0xff == ord('q'):
pass

if(isMaxWhite(plate_img)):
clean_plate, rect = clean2_plate(plate_img)
if rect:
fg=0
x1,y1,w1,h1 = rect
x,y,w,h = x+x1,y+y1,w1,h1
# cv2.imwrite("clena.png",clean_plate)
plate_im = Image.fromarray(clean_plate)
text = tess.image_to_string(plate_im, lang='eng')
print("Number  Detected Plate Text : ",text)


### Code for Project GUI

Make a new file gui.py and paste the below code:

import tkinter as tk
from tkinter import filedialog
from tkinter import *
from PIL import ImageTk, Image
from tkinter import PhotoImage
import numpy as np
import cv2
import pytesseract as tess
def clean2_plate(plate):
gray_img = cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY)

_, thresh = cv2.threshold(gray_img, 110, 255, cv2.THRESH_BINARY)
num_contours,hierarchy = cv2.findContours(thresh.copy(),cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

if num_contours:
contour_area = [cv2.contourArea(c) for c in num_contours]
max_cntr_index = np.argmax(contour_area)

max_cnt = num_contours[max_cntr_index]
max_cntArea = contour_area[max_cntr_index]
x,y,w,h = cv2.boundingRect(max_cnt)

if not ratioCheck(max_cntArea,w,h):
return plate,None

final_img = thresh[y:y+h, x:x+w]
return final_img,[x,y,w,h]

else:
return plate,None

def ratioCheck(area, width, height):
ratio = float(width) / float(height)
if ratio < 1:
ratio = 1 / ratio
if (area < 1063.62 or area > 73862.5) or (ratio < 3 or ratio > 6):
return False
return True

def isMaxWhite(plate):
avg = np.mean(plate)
if(avg>=115):
return True
else:
return False

def ratio_and_rotation(rect):
(x, y), (width, height), rect_angle = rect

if(width>height):
angle = -rect_angle
else:
angle = 90 + rect_angle

if angle>15:
return False

if height == 0 or width == 0:
return False

area = height*width
if not ratioCheck(area,width,height):
return False
else:
return True

top=tk.Tk()
top.geometry('900x700')
top.title('Number Plate Recognition')
top.iconphoto(True, PhotoImage(file="/home/shivam/Dataflair/Keras Projects_CIFAR/GUI/logo.png"))
img = ImageTk.PhotoImage(Image.open("logo.png"))
top.configure(background='#CDCDCD')
label=Label(top,background='#CDCDCD', font=('arial',35,'bold'))
# label.grid(row=0,column=1)
sign_image = Label(top,bd=10)
plate_image=Label(top,bd=10)
def classify(file_path):
res_text=
res_img=
img2 = cv2.GaussianBlur(img, (3,3), 0)
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

img2 = cv2.Sobel(img2,cv2.CV_8U,1,0,ksize=3)
_,img2 = cv2.threshold(img2,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

element = cv2.getStructuringElement(shape=cv2.MORPH_RECT, ksize=(17, 3))
morph_img_threshold = img2.copy()
cv2.morphologyEx(src=img2, op=cv2.MORPH_CLOSE, kernel=element, dst=morph_img_threshold)
num_contours, hierarchy= cv2.findContours(morph_img_threshold,mode=cv2.RETR_EXTERNAL,method=cv2.CHAIN_APPROX_NONE)
cv2.drawContours(img2, num_contours, -1, (0,255,0), 1)

for i,cnt in enumerate(num_contours):

min_rect = cv2.minAreaRect(cnt)

if ratio_and_rotation(min_rect):

x,y,w,h = cv2.boundingRect(cnt)
plate_img = img[y:y+h,x:x+w]
print("Number  identified number plate...")
res_img=plate_img
cv2.imwrite("result.png",plate_img)
if(isMaxWhite(plate_img)):
clean_plate, rect = clean2_plate(plate_img)

if rect:
fg=0
x1,y1,w1,h1 = rect
x,y,w,h = x+x1,y+y1,w1,h1
plate_im = Image.fromarray(clean_plate)
text = tess.image_to_string(plate_im, lang='eng')
res_text=text
if text:
break
label.configure(foreground='#011638', text=res_text)

plate_image.configure(image=im)
plate_image.image=im
plate_image.pack()
plate_image.place(x=560,y=320)
def show_classify_button(file_path):
classify_b.configure(background='#364156', foreground='white',font=('arial',15,'bold'))
classify_b.place(x=490,y=550)
try:
sign_image.configure(image=im)
sign_image.image=im
label.configure(text='')
show_classify_button(file_path)
except:
pass

sign_image.pack()
sign_image.place(x=70,y=200)

label.pack()
label.place(x=500,y=220)
top.mainloop()


## Summary

In this article, we have developed a deep learning project to recognize license number plate. We discussed some important features of openCV like Gaussian blur, Sobel operators, Morphological transformations. The application detects number plate text from an image. We have identified and cleaned the number plate using openCV. To identify the number plate digits and characters we used pytesseract.

### 2 Responses

1. raja says:

number plate detection project you provided in this site. is this project detects number plate of any input given by user?

2. g says:

can we have the images of the code? thx