Python-OpenCV车牌识别系统
本系统虽然完成了整个系统,但对车牌图像有要求,首先本系统只能识别蓝底白字的车牌,其次车牌所在图像区域应在图像中下部分(因为第一步要进行裁剪,可以看一下展示的车牌),其次,我制作的模板较少,每个字符做了3-4个。
原始图像
第一次处理
并进行初次裁剪处理,因为车牌一般都在图片的中下部分。
# 统一尺寸宽、高为600*300
def first(path):
image = cv2.imread(path)
height_1 = image.shape[0] # 高
width_1 = image.shape[1] # 宽
sign = 0 # 是否进行了缩放处理
while((height_1 < 300) or (width_1 < 600)):
sign = 1
height_1 = int(height_1 * 1.2)
width_1 = int(width_1 *1.2)
if sign == 1:
image = cv2.resize(image,(width_1,height_1))
global height
height = 300 # 高
global width
width = 600 # 宽
# 裁剪
h = int((height_1 - height)*2/3)
w = int((width_1 - width)/2)
img = image[h:h + height,w:w + width]
cv2.imwrite('C:\\Users\\lenovo\\Desktop\\demo_1\\1.jpg',img)
# cv2.imshow('1',img)
# cv2.waitKey(0)
return img
处理后效果
识别效果
测试的车牌
这些车牌都能进行识别,这里就不一一演示了。
制作的车牌字符识别模板
车牌识别数据集下载-数据集
源码
import cv2
import numpy as np
import math
from scipy import ndimage
import os
import time
from tkinter import *
from PIL import Image, ImageTk
# 统一尺寸宽、高为600*300
def first(path):
image = cv2.imread(path)
height_1 = image.shape[0] # 高
width_1 = image.shape[1] # 宽
sign = 0
while((height_1 < 300) or (width_1 < 600)):
sign = 1
height_1 = int(height_1 * 1.2)
width_1 = int(width_1 *1.2)
if sign == 1:
image = cv2.resize(image,(width_1,height_1))
global height
height = 300
global width
width = 600
h = int((height_1 - height)*2/3)
w = int((width_1 - width)/2)
img = image[h:h + height,w:w + width]
cv2.imwrite('C:\\Users\\lenovo\\Desktop\\demo_1\\1.jpg',img)
# cv2.imshow('1',img)
# cv2.waitKey(0)
return img
# 图像预处理
def image_Preprocessing(img):
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.GaussianBlur(img, (5, 5), 1) # 高斯平滑
img_median = cv2.medianBlur(img, 3) # 中值滤波
img_gray = cv2.cvtColor(img_median, cv2.COLOR_BGR2GRAY)
# cv2.imwrite('C:\\Users\\lenovo\\Desktop\\demo_1\\2.jpg', img_gray)
binary = cv2.Canny(img_gray, 100, 200)
kerne = np.ones((5, 5), np.uint8)
img_1 = cv2.morphologyEx(binary, cv2.MORPH_CLbOSE, kerne, iterations=2)
img_2 = cv2.morphologyEx(img_1, cv2.MORPH_OPEN, kerne, iterations=1)
img_3 = cv2.morphologyEx(img_2, cv2.MORPH_CLOSE, kerne, iterations=1)
# cv2.imwrite('C:\\Users\\lenovo\\Desktop\\demo_1\\3.jpg', img_3)
# cv2.imshow('1', img_3)
# cv2.waitKey(0)
return img_3
# 基于颜色特征辅助定位车牌
def fun(img):
Blue = 138
Green = 63
Red = 23
THRESHOLD = 50
# 高斯平滑
img_aussian = cv2.GaussianBlur(img, (5, 5), 1)
# 中值滤波
img_median = cv2.medianBlur(img_aussian, 3)
img_B = cv2.split(img_median)[0]
img_G = cv2.split(img_median)[1]
img_R = cv2.split(img_median)[2]
for i in range(img_median.shape[:2][0]):
for j in range(img_median.shape[:2][1]):
if abs(img_B[i, j] - Blue) < THRESHOLD and \
abs(img_G[i, j] - Green) < THRESHOLD and \
abs(img_R[i, j] - Red) < THRESHOLD:
img_median[i][j][0] = 255
img_median[i][j][1] = 255
img_median[i][j][2] = 255
else:
img_median[i][j][0] = 0
img_median[i][j][1] = 0
img_median[i][j][2] = 0
kernel = np.ones((3, 3), np.uint8)
img_dilate = cv2.dilate(img_median, kernel, iterations=5) # 膨胀操作
img_erosion = cv2.erode(img_dilate, kernel, iterations=5) # 腐蚀操作
# cv2.imshow('2',img_erosion)
# cv2.waitKey(0)
return img_erosion
# 基于轮廓定位车牌
def license_Plate_Positioning(img,img_1):
img_1 = cv2.cvtColor(img_1, cv2.COLOR_BGR2GRAY)
contours, hierarchy = cv2.findContours(img_1, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cv_contours = []
for contour in contours:
area = cv2.contourArea(contour)
if area >= 90000 or area <= 1400:
cv_contours.append(contour)
else:
continue
cv2.fillPoly(img_1, cv_contours, (0))
contours_1, hierarchy = cv2.findContours(img_1, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cv_contours_null = []
for contour in contours_1:
x, y, w, h = cv2.boundingRect(contour)
if ((w < h) and (w < (h * 2) or (w > (h * 5)))):
cv_contours_null.append(contour)
cv2.fillPoly(img_1, cv_contours_null, (0))
contours_2, _ = cv2.findContours(img_1, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
img_s = []
index = 4
for cont in contours_2:
# 外接矩形
x, y, w, h = cv2.boundingRect(cont)
if((w * h < 7000) or (w < 10) or (h < 50)):
continue
img_l = img[y:y + h, x :x + w ]
img_s.append(img_l)
cv2.imwrite("C:\\Users\\lenovo\\Desktop\\demo_1\\" + f"{index}" + ".jpg", img_s[0])
return img_s[0]
#
# return img_l
# img_1 = cv2.cvtColor(img_1, cv2.COLOR_BGR2GRAY)
# contours, hierarchy = cv2.findContours(img_1, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# # 筛选面积小的
# cv_contours = []
# for contour in contours:
# area = cv2.contourArea(contour)
# if area >= 17000 or area <= 1400:
# cv_contours.append(contour)
# else:
# continue
# cv2.fillPoly(img_1, cv_contours, (0))
# # cv2.imshow('img1', img)
# # cv2.waitKey(0)
# contours_1, hierarchy = cv2.findContours(img_1, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# # 处理面积差别不大
# cv_contours_null = []
# for contour in contours_1:
# x, y, w, h = cv2.boundingRect(contour)
# if ((w < h) and (w < (h * 2) or (w > (h * 5)))):
# cv_contours_null.append(contour)
# cv2.fillPoly(img_1, cv_contours_null, (0))
# cv2.imshow('3', img)
# cv2.waitKey(0)
# contours_2, _ = cv2.findContours(img_1, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
#
# index = 4
# img_2 = None
# for cont in contours_2:
# # 外接矩形
# x, y, w, h = cv2.boundingRect(cont)
# if (w * h < 2000):
# continue
# img_2 = img[y - 5:y + h + 5, x - 5:x + w + 5]
# cv2.imwrite("C:\\Users\\lenovo\\Desktop\\demo_1\\" + f"{index}" + ".jpg", img_2)
# index += 1
# print('查找轮廓完毕')
### #return img_2
# 车牌矫正
def correction(img):
# 二值化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 边缘检测
edges = cv2.Canny(gray, 50, 150, 3) # apertureSize 卷积大小
# 霍夫变换
lines = cv2.HoughLines(edges, 1, np.pi / 180, 0)
for rho, theta in lines[0]:
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(x0 + 1000 * (-b))
y1 = int(y0 + 1000 * (a))
x2 = int(x0 - 1000 * (-b))
y2 = int(y0 - 1000 * (a))
if x1 == x2 or y1 == y2:
continue
t = float(y2 - y1) / (x2 - x1)
# 得到角度后
rotate_angle = math.degrees(math.atan(t))
if rotate_angle > 45:
rotate_angle = -90 + rotate_angle
elif rotate_angle < -45:
rotate_angle = 90 + rotate_angle
# 图像根据角度进行校正
rotate_img = ndimage.rotate(img, rotate_angle)
# 输出图像
cv2.imwrite('C:\\Users\\lenovo\\Desktop\\demo_1\\5.jpg', rotate_img)
return rotate_img
# 去除边框和铆钉
def remove(img):
# 改变车牌号大小
height = img.shape[0] # 高
width = img.shape[1] # 宽
while(width < 500):
height = int(height * 1.2) # 高
width = int(width * 1.2) # 宽
image = cv2.resize(img, (width, height))
# 高斯模糊
blurerd = cv2.GaussianBlur(image, (3, 3), 0, 0, cv2.BORDER_DEFAULT)
# 图像转灰度
gray = cv2.cvtColor(blurerd, cv2.COLOR_BGR2GRAY)
# 图像二值化
ret, img_thre = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # cv2.THRESH_BINARY_INV
# 展示
# cv2.imwrite("C:\\Users\\lenovo\\Desktop\\demo_1\\20.jpg", img_thre)
white = [] # 记录每一列的白色像素总和
black = [] # 记录每一列的黑色像素的总和
height = img_thre.shape[0] # 高
width = img_thre.shape[1] # 宽
# 计算每一行的黑白像素的总和
for i in range(height):
s = 0 # 这一行白色总数
t = 0 # 这一行黑色总数
for j in range(width):
if img_thre[i][j] == 255:
s = s + 1
if img_thre[i][j] == 0:
t = t + 1
white.append(s)
black.append(t)
# 去除上
# 从车牌中间开始,向上向下找第一个不为0且大于50
mid = int(height / 2)
while ((white[mid] != 0) & (white[mid] > 50) & (mid > 1)):
mid = mid - 1
start = mid
mid = int(height / 2)
while ((white[mid] != 0) & (white[mid] > 50) & (mid < height - 1)):
mid = mid + 1
end = mid + 1
img_cut = img_thre[start: end, 3: width - 3]
cv2.imwrite('C:\\Users\\lenovo\\Desktop\\demo_1\\6.jpg', img_cut)
return img_cut
# 字符分割辅助函数
def get_vvList(list_data):
# 取出list中像素存在的区间
vv_list=[]
v_list=[]
for index,i in enumerate(list_data):
if i > 10 :
v_list.append(index)
else:
if v_list: # 如果v_list里有数据则执行下面代码
vv_list.append(v_list)
#list的clear与[]有区别
v_list=[]
# 分开字符处理,如在第一个字符所以保险起见考虑前3个切割的字符,川字符还未作处理
index = -1
for i in range(3):
j = i + 1
if vv_list[j][0] - vv_list[i][-1] > 5:
continue
else: # 如果两个字符之间间距小于5,判断为同一字符
index = i
break
if index != -1:
list = [vv_list[index][0],vv_list[index + 1][-1]]
vv_list[index] = list
vv_list.pop(index + 1)
# 川字符
index = -1
for i in range(3):
j = i + 1
if vv_list[j][0] - vv_list[i][-1] > 5:
continue
else: # 如果两个字符之间间距小于5,判断为同一字符
index = i
break
if index != -1:
list = [vv_list[index][0], vv_list[index + 1][-1]]
vv_list[index] = list
vv_list.pop(index + 1)
return vv_list
# 字符分割
def cut(img):
# grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 将BGR图转为灰度图
# ret, binary = cv2.threshold(grayImage, 127, 255, cv2.THRESH_BINARY) # 将图片进行二值化(130,255)之间的点均变为255(背景)
# ret, img_thre = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
rows, cols = img.shape
ver_list = [0] * cols
for j in range(cols):
for i in range(rows):
if img.item(i, j) == 255:
ver_list[j] = ver_list[j] + 1
ver_arr = np.array(ver_list)
ver_arr[np.where(ver_arr < 1)] = 0
ver_list = ver_arr.tolist()
# 切割单一字符
vv_list = get_vvList(ver_list)
name = 7
x = 1
imgs = []
for i in vv_list:
if (i[-1] - i[0] > 12):
img_ver = img[:, i[0] - 1:i[-1] + 1] # i[-1]取列表最后一个元素
else:
continue
img_i = cv2.resize(img_ver, (25, 50))
imgs.append(img_i)
name += 1
x += 1
if len(imgs) == 9:
imgs.pop(0)
imgs.pop(2)
name = 7
for img_i in imgs:
cv2.imwrite('C:\\Users\\lenovo\\Desktop\\demo_1\\' + f'{name}' + '.jpg', img_i)
name += 1
return imgs
# 字符识别
#模版匹配
# 准备模板(template[0-9]为数字模板;)
template = ['0','1','2','3','4','5','6','7','8','9',
'A','B','C','D','E','F','G','H','J','K',
'L','M','N','P','Q','R','S','T','U','V',
'W','X','Y','Z',
'藏','川','鄂','甘','赣','贵','桂','黑','沪','吉',
'冀','津','晋','京','辽','鲁','蒙','闽','宁','青',
'琼','陕','苏','皖','湘','新','渝','豫','粤','云',
'浙'] # 一共有65个
# 读取一个文件夹下的所有图片,输入参数是文件名,返回模板文件地址列表
# 返回列表
def read_directory(directory_name):
referImg_list = []
for filename in os.listdir(directory_name):
referImg_list.append(directory_name + "\\" + filename)
return referImg_list # 模板照片地址列表
# 获得中文模板列表(只匹配车牌的第一个字符)
# 返回列表
def get_chinese_words_list():
chinese_words_list = []
for i in range(34,64):
#将模板存放在字典中
c_word = read_directory('C:\\Users\\lenovo\\Desktop\\template\\'+ template[i]) # read_directory调用上一个函数
chinese_words_list.append(c_word)
return chinese_words_list
chinese_words_list = get_chinese_words_list() # chinese_words_list=[[],[],[]]
# 获得英文模板列表(只匹配车牌的第二个字符)
# 返回列表
def get_eng_words_list():
eng_words_list = []
for i in range(10,34):
e_word = read_directory('C:\\Users\\lenovo\\Desktop\\template\\'+ template[i])
eng_words_list.append(e_word)
return eng_words_list
eng_words_list = get_eng_words_list() #eng_words_list = [[],[],[]]
# 获得英文和数字模板列表(匹配车牌后面的字符)
# 返回列表
def get_eng_num_words_list():
eng_num_words_list = []
for i in range(0,34):
word = read_directory('C:\\Users\\lenovo\\Desktop\\template\\'+ template[i])
eng_num_words_list.append(word)
return eng_num_words_list
eng_num_words_list = get_eng_num_words_list() # eng_num_words_list = [[],[],[]]
# 读取一个模板地址与图片进行匹配,返回得分
def template_score(template,image): # 参数:read_directory--模板照片 image--待检测照片
#将模板进行格式转换
template_img=cv2.imdecode(np.fromfile(template,dtype=np.uint8),1) # imdecode-内存缓存中读取图像数据并将其转换为图像格式 np.fromfile--一种读取具有已知数据类型的二进制数据以及解析简单格式文本文件的高效方法 1--按照int读取数据
template_img = cv2.cvtColor(template_img, cv2.COLOR_RGB2GRAY)
#模板图像阈值化处理——获得黑白图
ret, template_img = cv2.threshold(template_img, 0, 255, cv2.THRESH_OTSU)
image_ = image.copy()
#获得待检测图片的尺寸
height, width = image_.shape
# 将模板resize至与图像一样大小
template_img = cv2.resize(template_img, (width, height))
# 模板匹配,返回匹配得分
result = cv2.matchTemplate(image_, template_img, cv2.TM_CCOEFF)
return result[0][0]
# 对分割得到的字符逐一匹配
def template_matching(word_images): # 参数word_images--待检测图片列表
results = []
for index,word_image in enumerate(word_images): # enumerate() 函数用于将一个可遍历的数据对象,返回索引和对象
if index==0: # 匹配中文字符
best_score = []
for chinese_words in chinese_words_list:
score = []
for chinese_word in chinese_words: # chinese_word--模板照片地址
result = template_score(chinese_word,word_image) # 得分
score.append(result)
best_score.append(max(score)) # 最大得分
i = best_score.index(max(best_score)) # 最大得分的索引,就是这个模板的从34开始之后的索引
# print(template[34+i])
r = template[34+i]
results.append(r)
continue
if index==1: # 匹配车牌第二个英文字符
best_score = []
for eng_word_list in eng_words_list:
score = []
for eng_word in eng_word_list:
result = template_score(eng_word,word_image)
score.append(result)
best_score.append(max(score))
i = best_score.index(max(best_score))
# print(template[10+i])
r = template[10+i]
results.append(r)
continue
else: # 匹配英文和数字字符
best_score = []
for eng_num_word_list in eng_num_words_list:
score = []
for eng_num_word in eng_num_word_list:
result = template_score(eng_num_word,word_image)
score.append(result)
best_score.append(max(score))
i = best_score.index(max(best_score))
# print(template[i])
r = template[i]
results.append(r)
continue
return results
# 字符识别
def character_Recognition(imgs):
global result_last
word_images = imgs.copy() # 切割后的照片
# 调用函数获得结果
result = template_matching(word_images)
# "".join(result)函数将列表转换为拼接好的字符串,方便结果显示
result_last = "".join(result)
# 调用所有的函数
def run():
global img
# 要识别的图像
# 指定路径
path = 'C:\\Users\\lenovo\\Desktop\\demo\\'
# 返回指定路径的文件夹名称
dirs = os.listdir(path)
# 拼接字符串
pa = path + dirs[0]
img = first(pa)
# 预处理后的图像,二值图
img_1 = fun(img)
img_2 = license_Plate_Positioning(img,img_1)
img_3 = correction(img_2)
img_4 = remove(img_3)
imgs = cut(img_4)
character_Recognition(imgs)
# 窗口函数
def gui():
# 全局变量
global root
global img
# 关闭原来的窗口
root.destroy()
# 创建新窗口 设定大小并命名
window = Tk()
window.title('车牌识别系统')
window.geometry('1200x500')
var = StringVar() # 文字变量储存器
var_1 = StringVar()
var_2 = StringVar()
var_3 = StringVar()
img_jpg = ImageTk.PhotoImage(img)
var.set('要识别图像')
label_Img = Label(window, image=img_jpg)
label_Img.place(x=10, y=20, width=600, height=300)
text_Show = Label(window, textvariable=var, fg='black', font=("黑体", 12), width=10)
text_Show.place(x=75, y=330, width=350, height=20)
# 点击按钮式执行的命令
btn_Run_1 = Button(window, text='开始识别', fg='red', width=15, height=5)
btn_Run_1.place(x=200, y=360, width=100, height=70) # 按钮位置
img_1 = Image.open('C:\\Users\\lenovo\\Desktop\\demo_1\\4.jpg')
img_jpg_1 = ImageTk.PhotoImage(img_1)
label_Img = Label(window, image=img_jpg_1)
label_Img.place(x=650, y=20, width=500, height=100)
var_1.set('定位到的车牌')
text_Show_1 = Label(window, textvariable=var_1, fg='black', font=("黑体", 12), width=10)
text_Show_1.place(x=800, y=130, width=170, height=20)
var_2.set('识别结果:')
text_Show_2 = Label(window, textvariable=var_2, fg='black', font=("黑体", 20), width=10)
text_Show_2.place(x=700, y=200, width=170, height=40)
var_3.set(result_last)
text_Show_3 = Label(window, textvariable=var_3, fg='red', font=("黑体", 20), width=10)
text_Show_3.place(x=700, y=250, width=200, height=40)
# 运行整体窗口
window.mainloop()
# 主函数
if __name__ == '__main__':
# 开始识别
run()
# 程序停一秒
time.sleep(1)
# 新建窗口
root = Tk()
root.title('车牌识别系统')
root.geometry('1200x500')
# 文字变量储存器
var = StringVar()
var_1 = StringVar()
var_2 = StringVar()
# 只能显示gif格式,需要借助其他库
img = Image.open('C:\\Users\\lenovo\\Desktop\\demo_1\\1.jpg')
img_jpg = ImageTk.PhotoImage(img)
# 显示要识别的图像,自定义位置
label_Img = Label(root, image=img_jpg)
label_Img.place(x=10, y=20, width=600, height=300)
# 显示"要识别的图像"字符串
var.set('要识别图像')
text_Show = Label(root, textvariable=var, fg='black', font=("黑体", 12), width=10)
text_Show.place(x=75, y=330, width=350, height=20)
# 点击按钮式执行的命令,关闭这个窗口,新建一个窗口
btn_Run_1 = Button(root, text='开始识别', fg='red', width=15, height=5, command=gui)
btn_Run_1.place(x=200, y=360, width=100, height=70) # 按钮位置
# # 运行整体窗口
root.mainloop()
要运行本系统,要先创建相关的文件夹。