基于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_())图像识别效果(想要效果好些 需要⾃⼰去琢磨调参数)视频车辆识别效果⽂章内容都为个⼈笔记,侵权必究源码下载:。
- 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 ); //象素关系重采样当图像缩小时候,该方法可以避免波纹出现。