基于opencv2.0的车牌检测与字符分割的代码
基于opencv车牌识别的主要算法
基于opencv车牌识别的主要算法
基于OpenCV的车牌识别主要涉及以下几个算法:
1. 图像预处理:车牌识别的第一步是对图像进行预处理,以提取车牌区域。
常用的预处理算法包括灰度化、高斯模糊、边缘检测(如Canny算子)、形态学操作(如腐蚀和膨胀)等。
2. 车牌定位:在预处理后,需要对图像进行车牌定位,以准确定位到车牌区域。
常用的车牌定位算法包括基于颜色特征的方法、基于边缘检测的方法、基于模板匹配的方法等。
3. 字符分割:车牌定位后,需要对车牌区域进行字符分割,将车牌上的字符分割开来。
常用的字符分割算法包括基于投影的方法、基于连通区域的方法、基于边缘检测的方法等。
4. 字符识别:字符分割后,对每个字符进行识别。
常用的字符识别算法包括基于模板匹配的方法、基于特征提取的方法(如垂直投影、水平投影、HOG特征等)、基于机器学习的方法(如支持向量机、神经网络等)等。
5. 后处理:字符识别后,可能需要进行后处理,以进一步提高识别
准确率。
常用的后处理算法包括字符合并、字符校验、模糊匹配等。
需要注意的是,车牌识别是一个复杂的任务,涉及到图像处理、模式识别、机器学习等多个领域的知识。
上述算法只是车牌识别中的一部分,实际应用中还需要根据具体情况进行算法的选择和优化。
此外,还可以结合深度学习等先进技术进行车牌识别的研究和开发。
毕业设计基于python和opencv的车牌识别
毕业设计基于python和opencv的车牌识别摘要:本篇文章介绍了基于Python和OpenCV的车牌识别技术,并详细讨论了车牌识别系统的原理、实现步骤和效果评估。
通过该系统,可以准确地识别出图像中的车牌信息,实现了对车辆的自动监测和管理。
该系统具有较高的准确率和实用性,可以在实际场景中广泛应用。
1. 前言车牌识别技术是计算机视觉领域中的重要研究方向之一。
随着交通运输的发展和车辆数量的增加,对车辆的管理和监测需求日益增加。
传统的车牌识别方法需要大量的人工干预和复杂的算法,效果受到诸多因素的影响。
而基于Python和OpenCV的车牌识别技术能够更加高效、准确地实现车牌的自动识别,为车辆管理提供了更好的支持。
2. 车牌识别系统的原理车牌识别系统的原理基于图像处理和机器学习技术。
首先,通过摄像机获取车辆图像,并使用图像处理技术进行预处理。
对图像进行灰度化、二值化、图像增强等处理,以提高图像质量和车牌的辨识度。
然后,使用基于机器学习的方法对处理后的图像进行特征提取和分类。
通过训练模型,将车牌区域与其他区域进行区分,并提取出车牌的特征信息。
最后,通过字符分割和字符识别技术对车牌上的字符进行提取和识别。
车牌识别系统的准确性取决于算法的优化和模型的训练效果。
3. 车牌识别系统的实现步骤基于Python和OpenCV的车牌识别系统的实现步骤分为图像预处理、特征提取与分类、字符分割和字符识别四个主要步骤。
3.1 图像预处理首先,将获取的车辆图像转换为灰度图像,并对其进行二值化处理。
通过设定合适的阈值,将车牌区域与其他区域进行区分。
然后,进行图像增强处理,包括对比度调整、边缘增强等,以提高车牌的辨识度。
最后,使用形态学操作对图像进行开运算和闭运算,去除噪声和细小的干扰。
3.2 特征提取与分类在图像预处理之后,需要对处理后的图像进行特征提取和分类。
可以使用机器学习算法,如支持向量机(SVM)、卷积神经网络(CNN)等,对车牌区域与其他区域进行分类。
基于opencv的车牌识别的代码
基于opencv的车牌识别的代码车牌识别是计算机视觉领域的一个重要应用,它可以通过图像处理和模式识别技术,自动识别出车辆的车牌号码。
OpenCV是一个开源的计算机视觉库,提供了丰富的图像处理和机器学习算法,非常适合用于车牌识别的开发。
下面是一个基于OpenCV的车牌识别的代码示例:```pythonimport cv2import numpy as np# 加载车牌识别模型plate_cascade =cv2.CascadeClassifier('haarcascade_russian_plate_number.xml') # 加载车牌字符识别模型char_cascade =cv2.CascadeClassifier('haarcascade_russian_plate_number_char.xml') # 读取图像img = cv2.imread('car.jpg')# 转换为灰度图像gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 检测车牌plates = plate_cascade.detectMultiScale(gray, 1.1, 4)# 遍历每个车牌for (x, y, w, h) in plates:# 绘制车牌区域cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2)# 提取车牌区域plate = gray[y:y+h, x:x+w]# 检测车牌字符chars = char_cascade.detectMultiScale(plate, 1.1, 4)# 遍历每个字符for (cx, cy, cw, ch) in chars:# 绘制字符区域cv2.rectangle(img, (x+cx, y+cy), (x+cx+cw, y+cy+ch), (0, 255, 0), 2)# 提取字符区域char = plate[cy:cy+ch, cx:cx+cw]# 进行字符识别# ...# 在这里可以使用机器学习或深度学习算法对字符进行识别# 显示结果图像cv2.imshow('License Plate Recognition', img)cv2.waitKey(0)cv2.destroyAllWindows()```在这个代码示例中,首先我们加载了车牌识别模型和车牌字符识别模型。
【车牌识别】-车牌中字符分割代码详解
【车牌识别】-车牌中字符分割代码详解车牌识别项⽬中,关于字符分割的实现:思路: 1. 读取图⽚,使⽤ cv2 。
2. 将 BGR 图像转为灰度图,使⽤ cv2.cvtColor( img,cv2.COLOR_RGB2GRAY) 函数。
3. 车牌原图尺⼨(170, 722) ,使⽤阈值处理灰度图,将像素值⼤于175的像素点的像素设置为 255 ,不⼤于175的像素点的像素设置为0 。
4.观察车牌中字符,可以看到每个字符块中的每列像素值的和都不为 0 ,这⾥做了假设,将左右结构的省份简写的字也看作是由连续相邻的列组成的,如 “ 桂 ” 。
5. 对于经过阈值处理的车牌中的字符进⾏按列求像素值的和,如果⼀列像素值的和为 0,则表明该列不含有字符为空⽩区域。
反之,则该列属于字符中的⼀列。
判断直到⼜出现⼀列像素点的值的和为0,则这这两列中间的列构成⼀个字符,保存到字典character_dict 中,字典的 key 值为第⼏个字符 ( 下标从0开始 ),字典的value值为起始列的下标和终⽌列的下标。
character_dict 是字典,每⼀个元素中的value 是⼀个列表记录了夹住⼀个字符的起始列下标和终⽌列下标。
6. 之后再对字符进⾏填充,填充为170*170⼤⼩的灰度图(第三个字符为⼀个点,不需要处理,跳过即可。
有可能列数不⾜170,这影响不⼤)。
7. 对填充之后的字符进⾏resize,处理成20*20的灰度图,然后对字符分别进⾏存储。
代码实现:1### 对车牌图⽚进⾏处理,分割出车牌中的每⼀个字符并保存2# 在本地读取图⽚的时候,如果路径中包含中⽂,会导致读取失败。
34import cv25import paddle6import numpy as np7import matplotlib.pyplot as plt8#以下两⾏实现了在plt画图时,可以输出中⽂字符9 plt.rcParams['font.sans-serif']=['SimHei']10 plt.rcParams['axes.unicode_minus'] = False111213# cv2.imread() 读进来直接是BGR 格式数据,数值范围在 0~255 。
OpenCV实现车牌定位(C++)
OpenCV实现车牌定位(C++)最近开始接触 C++ 了,就拿⼀个 OpenCV ⼩项⽬来练练⼿。
在车牌⾃动识别系统中,从汽车图像的获取到车牌字符处理是⼀个复杂的过程,本⽂就以⼀个简单的⽅法来处理车牌定位。
我国的汽车牌照⼀般由七个字符和⼀个点组成,车牌字符的⾼度和宽度是固定的,分别为90mm和45mm,七个字符之间的距离也是固定的12mm,点分割符的直径是10mm。
使⽤的图⽚是从百度上随便找的(侵删),展⽰⼀下原图和灰度图:#include <iostream>#include <opencv2/highgui/highgui.hpp>#include <opencv2/imgproc.hpp>#include <opencv2/imgproc/types_c.h>using namespace std;using namespace cv;int main() {// 读⼊原图Mat img = imread("license.jpg");Mat gray_img;// ⽣成灰度图像cvtColor(img, gray_img, CV_BGR2GRAY);// 在窗⼝中显⽰游戏原画imshow("原图", img);imshow("灰度图", gray_img);waitKey(0);return 0;}灰度图像的每⼀个像素都是由⼀个数字量化的,⽽彩⾊图像的每⼀个像素都是由三个数字组成的向量量化的,使⽤灰度图像会更⽅便后续的处理。
图像降噪每⼀副图像都包含某种程度的噪声,在⼤多数情况下,需要平滑技术(也常称为滤波或者降噪技术)进⾏抑制或者去除,这些技术包括基于⼆维离散卷积的⾼斯平滑、均值平滑、基于统计学⽅法的中值平滑等。
这⾥采⽤基于⼆维离散卷积的⾼斯平滑对灰度图像进⾏降噪处理,处理后的图像效果如下:形态学处理完成了⾼斯去噪以后,为了后⾯更加准确的提取车牌的轮廓,我们需要对图像进⾏形态学处理,在这⾥,我们对它进⾏开运算,处理后如下所⽰:开运算呢就是先进⾏ erode 再进⾏ dilate 的过程就是开运算,它具有消除亮度较⾼的细⼩区域、在纤细点处分离物体,对于较⼤物体,可以在不明显改变其⾯积的情况下平滑其边界等作⽤。
YOLOv5车牌识别实战教程
YOLOv5车牌识别实战教程:字符分割与识别5.1 字符分割在实际应用中,识别车牌的字符是很重要的。
为了实现字符分割,我们可以采用以下方法:1.投影法:通过计算车牌图像在水平和垂直方向上的投影直方图,确定字符的边界。
以下是一个简单的投影法实现:import cv2import numpy as npdef projection_segmentation(plate_image, direction='horizontal'):assert direction in ['horizontal', 'vertical'], 'Invalid direction' gray_image = cv2.cvtColor(plate_image, cv2.COLOR_BGR2GRAY)binary_image = cv2.adaptiveThreshold(gray_image, 255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)if direction == 'horizontal':histogram = np.sum(binary_image, axis=1)else:histogram = np.sum(binary_image, axis=0)threshold = np.max(histogram) * 0.5peaks = np.where(histogram > threshold)[0]start, end = peaks[0], peaks[-1]if direction == 'horizontal':return plate_image[start:end, :]else:return plate_image[:, start:end]复制代码1.轮廓法:通过检测二值化车牌图像的轮廓,然后根据轮廓的位置和形状筛选出字符。
python中超简单的字符分割算法记录(车牌识别、仪表识别等)
python中超简单的字符分割算法记录(车牌识别、仪表识别等)背景在诸如车牌识别,数字仪表识别等问题中,最关键的就是将单个的字符分割开来再分别进⾏识别,如下图。
最近刚好⽤到,就⾃⼰写了⼀个简单地算法进⾏字符分割,来记录⼀下。
图像预处理彩图⼆值化以减⼩参数量,再进⾏腐蚀膨胀去除噪点。
image = cv2.imread('F://demo.jpg', 0) # 读取为灰度图_, image = cv2.threshold(image, 50, 255, cv2.THRESH_BINARY) # ⼆值化kernel1 = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7)) # 腐蚀膨胀核kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) # 腐蚀膨胀核image = cv2.erode(image, kernel=kernel1) # 腐蚀image = cv2.dilate(image, kernel=kernel2) # 膨胀确定字符区域考虑最理想的情况,图中的字符是端正没有倾斜歪曲的。
将像素灰度矩阵分别进⾏列相加、⾏相加,则在得到的列和、⾏和数组中第⼀个⾮ 0 元素索引到最后⼀个⾮ 0 元素索引包裹的区间即就是字符区域。
h, w = image.shape # 原图的⾼和宽list1 = [] # 列和list2 = [] # ⾏和for i in range(w):list1.append(1 if image[:, i].sum() != 0 else 0) # 列求和,不为0置1for i in range(h):list2.append(1 if image[i, :].sum() != 0 else 0) # ⾏求和,不为0置1# 裁剪字符区域# 求⾏的范围flag = 0for i, e in enumerate(list1):if e != 0:if flag == 0: # 第⼀个不为0的位置记录start_w = iflag = 1else: # 最后⼀个不为0的位置end_w = i# 求列的范围flag = 0for i, e in enumerate(list2):if e != 0:if flag == 0: # 第⼀个不为0的位置记录start_h = ielse: # 最后⼀个不为0的位置end_h = iprint(start_w, end_w) # ⾏索引范围print(start_h, end_h) # 列索引范围分割单个字符与分割全部字符区域同理,在⾏和数组中⾮ 0 元素索引的范围即是单个字符的区域。
Opencvpython之车辆识别项目(附代码)
Opencvpython之车辆识别项⽬(附代码)⽂章⽬录图⽚车辆识别根据⽂章搭建好环境后开始进⾏做项⽬import sysimport cv2from PyQt5.QtGui import *from PyQt5.QtWidgets import *from PyQt5.QtGui import QIcon, QPalette, QPixmap, QBrush, QRegExpValidatorclass mainWin(QWidget):def __init__(self):"""构造函数"""super().__init__()self.initUI()self.openBtn.clicked.connect(self.openFile) # 信号和槽self.grayBtn.clicked.connect(self.imgGray) # 信号和槽self.carCheckBtn.clicked.connect(self.carCheck)def initUI(self):# 设置窗⼝得⼤⼩self.setFixedSize(860,600)# 图标和背景self.setWindowTitle("车辆检测")self.setWindowIcon(QIcon("img/icon.jpg")) # 图标# 标签self.leftLab =QLabel("原图:", self)self.leftLab.setGeometry(10,50,400,400) # 设置绝对位置self.leftLab.setStyleSheet("background:white")self.newLab =QLabel("新图:", self)self.newLab.setGeometry(420,50,400,400) # 设置绝对位置self.newLab.setStyleSheet("background-color:white")# 按钮self.openBtn =QPushButton(" 打开⽂件", self)self.openBtn.setGeometry(10,10,80,30)self.grayBtn =QPushButton(" 灰度处理", self)self.grayBtn.setGeometry(100,10,80,30)self.carCheckBtn =QPushButton(" 视频检测", self)self.carCheckBtn.setGeometry(200,10,80,30)打开⽂件⽅法"""打开⽂件的处理函数:return;:return:"""print("打开图⽚")self.img,imgType = QFileDialog.getOpenFileName(self,"打开图⽚","","*.jpg;;*.png;;ALL FILES(*)") print(self.img)#jpg = QPixmap(self.img)self.leftLab.setPixmap(QPixmap(self.img))self.leftLab.setScaledContents(True)图像变灰度并车辆识别⽅法def imgGray(self):print("灰度")img1 = cv2.imread(self.img)#1.灰度化处理img_gray = cv2.cvtColor(img1, cv2.COLOR_RGB2GRAY)# BGR = cv2.cvtColor(module,cv2.COLOR_BGR2RGB)# 转化为RGB格式# ret,thresh = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY)#⼆值化#2.加载级联分类器car_detector = cv2.CascadeClassifier("./cars.xml")"""image--图⽚像素数据scaleFactor=None,缩放⽐例minNeighbors=None,2写2就是3flags =None,标志位⽤什么来进⾏检测minSize=None,最⼩的尺⼨maxSize=None,最⼤的尺⼨self, image, scaleFactor=None, minNeighbors=None, flags=None, minSize=None, maxSize=None """#3.检测车辆多尺度检测,得到车辆的坐标定位cars = car_detector.detectMultiScale(img_gray,1.05,2, cv2.CASCADE_SCALE_IMAGE,(20,20),(100,100)) print(cars)#(274462828)--(x,y,w,h)#4.在车的定位上画图for(x, y, w, h) in cars:print(x, y, w, h)#img, pt1, pt2, color, thickness = None, lineType = None, shift = Nonecv2.rectangle(img1,(x,y),(x+w, y+h),(255,255,255),1, cv2.LINE_AA)# 保存图⽚img_gray_name ="3.png" # ⽂件名cv2.imwrite(img_gray_name, img1) # 保存# 显⽰再控件上⾯self.newLab.setPixmap(QPixmap(img_gray_name))self.newLab.setScaledContents(True)视频车辆识别视频打开且识别⽅法print("车流检测")# parent: QWidget = None, caption: str = '', directory: str = '', filter:#1.选择视频video, videoType = QFileDialog.getOpenFileName(self,"打开视频","","*.mp4")print(video, videoType)# video --打开的视频filename#2.读取加载视频cap = cv2.VideoCapture(video)#3.读取⼀帧图⽚while True:status,img = cap.read()if status:# 灰度gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)# 2.加载级联分类器car_detector = cv2.CascadeClassifier("./cars.xml")cars = car_detector.detectMultiScale(gray,1.2,2, cv2.CASCADE_SCALE_IMAGE,(25,25),(200,200)) # 画框框for(x, y, w, h) in cars:print(x, y, w, h)# img, pt1, pt2, color, thickness = None, lineType = None, shift = Nonecv2.rectangle(img,(x, y),(x + w, y + h),(255,255,255),1, cv2.LINE_AA)print("实时车流量",len(cars))text ='car number: '+str(len(cars))# 添加⽂字cv2.putText(img, text,(350,100), cv2.FONT_HERSHEY_SIMPLEX,1.2,(255,255,0),2)cv2.imshow("opencv", img)key = cv2.waitKey(10) # 延时并且监听按键if key ==27:breakelse:break# 释放资源cap.release()cv2.destroyAllWindows()主函数if __name__ =="__main__":app =QApplication(sys.argv) #创建⼀个应⽤程序win =mainWin() #实例化对象win.show() #显⽰窗⼝sys.exit(app.exec_())图像识别效果(想要效果好些 需要⾃⼰去琢磨调参数)视频车辆识别效果⽂章内容都为个⼈笔记,侵权必究源码下载:。
基于 OpenCV 和 Python 车牌识别系统的设计与实现代码大全
题目基于OpenCV和Python车牌识别系统的设计与实现1.1 题目的主要研究内容(1)工作的主要描述利用python中自带的opencv库中的模式识别算法制作一个简易的模式识别系统,使用自己搜集到的数据集对模型进行训练,最终完成特征提取、分类等工作,并且在最后的推理过程中,实现了车牌识别的工作。
(2)系统流程图1.2 题目研究的工作基础或实验条件项目的编程环境为python,编译器使用pycharm2021.3 x64,设计一个车牌识别系统,有GUI界面。
选择一张有车牌的图片后,完成车牌定位、倾斜校正、字符分割,最后通过k-NN 算法对车牌的字母和数字进行识别,将识别结果在GUI界面中显示出来1.3 数据集描述车牌定位就是在图片中识别出哪个位置有车牌,是字符分割和字母数字识别的前提,是车牌识别系统的关键和难点。
:例如,训练数据的目录结构树如下所示:1.4 特征提取过程描述1.对原始图像进行高斯模糊,减少噪点。
2.提取图像边缘。
首先将彩色图像转为灰度图gray,利用大核对灰度图进行开操作得到图像open,相当于对灰度图进行涂抹操作,将灰度图gray和开操作后的图像open按1:-1的比例融合得到图像add,以上操作可以将大面积灰度值相似的地方置黑,可以减少车灯、背景、地面、挡风玻璃等细节。
接着使用canny 算法对融合图像add提取边缘,得到图像canny。
3.使用横向长条作为核对边缘图像进行一次闭操作,得到图像close,相当于对边缘横向涂抹,因为一般视角车牌是宽大于高的矩形。
再对图像close进行一次开操作,得到图像open2,消除较细的线条和不大的竖向线条,从而将车牌位置的连通区域独立出来。
4.查找连通区域,通过最小外接矩形的宽高比2~5.5筛选合适的连通区域。
5.将最小外接矩形图像旋转矫正,上下左右向外扩展一点范围,避免连通区域没能覆盖车牌造成影响。
6.将连通区域原图转为HSV图像,确定图像的主要颜色,若不为蓝、黄、绿,则排除。
基于opencv的车牌识别系统设计与实现-毕业论文
---文档均为word文档,下载后可直接编辑使用亦可打印---摘要科技的进步以及人民自身的生活水平的不断提高,使得人们对于日常的出行需求变得不断增长。
汽车作为最常见的交通工具已经越来越成为人们最初的选择。
大量新的车辆在不断地投入到道路中使用,而以传统的人工方式对汽车车辆的管理也变得愈加困难。
因此,使用计算机来代替人来处理相对繁重的工作是必要的。
一个良好的交通管理系统是实现道路管理的基础。
想要对于汽车车辆进行管理,最有效的识别特征之一便是汽车的车牌,作为目前最常见的使用技术,车牌识别广泛应用在交叉路段、停车场、收费站等各种场合的监控与管理之中。
所以需要相应的技术来完成以上的需求。
本文以python为使用语言,OpenCV为主要工具,通过输入带有汽车车牌的图像,根据车牌所特有的一些特征,垂直投影法、SVM的方法来完成对于汽车车辆的车牌定位、车牌的字符分割以及字符识别功能。
最终将所识别到的车牌字符输出显示出来。
关键词:OpenCV;投影法;SVM;车牌识别AbstractThe advancement of science and technology and the continuous improvement of people's own living standards have made people's daily travel needs continue to grow. As the most common mode of transportation, cars have become the initial choice of people. A large number of new vehicles are constantly being put into use on the road, and the management of automobile vehicles by traditional manual methods has become increasingly difficult. Therefore, it is necessary to use a computer instead of a person to handle relatively heavy work. A good traffic management system is the foundation for road management.One of the most effective identification features for the management of automobile vehicles is the license plate of the car. As the most commonly used technology at present, license plate recognition is widely used in monitoring and management of various occasions such as intersections, parking lots, toll stations In. Therefore, corresponding technology is needed to complete the above requirements. This article uses python as the language and OpenCV as the main tool. By inputting an image with a car license plate, according to some characteristics unique to the license plate, vertical projection and SVM are used to complete the license plate positioning, character segmentation and characters of the car license plate. Recognition function. Finally, the recognized license plate characters are displayed.Keywords:OpenCV;SVM;projection method; License Plate Recognition1 绪论1.1选题背景与意义1.1.1选题背景随着人们的生活水平的不断提高以及对日常出行需求的不断增长,汽车成为越来越多人出行所选择的交通工具。
车牌识别代码OpenCV
车牌识别代码OpenCV#include<opencv2\opencv.hpp>#include<iostream> using namespace cv; using namespace std; int areas;//该函数⽤来验证是否是我们想要的区域,车牌定位原理其实就是在图⽚上寻找矩形,我们可以⽤长宽⽐例以及⾯积来验证是否是我们想要的矩形,宽⾼⽐为520/110=4.7272 (车牌的长除以宽),区域⾯积最⼩为15个像素,最⼤为125个像素bool VerifySize(RotatedRect candidate) { float error = 0.4; //40%的误差范围float aspect = 4.7272;//宽⾼⽐例int min = 25 * aspect * 25; //最⼩像素为15int max = 125 * aspect * 125;//最⼤像素为125float rmin = aspect - aspect*error;//最⼩误差float rmax = aspect + aspect*error;//最⼤误差int area = candidate.size.height*candidate.size.width;//求⾯积float r = (float)candidate.size.width / (float)candidate.size.height;//长宽⽐if (r < 1)r = 1 / r;if (area<min || area>max || r<rmin || r>rmax)return false;elsereturn true; } int main(int argc, char** argv) {Mat src;src = imread("D:\\Car1.jpg");//读取含车牌的图⽚if (!src.data) {cout << "Could not open Car.jph.." << endl;return -1;}Mat img_gray;cvtColor(src, img_gray, CV_BGR2GRAY);//灰度转换Mat img_blur;blur(img_gray, img_blur, Size(5, 5));//⽤来降噪Mat img_sobel;Sobel(img_gray, img_sobel, CV_8U, 1, 0, 3);//Sobel滤波,对x进⾏求导,就是强调y⽅向,对y进⾏求导,就是强调x⽅向,在此我们对x求导,查找图⽚中的竖直边Mat img_threshold;threshold(img_sobel, img_threshold, 0, 255, THRESH_BINARY | THRESH_OTSU);Mat element = getStructuringElement(MORPH_RECT, Size(21, 5));//这个Size很重要!!不同的图⽚适应不同的Size,待会在下⾯放图,⼤家就知道区别了morphologyEx(img_threshold, img_threshold,MORPH_CLOSE,element);//闭操作,就是先膨胀后腐蚀,⽬的就是将图⽚联通起来,取决于element的Size。
基于Python+OpenCV的车牌识别技术的研究
95收稿日期:2019-04-26*课题来源:2019年度大学生创新创业训练计划项目“基于深度学习的室内环境检测系统的研究”(2019052)作者简介:李晓莎(1987—),女,河北保定人,硕士,讲师,研究方向:数值计算方法,偏微分方程,人工智能,深度学习等。
0 引言智能交通系统(ITS)是将先进的数据通信、电子控制及人工智能等科技综合运用于整个交通运输管理体系,使人一车一路有机结合起来,从而构建起一种全方位发挥作用的精准高效便捷的综合运输系统。
本文介绍了图像预处理、牌照定位、字符分割及字符识别四个模块的原理算法,展示本系统的实现效果,旨在寻求更为快捷有效的算法运用于牌照识别。
1 图像预处理1.1 高斯滤波本文采用高斯滤波对原始图像进行降噪处理。
高斯滤波方法是使用卷积模板遍历图像中的每个像素点,将邻域内像素点被模板确定的高斯加权平均灰度值替换模板中心像素点的值。
二维零均值高斯函数表达式为:222()/(2)21(,)2x y G x y e σπσ-+= (1)OpenCV提供了函数cv2.GaussianBlur(img,(3,3),0)对图形进行高斯滤波,这里(3,3)表示高斯矩阵的长与宽都为3,标准差取0时,OpenCV会根据高斯矩阵的尺寸自行计算。
如图1所示为高斯滤波效果图。
1.2 灰度化彩色图片包含了大量的颜色信息,占用较多的存储空间。
为了提高识别效率,需要对图片进行预处理。
在RGB模型中,若三分量等值时,则颜色呈现为灰度颜色,其灰度值为R=G=B的像素值。
目前主流的灰度化方法是加权平均法[1],将三分量以不同权值进行加权平均,即:(,)0.299(,)0.579(,)0.114(,)Gray i j R i j G i j B i j =++ (2)其中,分别是读取RGB的三个通道的分量值。
OpenCV提供了函数cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)对图形进行灰度化处理,处理后的灰度效果如图2所示。
汽车车牌自动定位与字符分割
汽车车牌自动定位与字符分割I=imread('Car.jpg'); %读入图片figure(1),imshow(I); %显示出图片[y,x,z]=size(I);myI=double(I); %转化数据为双精度型%%%%%%%%%%% RGB to HIS %%%%%%%% tic % 测定算法执行的时间,开始计时%%%%%%%%%%% 统计分析 %%%%%%%%%%%%%%%%=========== Y 方向 =============Blue_y=zeros(y,1);for i=1:yfor j=1:xif((myI(i,j,1)<=30)&&((myI(i,j,2)<=62)&&(myI(i,j,2)>=51))&&((myI(i,j ,3)<=142)&&(myI(i,j,3)>=119)))% 蓝色RGB的灰度范围Blue_y(i,1)= Blue_y(i,1)+1; % 蓝色象素点统计endendend[temp MaxY]=max(Blue_y); % Y方向车牌区域确定 PY1=MaxY;while ((Blue_y(PY1,1)>=5)&&(PY1>1))PY1=PY1-1;endPY2=MaxY;while ((Blue_y(PY2,1)>=5)&&(PY2<y))PY2=PY2+1;endIY=I(PY1:PY2,:,:);%======================% X 方向%======================Blue_x=zeros(1,x); % 进一步确定X方向的车牌区域 for j=1:xfor i=PY1:PY2if((myI(i,j,1)<=30)&&((myI(i,j,2)<=62)&&(myI(i,j,2)>=51))&&((myI(i,j ,3)<=142)&&(myI(i,j,3)>=119)))Blue_x(1,j)= Blue_x(1,j)+1;endendendPX1=1;while ((Blue_x(1,PX1)<3)&&(PX1<x))PX1=PX1+1;endPX2=x;while ((Blue_x(1,PX2)<8)&&(PX2>PX1))PX2=PX2-1;end%======对车牌区域的修正=========PX1=PX1-2; %PX2=PX2+2;Plate=I(PY1:PY2,PX1-2:PX2+2,:); %======像素点数在X、Y方向上的统计并且显示数量统计图 t=toc; % 读取计时figure(2),plot(Blue_x);grid figure(3),plot(Blue_y);gridfigure(4),imshow(IY);figure(5),imshow(Plate);%======字符分割并且分别显示IA=I(PY1:PY2,PX1-2:PX1+12,:); figure(6),imshow(IA);IB=I(PY1:PY2,PX1+ 12:PX1+26,:); figure(7),imshow(IB);IC=I(PY1:PY2,PX1+28:PX1+44,:); figure(8),imshow(IC);ID=I(PY1:PY2,PX1+44:PX1+56,:); figure(9),imshow(ID);IE=I(PY1:PY2,PX1+58:PX1+70,:); figure(10),imshow(IE);IF=I(PY1:PY2,PX1+70:PX1+84,:); figure(11),imshow(IF);IG=I(PY1:PY2,PX1+84:PX2+2,:); figure(12),imshow(IG);%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 字符分割模块算法% 定位剪切后的彩色车牌图像,,灰度,,二值化,,统一到黑底白字,,去除上下边框 % ,,切割出最小范围,,滤波,,形态学处理,,分割出7个字符% 去除上下边框算法:% 1.黑白跳变小于阈值则被视为背景;2.连续白线大于某阈值则该白线被认为是背景% 3.单行白色大于阈值则被认为是背景,考虑FLAG的值; % 4.做完以上处理后,上边1/2 中搜索连续两条黑线,认为该黑线以上为背景;在下边1/2 中搜索连续两条黑线,认为该黑线以下为背景% 归一化为 40*20 ,商用系统程序中归一化为 32*16 ,此处仅演示作用function [d]=lpcseg(jpg)I=imread('car1.jpg');I1=rgb2gray(I);I2=edge(I1,'robert',0.15,'both'); se=[1;1;1];I3=imerode(I2,se);se=strel('rectangle',[25,25]); I4=imclose(I3,se);I5=bwareaopen(I4,2000);[y,x,z]=size(I5);myI=double(I5);ticwhite_y=zeros(y,1);for i=1:yfor j=1:xif(myI(i,j,1)==1)white_y(i,1)= white_y(i,1)+1;endendend[temp MaxY]=max(white_y);PY1=MaxY;while ((white_y(PY1,1)>=5)&&(PY1>1)) PY1=PY1-1;endPY2=MaxY;while ((white_y(PY2,1)>=5)&&(PY2<y)) PY2=PY2+1;endIY=I(PY1:PY2,:,:);white_x=zeros(1,x);for j=1:xfor i=PY1:PY2if(myI(i,j,1)==1)white_x(1,j)= white_x(1,j)+1;endendendPX1=1;while ((white_x(1,PX1)<3)&&(PX1<x)) PX1=PX1+1;endPX2=x;while ((white_x(1,PX2)<3)&&(PX2>PX1))PX2=PX2-1;endPX1=PX1-1;PX2=PX2+1;dw=I(PY1:PY2-8,PX1:PX2,:);t=toc;figure(1),subplot(3,2,1),imshow(dw),title('定位剪切后的彩色车牌图像') imwrite(dw,'dw.jpg');[filename,filepath]=uigetfile('dw.jpg','输入一个定位裁剪后的车牌图像'); jpg=strcat(filepath,filename); a=imread(jpg);%figure(1);subplot(3,2,1),imshow(a),title('1.定位剪切后的彩色车牌图像') b=rgb2gray(a);imwrite(b,'2.车牌灰度图像.jpg');figure(1);subplot(3,2,2),imshow(b),title('2.车牌灰度图像')g_max=double(max(max(b))); g_min=double(min(min(b))); T=round(g_max-(g_max-g_min)/3); % T 为二值化的阈值 [m,n]=size(b);d=(double(b)>=T); % d:二值图像imwrite(d,'3.车牌二值图像.jpg');figure(1);subplot(3,2,3),imshow(d),title('3.车牌二值图像')% 旋转rotate=0;d=imread('3.车牌二值图像.jpg');bw=edge(d);[m,n]=size(d);theta=1:179;% bw 表示需要变换的图像,theta 表示变换的角度% 返回值 r 表示的列中包含了对应于 theta中每一个角度的 Radon 变换结果% 向量 xp 包含相应的沿 x轴的坐标[r,xp]=radon(bw,theta); i=find(r>0);[foo,ind]=sort(-r(i));k=i(ind(1:size(i)));[y,x]=ind2sub(size(r),k); [mm,nn]=size(x);if mm~=0 && nn~=0j=1;while mm~=1 && j<180 && nn~=0i=find(r>j);[foo,ind]=sort(-r(i));k=i(ind(1:size(i)));[y,x]=ind2sub(size(r),k);[mm,nn]=size(x);j=j+1;endif nn~=0if x % Enpty matrix: 0-by-1 when x is an enpty array.x=x;else % 可能 x 为空值x=90; % 其实就是不旋转endd=imrotate(d,abs(90-x)); % 旋转图像rotate=1;endendimwrite(d,'4.Radon 变换旋转后的二值图像.jpg');figure(1),subplot(3,2,4),imshow(d),title('4.Radon 变换旋转后的二值图像')% 统一到白底黑字[m,n]=size(d);% flag=0 表示原来就是白底黑字,否则表示原来是黑底白字 flag=0;c=d([round(m/3):m-round(m/3)],[round(n/3):n-round(n/3)]);if sum(sum(c))/m/n*9>0.5d=~d;flag=1;end% 对反色后的图像预处理,整列几乎为白的认为是背景if flag==1for j=1:nif sum(sum(d(:,j)))/m>=0.95d(:,j)=0;endend% 对以上处理后的图像再处理% 在左边 1/2 处找连续两条黑线,认为该黑线左边为背景;在右边 1/2 处找连续两条黑线,认为该黑线右边是背景% 左边 1/2jj=0;for j=1:round(n/2)if sum(sum(d(:,[j:j+0])))==0jj=j;endendd(:,[1:jj])=0;% 右边 1/2for j=n:-1:round(n/2)if sum(sum(d(:,[j-0:j])))==0jj=j;endendd(:,[jj:n])=0;endimwrite(d,'5.统一成黑底白字.jpg');figure(1),subplot(3,2,5),imshow(d),title('5.背景色统一成黑底白字') figure(2),subplot(5,1,1),imshow(d),title('5.黑底白字的二值车牌图像') % 去除上下边框% STEP 1 黑白跳变小于阈值则被视为背景% 上面 2/5y1=10; % y1: 跳变阈值for i=1:round(m/5*2)count=0;jump=0;temp=0; for j=1:nif d(i,j)==1temp=1;elsetemp=0;endif temp==jumpcount=count;elsecount=count+1;endjump=temp;endif count<y1d(i,:)=0;endend% 下面 2/5for i=3*round(m/5):m count=0;jump=0;temp=0; for j=1:nif d(i,j)==1temp=1;elsetemp=0;endif temp==jumpcount=count;elsecount=count+1;endjump=temp;endif count<y1d(i,:)=0;endendimwrite(d,'6.黑白跳变小于某阈值的行则被视为背景.jpg');figure(2),subplot(5,1,2),imshow(d),title('6.黑白跳变小于某阈值的行则被视为背景')% STEP 2 单行白色大于阈值则被认为是背景,考虑 FLAG 的值 % 上面 2/5 y2=round(n/2); % y2: 阈值for i=1:round(m/5*2)if flag==0temp=sum(d(i,:));y2=round(n/2);if temp>y2d(i,:)=0;endelsetemp=m-sum(d(i,:));y2=m-round(n/2); if temp<y2d(i,:)=0;endendend% 下面 2/5for i=round(3*m/5):mif flag==0temp=sum(d(i,:));y2=round(n/2);if temp>y2d(i,:)=0;endelsetemp=m-sum(d(i,:));y2=m-round(n/2); if temp<y2d(i,:)=0;endendendimwrite(d,'7.单行白色点总数大于某阈值则该行被认为是背景.jpg');figure(2),subplot(5,1,3),imshow(d),title('7.单行白色点总数大于某阈值则该行被认为是背景')% STEP 3 单行白色大于阈值则被认为是背景,考虑 FLAG 的值 % 上面 2/5 y2=round(n/2); % y2: 阈值for i=1:round(m/5*2)if flag==0temp=sum(d(i,:));y2=round(n/2);if temp>y2d(i,:)=0;endelsetemp=m-sum(d(i,:));y2=m-round(n/2);if temp<y2d(i,:)=0;endendend% 下面 2/5for i=round(3*m/5):mif flag==0temp=sum(d(i,:));y2=round(n/2);if temp>y2d(i,:)=0;endelsetemp=m-sum(d(i,:));y2=m-round(n/2);if temp<y2d(i,:)=0;endendendimwrite(d,'8.单行白色点总数大于某阈值则该行被认为是背景.jpg');figure(2),subplot(5,1,4),imshow(d),title('8.单行白色点总数大于某阈值则该行被认为是背景')% STEP 4 做完以上处理后,上边 1/2 中搜索连续两条黑线,认为该黑线以上为背景; % 在下边 1/2 中搜索连续两条黑线,认为该黑线以下为背景 % 上边 1/2 for i=1:round(m/2)if sum(sum(d([i,i+0],:)))==0ii=i;endendd([1:ii],:)=0;% 下边 1/2for i=m:-1:round(m/2)if sum(sum(d([i-0:i],:)))==0ii=i;endendd([ii:m],:)=0;imwrite(d,'9.搜索上下两条黑线后的结果.jpg');figure(2),subplot(5,1,5),imshow(d),title('9.搜索上下两条黑线后的结果') % 反旋转if rotate==1d=imrotate(d,-abs(x-90)); endimwrite(d,'10.反旋转去毛刺后.jpg');figure(3),subplot(3,2,1),imshow(d),title('10.反旋转去毛刺后') % 切割处最小范围d=qiege(d);e=d;imwrite(d,'11.切割处最小范围.jpg');figure(3),subplot(3,2,2),imshow(d),title('11.切割处最小范围')figure(3),subplot(3,2,3),imshow(d),title('11.均值滤波前') % 滤波h=fspecial('average',3); d=im2bw(round(filter2(h,d))); imwrite(d,'12.均值滤波后.jpg');figure(3),subplot(3,2,4),imshow(d),title('12.均值滤波后')% 某些图像进行操作% 膨胀或腐蚀% se=strel('square',3); % 使用一个3X3的正方形结果元素对象对创建的图像进行膨胀% 'line'/'diamond'/'ball'... se=eye(2); % eye(n) returns the n-by-n identity matrix 单位矩阵 [m,n]=size(d);if bwarea(d)/m/n>=0.365d=imerode(d,se);elseif bwarea(d)/m/n<=0.235d=imdilate(d,se);endimwrite(d,'13.膨胀或腐蚀处理后.jpg');figure(3),subplot(3,2,5),imshow(d),title('13.膨胀或腐蚀处理后') % 寻找连续有文字的块,若长度大于某阈值,则认为该块有两个字符组成,需要分割 d=qiege(d);[m,n]=size(d);figure,subplot(2,1,1),imshow(d),title(n)k1=1;k2=1;s=sum(d);j=1;while j~=nwhile s(j)==0j=j+1;endk1=j;while s(j)~=0 && j<=n-1j=j+1;endk2=j-1;if k2-k1>=round(n/6.5)[val,num]=min(sum(d(:,[k1+5:k2-5])));d(:,k1+num+5)=0; % 分割endend% 再切割d=qiege(d);% 切割出 7 个字符y1=10;y2=0.25;flag=0;word1=[]; while flag==0[m,n]=size(d);left=1;wide=0;while sum(d(:,wide+1))~=0wide=wide+1;endif wide<y1 % 认为是左侧干扰d(:,[1:wide])=0;d=qiege(d);elsetemp=qiege(imcrop(d,[1 1 wide m]));[m,n]=size(temp);all=sum(sum(temp));two_thirds=sum(sum(temp([round(m/3):2*round(m/3)],:))); if two_thirds/all>y2flag=1;word1=temp; % WORD 1endd(:,[1:wide])=0;d=qiege(d);endend% 分割出第二个字符[word2,d]=getword(d);% 分割出第三个字符[word3,d]=getword(d);% 分割出第四个字符[word4,d]=getword(d);% 分割出第五个字符[word5,d]=getword(d);% 分割出第六个字符[word6,d]=getword(d);% 分割出第七个字符[word7,d]=getword(d);subplot(5,7,1),imshow(word1),title('1');subplot(5,7,2),imshow(word2),title('2');subplot(5,7,3),imshow(word3),title('3');subplot(5,7,4),imshow(word4),title('4');subplot(5,7,5),imshow(word5),title('5');subplot(5,7,6),imshow(word6),title('6');subplot(5,7,7),imshow(word7),title('7'); [m,n]=size(word1);% 商用系统程序中归一化大小为 32*16,此处演示word1=imresize(word1,[40 20]);word2=wordprocess(word2);word3=wordprocess(word3);word4=wordprocess(word4);word5=wordprocess(word5);word6=wordprocess(word6);word7=wordprocess(word7);subplot(5,7,15),imshow(word1),title('1');subplot(5,7,16),imshow(word2),title('2');subplot(5,7,17),imshow(word3),title('3');subplot(5,7,18),imshow(word4),title('4');subplot(5,7,19),imshow(word5),title('5');subplot(5,7,20),imshow(word6),title('6');subplot(5,7,21),imshow(word7),title('7'); imwrite(word1,'14.字符分割归一化后 1.jpg');imwrite(word2,'14.字符分割归一化后 2.jpg');imwrite(word3,'14.字符分割归一化后 3.jpg');imwrite(word4,'14.字符分割归一化后 4.jpg');imwrite(word5,'14.字符分割归一化后 5.jpg');imwrite(word6,'14.字符分割归一化后 6.jpg');imwrite(word7,'14.字符分割归一化后 7.jpg');%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% clcword='';word(1)=wordrec(word1);word(2)=wordrec(word2);word(3)=wordrec(word3);word(4)=wordrec(word4);word(5)=wordrec(word5);word(6)=wordrec(word6);word(7)=wordrec(word7);clcsave I 'word1' 'word2' 'word3' 'word4' 'word5' 'word6' 'word7' clearload I;load bp net;word='';word(1)=wordrec(word1);word(2)=wordrec(word2);word(3)=wordrec(word3);word(4)=wordrec(word4);word(5)=wordrec(word5);word(6)=wordrec(word6);word(7)=wordrec(word7);word=strcat('识别结果:',word);subplot(5,3,14),imshow([]),title(word,'fontsize',24)% 该子程序用于切割出最小范围function e=qiege(d)[m,n]=size(d);top=1;bottom=m;left=1;right=n; % init while sum(d(top,:))==0 && top<=mtop=top+1;endwhile sum(d(bottom,:))==0 && bottom>=1bottom=bottom-1;endwhile sum(d(:,left))==0 && left<=nleft=left+1;endwhile sum(d(:,right))==0 && right>=1right=right-1;enddd=right-left;hh=bottom-top;e=imcrop(d,[left top dd hh]);% 分割字符function [word,result]=getword(d) word=[];flag=0;y1=8;y2=0.5; % if d==[]% word=[];% elsewhile flag==0[m,n]=size(d);wide=0;while sum(d(:,wide+1))~=0 && wide<=n-2wide=wide+1;endtemp=qiege(imcrop(d,[1 1 wide m]));[m1,n1]=size(temp);if wide<y1 && n1/m1>y2d(:,[1:wide])=0;if sum(sum(d))~=0d=qiege(d); % 切割出最小范围else word=[];flag=1;endelseword=qiege(imcrop(d,[1 1 wide m]));d(:,[1:wide])=0;if sum(sum(d))~=0;d=qiege(d);flag=1;else d=[];endendend%endresult=d; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 该子程序用于字符归一化处理function d=wordprocess(d)[m,n]=size(d);%top 1/3, bottom 1/3for i=1:round(m/3)if sum(sum(d([i:i+0],:)))==0ii=i;d([1:ii],:)=0;endendfor i=m:-1:2*round(m/3)if sum(sum(d([i-0:i],:)))==0ii=i;d([ii:m],:)=0;endendif n~=1d=qiege(d);end% d=..这个可以通过训练过程设置大小% d=imresize(d,[32 16]); % 商用系统程序中归一划大小为:32*16d=imresize(d,[40 20]);%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 编号:A-Z 分别为 1-26; 0-9 分别为 27-36;% 京津沪渝港澳吉辽鲁豫冀鄂湘晋青皖苏 % 赣浙闽粤琼台陕甘云川贵黑藏蒙桂新宁 % 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59% 60 61 62 63 64 65 66 67 68 69 70 % 使用 BP 网络function word=wordrec(xx)% clear% clcload bp net;xx=im2bw(xx);xx=double(xx(:)); % 使用阈值将图像转换为二进制图像a=sim(net,xx); % 归一划为: 32*16,则 xx=512*1; [val,num]=max(a);if num<=26word=char(double('A')+num-1); elseif num<=36word=char(double('0')+num-1-26); elseswitch numcase 37word='京';case 38word='津';case 39word='沪';case 40word='渝';case 41word='港';case 42word='澳';case 43word='吉'; case 44 word='辽'; case 45 word='鲁'; case 46 word='豫'; case 47 word='冀'; case 48 word='鄂'; case 49 word='湘'; case 50 word='晋'; case 51 word='青'; case 52 word='皖'; case 53 word='苏'; case 54 word='赣'; case 55word='浙'; case 56 word='闽'; case 57 word='粤'; case 58 word='琼'; case 59 word='台'; case 60 word='陕'; case 61 word='甘'; case 62 word='云'; case 63 word='川'; case 64 word='贵'; case 65 word='黑'; case 66 word='藏'; case 67word='蒙'; case 68 word='桂'; case 69 word='新'; case 70 word='宁'; endend。
基于OpenCV的车牌识别系统中车牌定位的实现
• 113•基于OpenCV的车牌识别系统中车牌定位的实现广东工业大学 魏 雄随着我国社会的迅猛发展,城市交通事业发展也随之步入快节奏,如今智能交通已成为发展的重要方向与研究热点,其中车牌识别技术(O Russakovsky,J Deng,H Su,et al.Image Net large scale visual recognition challenge:International Journal of Computer Vision 2015,115(3):211-263)作为智能交通中最重要的部分之一,一直是国内外学者们研究计算机视觉的热门问题。
该论文设计的车牌定位采用SVM 支持向量机与颜色特征结合从候选车牌中辨别真伪车牌,从而使车牌识别系统具有更快的速度与更高的效率。
基于OpenCV 库并在Visual Studio 2013环境下对该方法进行工程实现,结果表明该方法具有良好的鲁棒性与较高的准确性。
1.车牌定位本论文设计的车牌定位模块的方法是:若用相机捕捉到的图像中车牌方向角度没有太大的偏转或遮挡,则车牌图像中会包含很多的边缘,这些边缘很有可能就是车牌上的字符形成的,如果在一幅图像中有包含大量垂直边缘的矩形块,而且该矩形块的颜色为蓝色、黄色或绿色,则该矩形块很有可能就是车牌产生的图像块。
如图1为车牌定位的流程。
车牌定位的首任务即为对原始图像进行预处理,首先对图像用高斯滤波算子进行高斯平滑,该操作的目的是去除图像的高斯噪声。
然后把滤波后的图像进行灰度化处理,使其转化为灰度图像,这样接下来的操作都是灰度图处理,相比对原始彩色图像进行操作,灰度图处理更加高效快速。
Sobel 算子采用图2中的sobel 卷积因子提取图像中的垂直边缘。
图2 sobel算子图3 边缘提取后腐蚀得到的图像提取边缘后再对图像进行二值化处理,然后使用图像形态学处图1 车牌定位流程图• 114•理中的闭操作,闭操作之后就可以一张包含有很多矩形块的二值图像,其中车辆车牌就包含其中。
基于python的车牌识别系统设计原理
基于python的车牌识别系统设计原理
车牌识别系统通常包括车辆检测、车牌定位和字符分割等主要步骤。
以下是一个基于Python的车牌识别系统的设计原理:
1. 车辆检测:这一步通常采用视频检测技术,通过在视频流中检测移动物体(如车辆)来实现。
常用的方法包括埋地线圈检测、红外检测、雷达检测和视频检测等。
视频检测技术具有不破坏路面、不需附加外部检测设备、不需矫正触发位置以及适合移动式、便携式应用等优点。
在进行视频车辆检测时,需要具备很高的处理速度并采用优秀的算法,在基本不丢帧的情况下实现图像采集、处理。
2. 车牌定位:在检测到车辆后,系统需要在图像中准确地定位车牌区域。
由于汽车图像背景复杂、光照不均匀,这一步是整个识别过程的关键。
常用的方法包括基于颜色、边缘、纹理等特征的分割方法,以及基于深度学习的目标检测方法(如YOLO、SSD等)。
3. 字符分割:在完成牌照区域的定位后,需要将牌照区域分割成单个字符。
这一步通常采用垂直投影法或水平投影法,根据车牌字符的宽度和间距进行分割。
4. 字符识别:最后,系统需要对分割后的字符进行识别。
常用的方法包括模板匹配法和深度学习方法。
模板匹配法是将分割后的字符与预定义的模板进
行比对,找到最相似的模板作为识别结果。
深度学习方法则是通过训练深度神经网络进行字符识别。
基于Python的车牌识别系统可以使用OpenCV、Pytorch等开源库来实现上述步骤。
在实际应用中,还需要考虑如何提高系统的鲁棒性和准确性,例如通过调整算法参数、训练更复杂的模型等方法来实现。
基于opencv的车牌识别的代码
基于opencv的车牌识别的代码车牌识别技术在现代交通管理、智能停车场等领域有着广泛的应用。
而基于 OpenCV 实现车牌识别是一种常见且有效的方法。
下面我们就来详细探讨一下实现车牌识别的代码逻辑和关键步骤。
首先,在开始编写代码之前,我们需要确保已经安装好了 OpenCV库以及相关的依赖项。
接下来,我们逐步分解车牌识别的主要流程。
第一步,图像采集。
这可以通过摄像头实时获取图像,或者读取已经存在的图片文件。
```pythonimport cv2从摄像头获取图像cap = cv2VideoCapture(0)或者读取图片文件image = cv2imread('platejpg')```第二步,图像预处理。
这一步的目的是提高图像质量,以便后续的处理。
常见的操作包括灰度化、高斯模糊去噪、边缘检测等。
```python灰度化gray_image = cv2cvtColor(image, cv2COLOR_BGR2GRAY)高斯模糊blurred_image = cv2GaussianBlur(gray_image, (5, 5), 0)Canny 边缘检测edges = cv2Canny(blurred_image, 50, 150)```第三步,车牌定位。
这是整个车牌识别中最关键的步骤之一。
通常会利用车牌的形状、颜色、纹理等特征来定位车牌。
```python寻找轮廓contours, _= cv2findContours(edges, cv2RETR_EXTERNAL,cv2CHAIN_APPROX_SIMPLE)for contour in contours:计算轮廓的面积和周长area = cv2contourArea(contour)perimeter = cv2arcLength(contour, True)根据面积和周长等条件筛选出可能的车牌区域if area > 1000 and perimeter > 300:获取车牌区域的矩形边界x, y, w, h = cv2boundingRect(contour)提取车牌区域plate_image = imagey:y + h, x:x + w```第四步,字符分割。
OpenCV下车牌定位算法实现
OpenCV下车牌定位算法实现代码(一)车牌定位算法在车牌识别技术中占有很重要地位,一个车牌识别系统的识别率往往取决于车牌定位的成功率及准确度。
车牌定位有很多种算法,从最简单的来,车牌在图像中一般被认为是长方形,由于图像摄取角度不同也可能是四边形。
我们可以使用OpenCV中的实例:C:\Program Files\OpenCV\samples\c.squares.c 这是一个搜索图片中矩形的一个算法。
我们只要稍微修改一下就可以实现定位车牌。
在这个实例中使用了canny算法进行边缘检测,然后二值化,接着用cvFindContours搜索轮廓,最后从找到的轮廓中根据角点的个数,角的度数和轮廓大小确定,矩形位置。
以下是效果图:这个算法可以找到一些车牌位置,但在复杂噪声背景下,或者车牌图像灰度与背景相差不大就很难定位车牌。
所以我们需要寻找更好的定位算法。
下面是squares的代码:#ifdef _CH_#pragma package <opencv>#endif#ifndef _EiC#include "cv.h"#include "highgui.h"#include <stdio.h>#include <math.h>#include <string.h>#endifint thresh = 50;IplImage* img = 0;IplImage* img0 = 0;CvMemStorage* storage = 0;CvPoint pt[4];const char* wndname = "Square Detection Demo";// helper function:// finds a cosine of angle between vectors// from pt0->pt1 and from pt0->pt2double angle( CvPoint* pt1, CvPoint* pt2, CvPoint* pt0 ){double dx1 = pt1->x - pt0->x;double dy1 = pt1->y - pt0->y;double dx2 = pt2->x - pt0->x;double dy2 = pt2->y - pt0->y;return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10); }// returns sequence of squares detected on the image.// the sequence is stored in the specified memory storageCvSeq* findSquares4( IplImage* img, CvMemStorage* storage ){CvSeq* contours;int i, c, l, N = 11;CvSize sz = cvSize( img->width & -2, img->height & -2 );IplImage* timg = cvCloneImage( img ); // make a copy of input imageIplImage* gray = cvCreateImage( sz, 8, 1 );IplImage* pyr = cvCreateImage( cvSize(sz.width/2, sz.height/2), 8, 3 );IplImage* tgray;CvSeq* result;double s, t;// create empty sequence that will contain points -// 4 points per square (the square's vertices)CvSeq* squares = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvPoint), storage );// select the maximum ROI in the image// with the width and height divisible by 2cvSetImageROI( timg, cvRect( 0, 0, sz.width, sz.height ));// down-scale and upscale the image to filter out the noisecvPyrDown( timg, pyr, 7 );cvPyrUp( pyr, timg, 7 );tgray = cvCreateImage( sz, 8, 1 );// find squares in every color plane of the imagefor( c = 0; c < 3; c++ ){// extract the c-th color planecvSetImageCOI( timg, c+1 );cvCopy( timg, tgray, 0 );// try several threshold levelsfor( l = 0; l < N; l++ ){// hack: use Canny instead of zero threshold level.// Canny helps to catch squares with gradient shadingif( l == 0 ){// apply Canny. Take the upper threshold from slider// and set the lower to 0 (which forces edges merging)cvCanny( tgray, gray,60, 180, 3 );// dilate canny output to remove potential// holes between edge segmentscvDilate( gray, gray, 0, 1 );}else{// apply threshold if l!=0:// tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0//cvThreshold( tgray, gray, (l+1)*255/N, 255, CV_THRESH_BINARY ); cvThreshold( tgray, gray, 50, 255, CV_THRESH_BINARY );}// find contours and store them all as a listcvFindContours( gray, storage, &contours, sizeof(CvContour),CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );// test each contourwhile( contours ){// approximate contour with accuracy proportional// to the contour perimeterresult = cvApproxPoly( contours, sizeof(CvContour), storage,CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0 );// square contours should have 4 vertices after approximation// relatively large area (to filter out noisy contours)// and be convex.// Note: absolute value of an area is used because// area may be positive or negative - in accordance with the// contour orientationif( result->total == 4 &&fabs(cvContourArea(result,CV_WHOLE_SEQ)) > 1000 &&cvCheckContourConvexity(result) ){s = 0;for( i = 0; i < 5; i++ ){// find minimum angle between joint// edges (maximum of cosine)if( i >= 2 ){t = fabs(angle((CvPoint*)cvGetSeqElem( result, i ),(CvPoint*)cvGetSeqElem( result, i-2 ),(CvPoint*)cvGetSeqElem( result, i-1 )));s = s > t ? s : t;}}// if cosines of all angles are small// (all angles are ~90 degree) then write quandrange// vertices to resultant sequenceif( s < 0.3 )for( i = 0; i < 4; i++ )cvSeqPush( squares,(CvPoint*)cvGetSeqElem( result, i ));}// take the next contourcontours = contours->h_next;}}}// release all the temporary imagescvReleaseImage( &gray );cvReleaseImage( &pyr );cvReleaseImage( &tgray );cvReleaseImage( &timg );return squares;}// the function draws all the squares in the imagevoid drawSquares( IplImage* img, CvSeq* squares ){CvSeqReader reader;IplImage* cpy = cvCloneImage( img );int i;// initialize reader of the sequencecvStartReadSeq( squares, &reader, 0 );// read 4 sequence elements at a time (all vertices of a square)for( i = 0; i < squares->total; i += 4 ){CvPoint* rect = pt;int count = 4;// read 4 verticesmemcpy( pt, reader.ptr, squares->elem_size );CV_NEXT_SEQ_ELEM( squares->elem_size, reader );memcpy( pt + 1, reader.ptr, squares->elem_size );CV_NEXT_SEQ_ELEM( squares->elem_size, reader );memcpy( pt + 2, reader.ptr, squares->elem_size );CV_NEXT_SEQ_ELEM( squares->elem_size, reader );memcpy( pt + 3, reader.ptr, squares->elem_size );CV_NEXT_SEQ_ELEM( squares->elem_size, reader );// draw the square as a closed polylinecvPolyLine( cpy, &rect, &count, 1, 1, CV_RGB(0,255,0), 3, CV_AA, 0 );}// show the resultant imagecvShowImage( wndname, cpy );cvReleaseImage( &cpy );}void on_trackbar( int a ){if( img )drawSquares( img, findSquares4( img, storage ) );}char* names[] = { "pic1.png", "pic2.png", "pic3.png","pic4.png", "pic5.png", "pic6.png", 0 };int main(int argc, char** argv){int i, c;// create memory storage that will contain all the dynamic datastorage = cvCreateMemStorage(0);for( i = 0; names[i] != 0; i++ ){// load i-th imageimg0 = cvLoadImage( names[i], 1 );if( !img0 ){printf("Couldn't load %s\n", names[i] );continue;}img = cvCloneImage( img0 );// create window and a trackbar (slider) with parent "image" and set callback// (the slider regulates upper threshold, passed to Canny edge detector)cvNamedWindow( wndname,0 );cvCreateTrackbar( "canny thresh", wndname, &thresh, 1000, on_trackbar );// force the image processingon_trackbar(0);// wait for key.// Also the function cvWaitKey takes care of event processingc = cvWaitKey(0);// release both imagescvReleaseImage( &img );cvReleaseImage( &img0 );// clear memory storage - reset free space positioncvClearMemStorage( storage );if( c == 27 )break;}cvDestroyWindow( wndname );return 0;}#ifdef _EiCmain(1,"squares.c");#endifOpenCV下车牌定位算法实现代码(二)前面介绍了用OpenCV的squares实例定位车牌的算法,效果不是很理想。
python中使用Opencv进行车牌号检测——2018.10.24
python中使⽤Opencv进⾏车牌号检测——2018.10.24初学Python.Opencv,想⽤它做个实例解决车牌号检测。
车牌号检测需要分为四个部分:1.车辆图像获取、2.车牌定位、3.车牌字符分割和4.车牌字符识别在百度查到了车牌识别部分车牌定位和车牌字符分割,先介绍车牌定位部分车牌定位需要⽤到的是图⽚⼆值化为⿊⽩后进canny边缘检测后多次进⾏开运算与闭运算⽤于消除⼩块的区域,保留⼤块的区域,后⽤cv2.rectangle选取矩形框,从⽽定位车牌位置车牌字符的分割前需要准备的是只保留车牌部分,将其他部分均变为⿊⾊背景。
这⾥我采⽤cv2.grabCut⽅法,可将图像分割成前景与背景。
分割完成后,再经过⼆值化为⿊⽩图后即可进⾏字符分割。
由于图像中只有⿊⾊和⽩⾊像素,因此我们需要通过图像的⽩⾊像素和⿊⾊像素来分割开字符。
即分别通过判断每⼀⾏每⼀列的⿊⾊⽩⾊像素值的位置,来定位出字符。
具体程序附下# -*- coding: utf-8 -*-"""Created on Tue Oct 23 20:46:45 2018@author: Administrator"""import cv2import numpy as npdef stretch(img):'''图像拉伸函数'''maxi=float(img.max())mini=float(img.min())for i in range(img.shape[0]):for j in range(img.shape[1]):img[i,j]=(255/(maxi-mini)*img[i,j]-(255*mini)/(maxi-mini))return imgdef dobinaryzation(img):'''⼆值化处理函数'''maxi=float(img.max())mini=float(img.min())x=maxi-((maxi-mini)/2)#⼆值化,返回阈值ret 和⼆值化操作后的图像threshret,thresh=cv2.threshold(img,x,255,cv2.THRESH_BINARY)#返回⼆值化后的⿊⽩图像return threshdef find_rectangle(contour):'''寻找矩形轮廓'''y,x=[],[]for p in contour:y.append(p[0][0])x.append(p[0][1])return [min(y),min(x),max(y),max(x)]def locate_license(img,afterimg):'''定位车牌号'''img,contours,hierarchy=cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)#找出最⼤的三个区域block=[]for c in contours:#找出轮廓的左上点和右下点,由此计算它的⾯积和长度⽐r=find_rectangle(c)a=(r[2]-r[0])*(r[3]-r[1]) #⾯积s=(r[2]-r[0])*(r[3]-r[1]) #长度⽐block.append([r,a,s])#选出⾯积最⼤的3个区域block=sorted(block,key=lambda b: b[1])[-3:]#使⽤颜⾊识别判断找出最像车牌的区域maxweight,maxindex=0,-1for i in range(len(block)):b=afterimg[block[i][0][1]:block[i][0][3],block[i][0][0]:block[i][0][2]] #BGR转HSVhsv=cv2.cvtColor(b,cv2.COLOR_BGR2HSV)#蓝⾊车牌的范围lower=np.array([100,50,50])upper=np.array([140,255,255])#根据阈值构建掩膜mask=cv2.inRange(hsv,lower,upper)#统计权值w1=0for m in mask:w1+=m/255w2=0for n in w1:w2+=n#选出最⼤权值的区域if w2>maxweight:maxindex=imaxweight=w2return block[maxindex][0]def find_license(img):'''预处理函数'''m=400*img.shape[0]/img.shape[1]#压缩图像img=cv2.resize(img,(400,int(m)),interpolation=cv2.INTER_CUBIC)#BGR转换为灰度图像gray_img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#灰度拉伸stretchedimg=stretch(gray_img)'''进⾏开运算,⽤来去除噪声'''r=16h=w=r*2+1kernel=np.zeros((h,w),np.uint8)cv2.circle(kernel,(r,r),r,1,-1)#开运算openingimg=cv2.morphologyEx(stretchedimg,cv2.MORPH_OPEN,kernel) #获取差分图,两幅图像做差 cv2.absdiff('图像1','图像2')strtimg=cv2.absdiff(stretchedimg,openingimg)#图像⼆值化binaryimg=dobinaryzation(strtimg)#canny边缘检测canny=cv2.Canny(binaryimg,binaryimg.shape[0],binaryimg.shape[1])'''消除⼩的区域,保留⼤块的区域,从⽽定位车牌'''#进⾏闭运算kernel=np.ones((5,19),np.uint8)closingimg=cv2.morphologyEx(canny,cv2.MORPH_CLOSE,kernel)#进⾏开运算openingimg=cv2.morphologyEx(closingimg,cv2.MORPH_OPEN,kernel) #再次进⾏开运算kernel=np.ones((11,5),np.uint8)openingimg=cv2.morphologyEx(openingimg,cv2.MORPH_OPEN,kernel) #消除⼩区域,定位车牌位置rect=locate_license(openingimg,img)return rect,imgdef cut_license(afterimg,rect):'''图像分割函数'''#转换为宽度和⾼度rect[2]=rect[2]-rect[0]rect[3]=rect[3]-rect[1]rect_copy=tuple(rect.copy())rect=[0,0,0,0]#创建掩膜mask=np.zeros(afterimg.shape[:2],np.uint8)#创建背景模型⼤⼩只能为13*5,⾏数只能为1,单通道浮点型bgdModel=np.zeros((1,65),np.float64)#创建前景模型fgdModel=np.zeros((1,65),np.float64)#分割图像cv2.grabCut(afterimg,mask,rect_copy,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT) mask2=np.where((mask==2)|(mask==0),0,1).astype('uint8')img_show=afterimg*mask2[:,:,np.newaxis]return img_showdef deal_license(licenseimg):'''车牌图⽚⼆值化'''#车牌变为灰度图像gray_img=cv2.cvtColor(licenseimg,cv2.COLOR_BGR2GRAY)#均值滤波去除噪声kernel=np.ones((3,3),np.float32)/9gray_img=cv2.filter2D(gray_img,-1,kernel)#⼆值化处理ret,thresh=cv2.threshold(gray_img,120,255,cv2.THRESH_BINARY)return threshdef find_end(start,arg,black,white,width,black_max,white_max):end=start+1for m in range(start+1,width-1):if (black[m] if arg else white[m])>(0.98*black_max if arg else 0.98*white_max):end=mbreakreturn endif__name__=='__main__':img=cv2.imread('../image/carnumber7.jpg',cv2.IMREAD_COLOR)#预处理图像rect,afterimg=find_license(img)#框出车牌号cv2.rectangle(afterimg,(rect[0],rect[1]),(rect[2],rect[3]),(0,255,0),2)cv2.imshow('afterimg',afterimg)#分割车牌与背景cutimg=cut_license(afterimg,rect)cv2.imshow('cutimg',cutimg)#⼆值化⽣成⿊⽩图thresh=deal_license(cutimg)cv2.imshow('thresh',thresh)cv2.waitKey(0)#分割字符'''判断底⾊和字⾊'''#记录⿊⽩像素总和white=[]black=[]height=thresh.shape[0] #263width=thresh.shape[1] #400#print('height',height)#print('width',width)white_max=0black_max=0#计算每⼀列的⿊⽩像素总和for i in range(width):line_white=0line_black=0for j in range(height):if thresh[j][i]==255:line_white+=1if thresh[j][i]==0:line_black+=1white_max=max(white_max,line_white)black_max=max(black_max,line_black)white.append(line_white)black.append(line_black)print('white',white)print('black',black)#arg为true表⽰⿊底⽩字,False为⽩底⿊字arg=Trueif black_max<white_max:arg=Falsen=1start=1end=2while n<width-2:n+=1#判断是⽩底⿊字还是⿊底⽩字 0.05参数对应上⾯的0.95 可作调整if(white[n] if arg else black[n])>(0.02*white_max if arg else 0.02*black_max):start=nend=find_end(start,arg,black,white,width,black_max,white_max)n=endif end-start>5:cj=thresh[1:height,start:end]cv2.imshow('cutlicense',cj)cv2.waitKey(0)cv2.waitKey(0)cv2.destroyAllWindows()最后的结果图供参考这算是识别⽐较好的⼀张图⽚,但是有些图⽚仍然识别⽐较差,希望有⼤神可以提出以下改进意见,但是鉴于⾃⼰⽬前处于初学者状态,就先不深究,等到以后学习精进后再回头解决⽬前解决不了的问题。
python+opencv实现车牌定位功能(实例代码)
python+opencv实现车牌定位功能(实例代码)写在前⾯HIT⼤三上学期视听觉信号处理课程中视觉部分的实验三,经过和学长们实验的对⽐发现每⼀级实验要求都不⼀样,因此这⾥标明了是2019年秋季学期的视觉实验三。
由于时间紧张,代码没有进⾏任何优化,实验算法仅供参考。
实验要求对给定的车牌进⾏车牌识别实验代码代码⾸先贴在这⾥,仅供参考源代码实验代码如下:import cv2import numpy as npdef lpr(filename):img = cv2.imread(filename)# 预处理,包括灰度处理,⾼斯滤波平滑处理,Sobel提取边界,图像⼆值化# 对于⾼斯滤波函数的参数设置,第四个参数设为零,表⽰不计算y⽅向的梯度,原因是车牌上的数字在竖⽅向较长,重点在于得到竖⽅向的边界# 对于⼆值化函数的参数设置,第⼆个参数设为127,是⼆值化的阈值,是⼀个经验值gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)GaussianBlur_img = cv2.GaussianBlur(gray_img, (3, 3), 0)Sobel_img = cv2.Sobel(GaussianBlur_img, -1, 1, 0, ksize=3)ret, binary_img = cv2.threshold(Sobel_img, 127, 255, cv2.THRESH_BINARY)# 形态学运算kernel = np.ones((5, 15), np.uint8)# 先闭运算将车牌数字部分连接,再开运算将不是块状的或是较⼩的部分去掉close_img = cv2.morphologyEx(binary_img, cv2.MORPH_CLOSE, kernel)open_img = cv2.morphologyEx(close_img, cv2.MORPH_OPEN, kernel)# kernel2 = np.ones((10, 10), np.uint8)# open_img2 = cv2.morphologyEx(open_img, cv2.MORPH_OPEN, kernel2)# 由于部分图像得到的轮廓边缘不整齐,因此再进⾏⼀次膨胀操作element = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))dilation_img = cv2.dilate(open_img, element, iterations=3)# 获取轮廓contours, hierarchy = cv2.findContours(dilation_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)# 测试边框识别结果# cv2.drawContours(img, contours, -1, (0, 0, 255), 3)# cv2.imshow("lpr", img)# cv2.waitKey(0)# 将轮廓规整为长⽅形rectangles = []for c in contours:x = []y = []for point in c:y.append(point[0][0])x.append(point[0][1])r = [min(y), min(x), max(y), max(x)]rectangles.append(r)# ⽤颜⾊识别出车牌区域# 需要注意的是这⾥设置颜⾊识别下限low时,可根据识别结果⾃⾏调整dist_r = []max_mean = 0for r in rectangles:block = img[r[1]:r[3], r[0]:r[2]]hsv = cv2.cvtColor(block, cv2.COLOR_BGR2HSV)low = np.array([100, 60, 60])up = np.array([140, 255, 255])result = cv2.inRange(hsv, low, up)# ⽤计算均值的⽅式找蓝⾊最多的区块mean = cv2.mean(result)if mean[0] > max_mean:max_mean = mean[0]dist_r = r# 画出识别结果,由于之前多做了⼀次膨胀操作,导致矩形框稍⼤了⼀些,因此这⾥对于框架+3-3可以使框架更贴合车牌cv2.rectangle(img, (dist_r[0]+3, dist_r[1]), (dist_r[2]-3, dist_r[3]), (0, 255, 0), 2)cv2.imshow("lpr", img)cv2.waitKey(0)# 主程序for i in range(5):lpr(str(i+1) + ".jpg")参数调整上述代码中,所有涉及到参数调整的函数,例如形态学操作,都需边调整边观察当前参数下的运⾏结果,待本步运⾏结果较好时,再继续写下⼀步。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
本程序主要实现的是车牌的定位与检测主要是利用申继龙论文里面的方法1、采集得到的图像2、把RGB图像转换成HSI彩色图像3、利用设定的H、S阈值得到二值图像4、对二值图像水平投影获得候选区域5、对候选区域的HSI图像边缘检测*/#include "stdafx.h"#include "opencv2/opencv.hpp"#include "opencv2/objdetect/objdetect.hpp"#include "opencv2/features2d/features2d.hpp"#include "opencv2/highgui/highgui.hpp"#include "opencv2/calib3d/calib3d.hpp"#include "opencv2/nonfree/nonfree.hpp"#include "opencv2/nonfree/features2d.hpp"#include "opencv2/imgproc/imgproc_c.h"#include "opencv2/legacy/legacy.hpp"#include "opencv2/legacy/compat.hpp"#include <iostream>#include <algorithm>#include <functional>#include <vector>#include <string>#include <stdlib.h>#include <stddef.h>#include <stdint.h>#include <math.h>using namespace std;using namespace cv;#define pi 3.14159265IplImage* srcImage=NULL;//存储原图片IplImage*srcImage1=NULL;//存储原始图片的副本IplImage* HSI=NULL;static IplImage* grayImage=NULL;//存储原图片灰度图static double posdouble=0.0;IplImage* channelOneImage=NULL;IplImage* channelTwoImage=NULL;IplImage* channelThreeImage=NULL;IplImage* plateImage=NULL;//存储车牌图像IplImage* grayPlateImage=NULL;//存储车牌灰度图像vector<IplImage*>characterImageList;//存储7个车牌字符图像的容器vector<int>xList;//存储7个车牌字符的起始和结束位置vector<vector<KeyPoint>>keyPointsList;//存储车牌字符特征点的集合vector<Mat>descriptorsMatList;//存储每一个车牌字符的特征点描述子矩阵vector<vector<Point>> contours;//用来存储经过闭开操作处理后的车牌轮廓double GetH(int r,int g,int b){double H=0;//H分量double fenZi=1/2.0*((r-g)+(r-b));double sq=pow(double(r-b),2)+(r-b)*(g-b);double fenMu=sqrt(sq);H=acos(fenZi/fenMu)*180/pi;if(b>g){H=360-H;}return H;}double GetS(int r,int g,int b){double s=0;//S分量int min=r;if(g<r){min=g;if(b<g){min=b;}}else{if(b<r){min=b;}}s=1-3.0*min/((r+g+b)*1.0);return s;}double GetI(int r,int g,int b){double i=0.0;i=1/3.0*(r+g+b);i=i/255.0;return i;}//通过公式来直接求H的值然后对H分量进行处理void doHByMath(IplImage* eleImage){int width=eleImage->width;int height=eleImage->height;for(int col=0;col<height;col++){uchar* ptr=(uchar*)(eleImage->imageData+col*eleImage->widthStep);//uchar* ptrGray=(uchar*)(grayImage->imageData+col*grayImage->widthStep);//for(int row=0;row<width;row++){int b=ptr[3*row];int g=ptr[3*row+1];int r=ptr[3*row+2];double H=GetH(r,g,b);double S=GetS(r,g,b);double I=GetI(r,g,b);if(H>=190&&H<=255&&S>0.3){ptrGray[row]=255;}else{ptrGray[row]=0;}if(I<0.25&&S<0.4){ptrGray[row]=0;}}}cvShowImage("H分量处理后的图像",grayImage);//考虑H分量处理后就提取尽量多的有用的区域vector<vector<cv::Point>>contours;Mat mtx = grayImage;//把IplImage类型转换成Mat类型findContours(mtx,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);Mat result(mtx.size(),CV_8U,Scalar(255));drawContours(result,contours,-1,Scalar(0),2);imshow("轮廓图",result);}//以下代买判断是否为蓝色像素点//返回0代表是白色点//返回1代表是蓝色点int IsBlueOrWhite(IplImage* image,int col,int row){CvScalar s;s = cvGet2D(image, col,row);int b=s.val[0];int g=s.val[1];int r=s.val[2];double H=GetH(r,g,b);double S=GetS(r,g,b);double I=GetI(r,g,b);if(H>=190&&H<=255&&S>0.3){return 1;}/*if(I<255&&S>0){}*//*double grayLevel = r * 0.299 + g * 0.587 + b * 0.114;if (grayLevel >= 200){return 0;}*/boolflagWhite2=(I>=0.95)||(I>=0.81&&I<0.95&&S<(18.0/180.0))||(I>=0.61&&I<=0.8&&S<(20.0/18 0.0))||(I>=0.61&&I<=0.8&&S<(20.0/180.0))||(I>=0.51&&I<=0.6&&S<(30.0/180.0));bool flagWhite1=(S<=0.3&&I>=0.6);if(flagWhite1||flagWhite2){return 0;}return 2;}int IsBlueOrWhite(int col,int row){return IsBlueOrWhite(srcImage,col,row);}bool IsEdgeOrNot(int col,int row){int flag00=IsBlueOrWhite(col-1,row-1);int flag10=IsBlueOrWhite(col,row-1);int flag20=IsBlueOrWhite(col+1,row-1);int flag01=IsBlueOrWhite(col-1,row);int flag11=IsBlueOrWhite(col,row);int flag21=IsBlueOrWhite(col+1,row);int flag02=IsBlueOrWhite(col-1,row+1);int flag12=IsBlueOrWhite(col,row+1);int flag22=IsBlueOrWhite(col+1,row+1);if(flag00==1&&flag10==1&&flag20==1){if(flag02==0&&flag12==0&&flag22==0){return true;//是蓝白边缘}}if(flag00==0&&flag10==0&&flag20==0){if(flag02==1&&flag12==1&&flag22==1){return true;//是蓝白边缘}}return false;}//对是蓝白边缘的一个处理void doIsEdge(int col ,int row){cvSet2D(grayImage, col-1,row,cvScalar(255));cvSet2D(grayImage, col,row,cvScalar(255));cvSet2D(grayImage, col+1,row,cvScalar(255));cvSet2D(grayImage, col-1,row-1,cvScalar(0));cvSet2D(grayImage, col,row-1,cvScalar(0));cvSet2D(grayImage, col+1,row-1,cvScalar(0));cvSet2D(grayImage, col-1,row+1,cvScalar(0));cvSet2D(grayImage, col,row+1,cvScalar(0));cvSet2D(grayImage, col+1,row+1,cvScalar(0));}//对不是蓝白边缘的处理void doNotEdge(int col,int row){cvSet2D(grayImage, col-1,row,cvScalar(0));cvSet2D(grayImage, col,row,cvScalar(0));cvSet2D(grayImage, col+1,row,cvScalar(0));cvSet2D(grayImage, col-1,row-1,cvScalar(0));cvSet2D(grayImage, col,row-1,cvScalar(0));cvSet2D(grayImage, col+1,row-1,cvScalar(0));cvSet2D(grayImage, col-1,row+1,cvScalar(0));cvSet2D(grayImage, col,row+1,cvScalar(0));cvSet2D(grayImage, col+1,row+1,cvScalar(0));}//边缘检测代码//检测蓝白边缘//通过构造3*3窗口void EdgeDetection(){int width=srcImage->width;int height=srcImage->height;for(int col=1;col<height;col+=3){for(int row=1;row<width;row+=3){//判断是否为蓝白边缘if(IsEdgeOrNot(col,row)){//设置3*3窗口中间一列的所有值为1 其他列为0doIsEdge(col,row);}else{//设置3*3窗口中所有元素的值为0doNotEdge(col,row);}}}cvShowImage("蓝白边缘检测后的车辆图像",grayImage);}//这个函数主要是实现提取每一个字符的sift特征//在提取sift特征我们先对每一个字符图片做尺寸的归一化处理void ExtractSiftFeature(){Mat image11= imread("C:/Users/wzafxj/Desktop/车牌车标识别论文/车牌识别论文集锦/字符模板/字符模板/G.bmp",0);SurfFeatureDetector surf(5000);//特征点选取的hessian 阈值为3000SurfDescriptorExtractor surfDesc;//SurfDescriptorExtractor是提取特征向量的描述子Mat descriptors;vector<KeyPoint> keypoints;//存储每个车牌字符的surf特征点CvSize size=cvSize(41,82);CvSize cvsize11=cvSize(30,62);resize(image11,image11,cvsize11);vector<DMatch> matches;DescriptorMatcher *pMatcher = new FlannBasedMatcher; // 使用Flann匹配算法vector<KeyPoint> keypoints11;surf.detect(image11,keypoints11);//这里是利用surf算法检测关键点Mat descriptors11;pute(image11,keypoints11,descriptors11);imshow("moban",image11);IplImage* pDstImage = cvCreateImage(size, grayPlateImage->depth, grayPlateImage->nChannels);for(int i=0;i<characterImageList.size();i++){cvResize(characterImageList[i],pDstImage,CV_INTER_AREA);Mat image=pDstImage;imshow("hahh",image);surf.detect(image,keypoints);//这里是利用surf算法检测关键点keyPointsList.push_back(keypoints);//接下来提取surf特征点pute(image,keypoints,descriptors);descriptorsMatList.push_back(descriptors);pMatcher->match(descriptors11, descriptors, matches);Mat imageMatches;drawMatches(image11,keypoints11, // 1st image and its keypointsimage,keypoints, // 2nd image and its keypointsmatches, // the matchesimageMatches, // the image producedScalar(255,255,255)); // color of the linesstring winName="WN"+i;imshow(winName,imageMatches);}}//显示已经分割出来的车牌字符void ShowSeparatedCharacterImage(){if(characterImageList.size()>0){for (int i=0;i<characterImageList.size();i++){const char* windowName="识别出来的车牌字符"+i;//注意这里前面必须得加const//否则会报错因为"识别出来的车牌字符" 是常量字符不能赋给不是常量的字符cvNamedWindow(windowName,1);cvShowImage(windowName,characterImageList[i]);}}}//根据xList里面存储的14个值来对void CharacterSegmentation(){int height=grayPlateImage->height;IplImage* characterSegImage=NULL;//定义一个临时存储字符图片CvRect roiGrayPlate;CvRect roiCharacter;for(int i=0;i<xList.size();i=i+2){int x1=xList[i];int x2=xList[i+1];int width=x2-x1;characterSegImage=cvCreateImage(cvSize(width,height),grayPlateImage->depth,grayPlateI mage->nChannels);roiGrayPlate=cvRect(x1,0,width,height);cvSetImageROI(grayPlateImage,roiGrayPlate);roiCharacter=cvRect(0,0,width,height);cvSetImageROI(characterSegImage,roiCharacter);cvCopy(grayPlateImage,characterSegImage);cvResetImageROI(characterSegImage);cvResetImageROI(grayPlateImage);characterImageList.push_back(characterSegImage);}//接下来就是对提取出来的字符进行sift特征提取,并且标准库中的字符模板进行sift 特征匹配,以达到字符识别的目的//不过进行上面的操作之前我们先把这七个字符显示出来再说ShowSeparatedCharacterImage();//接下来是对分割出来的车牌特征进行sift特征提取//ExtractSiftFeature();}//对字符为白色、背景为黑色的车牌做垂直投影//以此来达到字符分割的目的bool VerticalProjectionToPlateImage(IplImage* grayPlateImage){IplImage* paintx=cvCreateImage( cvGetSize(grayPlateImage),IPL_DEPTH_8U, 1 );cvZero(paintx);int* v=new int[grayPlateImage->width];memset(v,0,grayPlateImage->width*4); //作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法int x,y;CvScalar s,t;//再次提醒对于图像来说坐标原点是在图像的左上角//我们定义一个容器来存储车牌字符的每一个起始位置和结束位置//是这样的容器里面的第一个值是第一个字符的起始位置//第二个值既是第一个字符的结束位置,依次类推//但是第二个字符过后的14个像素单位里面可能出现字符点,我们不能提取,这个点就是车牌字符中的那个点//在字符的检测中,如果x2-x1的长度超过了20,我们就默认这个字符的检测已经结束,就是说已经检测出来这个字符了//注意除了第二个字符和第三个字符之间的距离超过了14个像素点之多外呢,其他每两个字符之间的距离应该也至少有2个像素//因此我们在取值的时候一定要注意到这个问题比如2 22 27 47 60 80 81 101 102 122 123 143 146 166 167//仔细观察60 80 81 中的81的取值不对应该跨两个像素点再去取应该取83 也就是xList的size()为双数的时候要进行判断//可惜我们还是没有考虑字符“1”的情形//通过仔细观察我们字符“1”它的高度至少超过30像素它的宽度在5左右主要是它离它后面的一个字符的距离有10像素左右bool flag1=false;bool flag2=false;int count=0;int x1=0;int x2=0;int countPoint=0;//设置这个是为了对中间因为有一个. 而存在8个多像素没有字符出现的情况所以我们设置一个这个来判断int countOne=0;//用来对字符‘1’的处理bool flagOne=false;//也是用来对字符1的处理bool flagoneOut=false;//为true 代表字符'1'出现了for(x=3;x<grayPlateImage->width;x++){int countCol=0;for(y=0;y<grayPlateImage->height;y++){s=cvGet2D(grayPlateImage,y,x);if(s.val[0]==0)v[x]++;if(s.val[0]==255){countCol++;}}if(xList.size()==4){countPoint++;}if(countCol>0&&flag1==false){if(countPoint>0&&countPoint<12){//对中间出现"." 的处理}else{if(xList.size()!=0&&xList.size()%2==0){if(xList.size()>0){//xList.back()获得容器的最后一个元素if(x-xList.back()<2){}else{xList.push_back(x);flag1=true;}}}else{xList.push_back(x);flag1=true;}}}if(flag1==true){if(countPoint>0&&countPoint<12){//对中间出现"." 的处理}else{//如果字符'1' 出现在最后一个位子我们就没有处理的必要了if(xList.size()<=12){if(countCol>0){flagOne==true;countOne=0;}if(countCol==0&&flagOne==false){countOne++;//如果countOne的值达到10左右(单位像素)且已不再countPoint处理的范围也就是已经过了中间的那个点}if(countOne>=12){flagOne==true;countOne=0;//代表很有可能这个字符是1flagoneOut=true;//代表是字符1出现了}}}}if(countCol>0||(countCol==0&&count<20)){if(countPoint>0&&countPoint<12){//对中间出现"." 的处理}else{//在这里我们加入一个字符1的处理if(flagoneOut==true){count=0;flag1=false;flagoneOut=false;xList.push_back(x);}else{if(count<20){if(flag1==true){count++;//对于一个字符来讲必须等到起始位置记录了,才让count的值++}}else{count=0;flag1=false;xList.push_back(x);}}}}else{if(countPoint>0&&countPoint<12){//对中间出现"." 的处理}else{count=0;flag1=false;xList.push_back(x);}}}//当xList里面存储的值的个数为13个(本来7个字符应该存储14个)的话,则把grayPlateImage->width存储到Xlist里面if(xList.size()<13)return false;if(xList.size()==13){xList.push_back(grayPlateImage->width);}if(xList.size()==15){xList.pop_back();//删除容器的最后一个元素}for(x=0;x<grayPlateImage->width;x++){for(y=0;y<v[x];y++){t.val[0]=255;cvSet2D(paintx,y,x,t);}}cvShowImage("车牌垂直积分投影",paintx);//到这一步xList里面存储的是14个值每两个值代表的是一个字符元素的起始位置和结束位置//接下来的操作就是我们利用这14个值来分割出字符来CharacterSegmentation();}//这里是对图像进行任意角度倾斜的函数,angel表示要旋转的角度//clockwise传递过来的值如果是为true则顺时针旋转,为false逆时针旋转IplImage* rotateImage(IplImage* src, int angle, bool clockwise){angle = abs(angle) % 180;if (angle > 90){angle = 90 - (angle % 90);}IplImage* dst = NULL;int width =(double)(src->height * sin(angle * CV_PI / 180.0)) +(double)(src->width * cos(angle * CV_PI / 180.0 )) + 1;int height =(double)(src->height * cos(angle * CV_PI / 180.0)) +(double)(src->width * sin(angle * CV_PI / 180.0 )) + 1;int tempLength = sqrt((double)src->width * src->width + src->height * src->height) + 10;int tempX = (tempLength + 1) / 2 - src->width / 2;int tempY = (tempLength + 1) / 2 - src->height / 2; //这里的tempX和tempY指的是图像的中心像素点int flag = -1;dst = cvCreateImage(cvSize(width, height), src->depth, src->nChannels);cvZero(dst);IplImage* temp = cvCreateImage(cvSize(tempLength, tempLength), src->depth, src->nChannels); //图像temp并没有多大的实际含义,//只不过是说把src图像size稍微扩大一点//内容和src完全一样cvZero(temp);cvSetImageROI(temp, cvRect(tempX, tempY, src->width, src->height));cvCopy(src, temp, NULL);cvResetImageROI(temp);if (clockwise)flag = 1;float m[6];int w = temp->width;int h = temp->height;m[0] = (float) cos(flag * angle * CV_PI / 180.);m[1] = (float) sin(flag * angle * CV_PI / 180.);m[3] = -m[1];m[4] = m[0];// 将旋转中心移至图像中间m[2] = w * 0.5f;m[5] = h * 0.5f;//CvMat M = cvMat(2, 3, CV_32F, m); //M是一个3 ×2 变换矩阵[A|b]//m0 m1 m2// m3 m4 m5//变换公式如下//dst(x + width(dst) / 2,y + height(dst) / 2)=//(m0x+m1y+b1,m3x+m4y+b2)cvGetQuadrangleSubPix(temp, dst, &M); //提取象素四边形,使用子象素精度dst提取的是一个四边形cvReleaseImage(&temp);return dst;}//获得车牌的倾斜角度然后校正//定义一个容器存储所有的countColvector<int> countColVector;struct maxTotalColAndAngel{int maxTotalCol;int angel;};vector <maxTotalColAndAngel> maxTotalColAndAngelVector;bool less_second(const maxTotalColAndAngel m1, maxTotalColAndAngel m2) { return m1.maxTotalCol < m2.maxTotalCol;}bool GetAngleAndRotate(IplImage* _image){IplImage* dstImage=NULL;//假定车牌的旋转角度控制在30度的范围之内bool _direction=false;//true代表顺时针int _actualAngle=0;//存储最后得到的旋转角度CvScalar s;int maxCount=0;//存储最大的那个投影值bool changeDirection=false;int flag=-2;//-1代表的是往一开始设定的相反的方向旋转1代表的是旋转的方向是对的但是已经过了最大值int Xnum=0;//这里我们可以先做下canny二值化来减少计算量for(int _angle=0;_angle<=30;_angle+=5){dstImage=rotateImage(_image,_angle,_direction);for(int x=dstImage->width-1;x>=0;x--){int countCol=0;for(int y=0;y<dstImage->height;y++){s=cvGet2D(dstImage,y,x);//ptr[row]=0;if(s.val[0]>0){countCol++;}}if(countCol>0){countColVector.push_back(countCol);}}sort(countColVector.begin(),countColVector.end());maxTotalColAndAngel _colandangel={0,0};_colandangel.angel=_angle;for (int i=countColVector.size()-1;i>=countColVector.size()-2;i--){_colandangel.maxTotalCol+=countColVector[i];}if(changeDirection==false&&_angle==5&&_colandangel.maxTotalCol<=maxTotalColAndA ngelVector[0].maxTotalCol){maxTotalColAndAngelVector.clear();changeDirection=true;_direction=!_direction;_angle=-5;continue;}maxTotalColAndAngelVector.push_back(_colandangel);countColVector.clear();//以便下次存储数据}sort(maxTotalColAndAngelVector.begin(),maxTotalColAndAngelVector.end(),less_second);_actualAngle=maxTotalColAndAngelVector[maxTotalColAndAngelVector.size()-1].angel;dstImage=rotateImage(_image,_actualAngle,_direction);//在maxTotalColAndAngelVector[maxTotalColAndAngelVector.size()-1].angel的正负5度之内寻找旋转角度的精确值if(_actualAngle>0){maxTotalColAndAngelVector.clear();for (int _angleDetail=_actualAngle-5;_angleDetail<=_actualAngle+5;_angleDetail++){dstImage=rotateImage(_image,_angleDetail,_direction);for(int x=dstImage->width-1;x>=0;x--){int countCol=0;for(int y=0;y<dstImage->height;y++){s=cvGet2D(dstImage,y,x);//ptr[row]=0;if(s.val[0]>0){countCol++;}}if(countCol>0){countColVector.push_back(countCol);}}sort(countColVector.begin(),countColVector.end());maxTotalColAndAngel _colandangel={0,0};_colandangel.angel=_angleDetail;for (int i=countColVector.size()-1;i>=countColVector.size()-2;i--){_colandangel.maxTotalCol+=countColVector[i];}maxTotalColAndAngelVector.push_back(_colandangel);countColVector.clear();//以便下次存储数据}sort(maxTotalColAndAngelVector.begin(),maxTotalColAndAngelVector.end(),less_second);_actualAngle=maxTotalColAndAngelVector[maxTotalColAndAngelVector.size()-1].angel;dstImage=rotateImage(_image,_actualAngle,_direction);}imshow("旋转后的车牌图像",(Mat)dstImage);//这里只是水平旋转的结果//我们把这个水平旋转的角度传给原始的车脸图像,对车脸图像进行旋转IplImage* srcDst=rotateImage(srcImage,_actualAngle,_direction);imshow("车脸图片的旋转",(Mat)srcDst);//下面还要加一个垂直旋转CvSize cvsize=cvSize(172,60);IplImage*plateImage2=cvCreateImage(cvsize,dstImage->depth,dstImage->nChannels); CvRect roiSrc =cvRect(dstImage->width/2-86,dstImage->height/2-30,172,60); cvSetImageROI(dstImage, roiSrc);CvRect roiPlate=cvRect(0,0,172,60);cvSetImageROI(plateImage2,roiPlate);cvCopy(dstImage,plateImage2);cvResetImageROI(dstImage);cvResetImageROI(plateImage2);imshow("裁剪后的图像",(Mat)plateImage2);//去除一些边边框框//我们最好在这里对plateImage2进行垂直旋转//对经过处理后的车牌进行垂直投影处理grayPlateImage=plateImage2;bool flagYN=VerticalProjectionToPlateImage(plateImage2);if(flagYN==false)return false;}//对提取出来的车牌图像的一个处理//把字符设置成白色//把背景设置成黑色bool DoWithPlateImage(){//首先把PlateImage进行灰度化处理grayPlateImage=cvCreateImage(cvGetSize(plateImage),8,1);cvCvtColor(plateImage,grayPlateImage,CV_RGB2GRAY);for(int col=0;col<grayPlateImage->height;col++){uchar* ptr=(uchar*)(grayPlateImage->imageData+col*grayPlateImage->widthStep);for(int row=0;row<grayPlateImage->width;row++){int flag=IsBlueOrWhite(plateImage,col,row);////flag为0代表是白色点flag为1代表是蓝色点if(flag==1||flag==2){ptr[row]=0;}else{ptr[row]=255;}}}//做一个开操作和闭操作cvShowImage("经处理后的车牌图像",grayPlateImage);//我们最好对车牌定一个统一的尺寸CvSize cvsize=cvSize(175,70);IplImage* pDstImage = cvCreateImage(cvsize, grayPlateImage->depth, grayPlateImage->nChannels);cvResize(grayPlateImage, pDstImage, CV_INTER_AREA ); //象素关系重采样当图像缩小时候,该方法可以避免波纹出现。