2025年3月30日 星期日 甲辰(龙)年 月廿九 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Python

简洁易懂的车牌号识别Python实现“超详解”(含代码)

时间:12-15来源:作者:点击数:31
城东书院 www.cdsy.xyz

1、整体思路

首先附上本次识别的图片:(图片是我在百度上找的)

基于OpenCV车牌号识别总体分为四个步骤

1)提取车牌位置,将车牌从图中分割出来;

2)车牌字符的分割;

3)通过模版匹配识别字符;

4)将结果绘制在图片上显示出来。

与深度学习相比,传统图像处理的识别有好处又有坏处

好处:不需要大量的数据集训练模型,通过形态学、边缘检测等操作提取特征

坏处:基于传统图像处理的图像识别代码的泛化性较低,当图像的角度,光照不同时,识别效果有时会不尽人意。

2、代码详解

为了方便观察每一步图片的变化,本次代码在Jupyter Notebook上编写,全部代码以上传(可直接运行)。

本次项目中会多次使用到图片显示和图片去噪灰度处理,所以首先定义了显示函数高斯滤波灰度处理函数,方便后面的调用:

  • # 导入所需模块
  • import cv2
  • from matplotlib import pyplot as plt
  • import os
  • import numpy as np
  • # plt显示彩色图片
  • def plt_show0(img):
  • #cv2与plt的图像通道不同:cv2为[b,g,r];plt为[r, g, b]
  • b,g,r = cv2.split(img)
  • img = cv2.merge([r, g, b])
  • plt.imshow(img)
  • plt.show()
  • # plt显示灰度图片
  • def plt_show(img):
  • plt.imshow(img,cmap='gray')
  • plt.show()
  • # 图像去噪灰度处理
  • def gray_guss(image):
  • image = cv2.GaussianBlur(image, (3, 3), 0)
  • gray_image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
  • return gray_image

2.1提取车牌位置

对图片进行阈值化处理、边缘检测及形态学操作,根据得到的轮廓特征识别车牌的具体位置,将车牌分割出来。直接上代码及代码详解:

  • # 读取待检测图片
  • origin_image = cv2.imread('./image/car.jpg')
  • # 复制一张图片,在复制图上进行图像操作,保留原图
  • image = origin_image.copy()
  • # 图像去噪灰度处理
  • gray_image = gray_guss(image)
  • # x方向上的边缘检测(增强边缘信息)
  • Sobel_x = cv2.Sobel(gray_image, cv2.CV_16S, 1, 0)
  • absX = cv2.convertScaleAbs(Sobel_x)
  • image = absX
  • # 图像阈值化操作——获得二值化图
  • ret, image = cv2.threshold(image, 0, 255, cv2.THRESH_OTSU)
  • # 显示灰度图像
  • plt_show(image)
  • # 形态学(从图像中提取对表达和描绘区域形状有意义的图像分量)——闭操作
  • kernelX = cv2.getStructuringElement(cv2.MORPH_RECT, (30, 10))
  • image = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernelX,iterations = 1)
  • # 显示灰度图像
  • plt_show(image)

二值化图以及闭操作(闭合细小的连接,抑制暗细节)的结果如图所示:

  • # 腐蚀(erode)和膨胀(dilate)
  • kernelX = cv2.getStructuringElement(cv2.MORPH_RECT, (50, 1))
  • kernelY = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 20))
  • #x方向进行闭操作(抑制暗细节)
  • image = cv2.dilate(image, kernelX)
  • image = cv2.erode(image, kernelX)
  • #y方向的开操作
  • image = cv2.erode(image, kernelY)
  • image = cv2.dilate(image, kernelY)
  • # 中值滤波(去噪)
  • image = cv2.medianBlur(image, 21)
  • # 显示灰度图像
  • plt_show(image)
  • # 获得轮廓
  • contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  • for item in contours:
  • rect = cv2.boundingRect(item)
  • x = rect[0]
  • y = rect[1]
  • weight = rect[2]
  • height = rect[3]
  • # 根据轮廓的形状特点,确定车牌的轮廓位置并截取图像
  • if (weight > (height * 3.5)) and (weight < (height * 4)):
  • image = origin_image[y:y + height, x:x + weight]
  • plt_show0(image)

2.2车牌字符的分割

  • #车牌字符分割
  • # 图像去噪灰度处理
  • gray_image = gray_guss(image)
  • # 图像阈值化操作——获得二值化图
  • ret, image = cv2.threshold(gray_image, 0, 255, cv2.THRESH_OTSU)
  • plt_show(image)
  • #膨胀操作,使“苏”字膨胀为一个近似的整体,为分割做准备
  • kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
  • image = cv2.dilate(image, kernel)
  • plt_show(image)
  • # 查找轮廓
  • contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  • words = []
  • word_images = []
  • #对所有轮廓逐一操作
  • for item in contours:
  • word = []
  • rect = cv2.boundingRect(item)
  • x = rect[0]
  • y = rect[1]
  • weight = rect[2]
  • height = rect[3]
  • word.append(x)
  • word.append(y)
  • word.append(weight)
  • word.append(height)
  • words.append(word)
  • # 排序,车牌号有顺序。words是一个嵌套列表
  • words = sorted(words,key=lambda s:s[0],reverse=False)
  • i = 0
  • #word中存放轮廓的起始点和宽高
  • for word in words:
  • # 筛选字符的轮廓
  • if (word[3] > (word[2] * 1.5)) and (word[3] < (word[2] * 3.5)) and (word[2] > 25):
  • i = i+1
  • splite_image = image[word[1]:word[1] + word[3], word[0]:word[0] + word[2]]
  • word_images.append(splite_image)
  • print(i)
  • print(words)
  • for i,j in enumerate(word_images):
  • plt.subplot(1,7,i+1)
  • plt.imshow(word_images[i],cmap='gray')
  • plt.show()

2.3模板匹配识别字符

模板匹配是一个机械性的流程,所以把机械性的操作设定为函数。

  • #模版匹配
  • # 准备模板(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',
  • '藏','川','鄂','甘','赣','贵','桂','黑','沪','吉','冀','津','晋','京','辽','鲁','蒙','闽','宁',
  • '青','琼','陕','苏','皖','湘','新','渝','豫','粤','云','浙']
  • # 读取一个文件夹下的所有图片,输入参数是文件名,返回模板文件地址列表
  • 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('./refer1/'+ template[i])
  • chinese_words_list.append(c_word)
  • return chinese_words_list
  • chinese_words_list = get_chinese_words_list()
  • # 获得英文模板列表(只匹配车牌的第二个字符)
  • def get_eng_words_list():
  • eng_words_list = []
  • for i in range(10,34):
  • e_word = read_directory('./refer1/'+ template[i])
  • eng_words_list.append(e_word)
  • return eng_words_list
  • eng_words_list = get_eng_words_list()
  • # 获得英文和数字模板列表(匹配车牌后面的字符)
  • def get_eng_num_words_list():
  • eng_num_words_list = []
  • for i in range(0,34):
  • word = read_directory('./refer1/'+ template[i])
  • eng_num_words_list.append(word)
  • return eng_num_words_list
  • eng_num_words_list = get_eng_num_words_list()
  • # 读取一个模板地址与图片进行匹配,返回得分
  • def template_score(template,image):
  • #将模板进行格式转换
  • template_img=cv2.imdecode(np.fromfile(template,dtype=np.uint8),1)
  • template_img = cv2.cvtColor(template_img, cv2.COLOR_RGB2GRAY)
  • #模板图像阈值化处理——获得黑白图
  • ret, template_img = cv2.threshold(template_img, 0, 255, cv2.THRESH_OTSU)
  • # height, width = template_img.shape
  • # image_ = image.copy()
  • # image_ = cv2.resize(image_, (width, height))
  • 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):
  • results = []
  • for index,word_image in enumerate(word_images):
  • if index==0:
  • best_score = []
  • for chinese_words in chinese_words_list:
  • score = []
  • for chinese_word in chinese_words:
  • result = template_score(chinese_word,word_image)
  • score.append(result)
  • best_score.append(max(score))
  • i = best_score.index(max(best_score))
  • # 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
  • word_images_ = word_images.copy()
  • # 调用函数获得结果
  • result = template_matching(word_images_)
  • print(result)
  • # "".join(result)函数将列表转换为拼接好的字符串,方便结果显示
  • print( "".join(result))
  • Output:
  • ['苏', 'E', '0', '5', 'E', 'V', '8']
  • 苏E05EV8

最后,利用PIL库将结果绘制在原图上,获得的最终输出图片如下:

  • from PIL import ImageFont, ImageDraw, Image
  • height,weight = origin_image.shape[0:2]
  • print(height)
  • print(weight)
  • image_1 = origin_image.copy()
  • cv2.rectangle(image_1, (int(0.2*weight), int(0.75*height)), (int(weight*0.9), int(height*0.95)), (0, 255, 0), 5)
  • #设置需要显示的字体
  • fontpath = "font/simsun.ttc"
  • font = ImageFont.truetype(fontpath,64)
  • img_pil = Image.fromarray(image_1)
  • draw = ImageDraw.Draw(img_pil)
  • #绘制文字信息
  • draw.text((int(0.2*weight)+25, int(0.75*height)), "".join(result), font = font, fill = (255, 255, 0))
  • bk_img = np.array(img_pil)
  • print(result)
  • print( "".join(result))
  • plt_show0(bk_img)

大功告成!!!!!

3、总结

(一) 、OpenCV的车牌号码识别一共分为四步走:

1--提取车牌位置,将车牌从图中分割出来;

2--车牌字符的分割;

3--通过模版匹配识别字符;

4--将结果绘制在图片上显示出来。

(二)、图像处理的识别泛化性较低,对图片的角度光照有要求,所以要理解图像处理每一步的作用,根据自己图像的特点调整参数,更改操作顺序等等,以达到最好的效果。

(三)、车牌号识别的模板连接如下,需要的可以下载,有了模板就可以识别自己的图片了

链接:https://pan.baidu.com/s/1QBjy7c0klv_PBUwJjA8ynA

提取码:v53d

城东书院 www.cdsy.xyz
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐