基于opencv的车牌识别解析与代码

article/2025/9/11 18:38:20

Jeremy Lin @HQU

车牌识别太出名了,我也就花几天来了解下这个系统,并结合opencv进行实现。下面是一些介绍:

车辆牌照识别(License Plate RecognitionLPR)技术作为交通管理自动化的重要手段和车辆检测系统的一个重要环节,在交通监视和控制中占有很重要的地位。LPR系统可以广泛应用于高速公路电子收费站、出入控制、公路流量监控、失窃车辆查询、停车场车辆管理、公路稽查、检测黑牌机动车、监控违章车辆的电子警察等需要车牌认证的重要场合,尤其是基于车牌识别技术的高速公路收费系统中,相对于射频卡等技术,可以实现不停车收费,提高了公路系统的运行效率。

车牌识别技术的研究最早出现在20世纪80年代,这个阶段的研究没有形成完整的系统体系,而是就某一具体的问题进行研究,通常采用简单的图像处理方法来解决。识别过程是使用工业电视摄像机拍下汽车的正前方图像,然后交给计算机进行简单处理,并且最终仍需要人工干预。进入20世纪90年代后,随着计算机视觉的发展和计算机性能的提高,开始出现了车牌识别的系统化研究。1990A.S. Johnson等运用计算机视觉技术和图像处理技术实现了车辆牌照的自动识别。该系统分为图像分割、特征提取和模板构造等三个部分,利用不同的阈值对应的直方图的不同,经过大量的统计实验确定车牌位置的图像直方图的阈值范围,从而根据特定的阈值对应的直方图分割出车牌,再利用预先设置的标准字符模板进行模式匹配,识别出字符。1994Young Sung Soh[2]开发出一套实时车牌识别系统,该系统的准确率达到99.2%。国外还有许多车牌识别系统的报道,由于他们起步早,总体来讲其技术水平高于国内,特别在产品化方面。但由于中国车牌的格式与外国的有很大的差异,并有汉字的识别问题,所以国外关于识别率的研究只是具有参考价值。

国内在90年代也开始了车牌识别的研究。目前比较成熟的产品有中科院自动化研究所汉王公司的汉王眼,亚洲视觉科技有限公司、深圳吉通电子有限公司、中国信息产业部下属的中智交通有限公司等也有自己的产品。另外西安交通大学的图像处理和识别实验室、上海交通大学的计算机科学与工程系、清华大学人工智能国家重点实验室、浙江大学的自动化系等也做过类似的研究。

目前车牌识别系统面临的一些问题:(1)、在全天候的情况下,如何获取车辆号牌的清晰图像,即无论是白天还是黑夜,强逆光还是强顺光,刮风还是下雨,都希望将车辆的号牌图像拍清晰;(2)、在图像中如何正确的识别出车牌,即算法如何在一个尽量大的范围内适应各种天气情况下的图片。

因为不是我的研究重点,只是打算用这个系统练练手,所以我也就不打算考虑它所面临的那些问题,只是进行一般的代码实现。

一个典型的车辆牌照识别系统一般包括以下4个部分:车辆图像获取、车牌定位、车牌字符分割和车牌字符识别,如下图所示:

车辆图像获取

车辆图像获取是车牌识别的第一步,也是很重要的一步,车辆图像的好坏对后面的工作有很大的影响。如果车辆图像的质量太差,连人眼都没法分辨,那么肯定不会被机器所识别出来。车辆图像都是在实际现场拍摄出来的,实际环境情况比较复杂,图像受天气和光线等环境影响较大,在恶劣的工作条件下系统性能将显著下降。

现有的车辆图像获取方式主要有两种:一种是由彩色摄像机和图像采集卡组成,其工作过程是:当车辆检测器(如地感线圈、红外线等)检测到车辆进入拍摄范围时,向主机发送启动信号,主机通过采集卡采集一幅车辆图像,为了提高系统对天气、环境、光线等的适应性,摄像机一般采用自动对焦和自动光圈的一体化机,同时光照不足时还可以自动补光照明,保证拍摄图片的质量;另一种是由数码照相机构成,其工作过程是:当车辆检测器检测到车辆进入拍摄范围时,直接给数码照相机发送一个信号,数码相机自动拍摄一幅车辆图像,再传到主机上,数码相机的一些技术参数可以通过与数码相机相连的主机进行设置,光照不足时也需要自动开启补光照明,保证拍摄图片的质量。

车牌定位

车牌定位的主要工作是从摄入的汽车图像中找到汽车牌照所在位置,并把车牌从该区域中准确地分割出来,供字符分割使用。因此,牌照区域的确定是影响系统性能的重要因素之一,牌照的定位与否直接影响到字符分割和字符识别的准确率。目前车牌定位的方法很多,但总的来说可以分为以下4类:(1)基于颜色的分割方法,这种方法主要利用颜色空间的信息,实现车牌分割,包括彩色边缘算法、颜色距离和相似度算法等;(2)基于纹理的分割方法,这种方法主要利用车牌区域水平方向的纹理特征进行分割,包括小波纹理、水平梯度差分纹理等;(3)基于边缘检测的分割方法;(4)基于数学形态法的分割方法。

本文为了代码实现上的方便,我采用的是基于边缘检测的分割方法。主要是利用水平投影方法和垂直投影方法进行车牌定位。

车牌字符分割

要识别车牌字符,前提是先进行车牌字符的正确分割与提取。字符分割的任务是把多列或多行字符图像中的每个字符从整个图像中切割出来成为单个字符。车牌字符的正确分割对字符的识别是很关键的。传统的字符分割算法可以归纳为以下三类:直接分割法、基于识别基础上的分割法、自适应分割线类聚法。直接分割法简单,但它的局限是分割点的确定需要较高的准确性;基于识别基础上的分割法是把识别和分割结合起来,但是需要识别的高准确性,它根据分类和识别的耦合程度又有不同的划分;自适应分割线聚类法是要建立一个分类器,用它来判断图像的每一列是否是分割线,它是根据训练样本来进行自适应学习的神经网络分类器,但对于粘连字符训练困难。也有直接把字符组成的单词当作一个整体来识别的,诸如运用马尔科夫数学模型等方法进行处理,这些算法主要应用于印刷体文本识别。

本文的同样为了实现上的简单,采用的是垂直投影方法。

车牌字符识别

与一般印刷体字符识别相比,车牌字符识别尤其自身的特点,它是文字识别技术与车牌图像自身因素协调兼顾的综合技术,目前,车牌字符识别算法主要是基于模板匹配、特征匹配或神经网络的方法。我国的车牌字符包括50多个汉字,25个大写英文字母,10个数字,总共也就80多个字符,鉴于车牌识别系统的特殊性,如果照搬普通汉字识别的方法,对文字细化后再提取其结构或统计特征,非但得不到意想的结果,反而会降低识别率。

这一部分我没有实现。



代码如下: 全部文件下载地址

// LPRmain.cpp : 快速车牌识别系统的主文件
// 本文件是系统的车牌定位和字符分割部分的代码,本系统引用的是OpenCV2.4.7,IDE是VS2012
// Author:LinJianmin
// Email: jianmin1990@outlook.com
// University:Huaqiao University
// Date:  2014-1#include "LPR.h"
int main()
{
////第一部分:图像采集/图像载入(本系统直接读取图像)//若采用camera采集示例如下:/*	VideoCapture cap(0);if (!cap.isOpened()){return -1;}Mat car_frame;cap >> car_frame;*///图像载入Mat car_img;car_img = imread("E:\\LPR\\car.bmp");	if (!car_img.data){cout<<"Please check the input image"<<endl;}/// 第二部分:车牌定位//转变成灰度图像Mat gray;cvtColor(car_img,gray,CV_BGR2GRAY);//高斯滤波器滤波去噪(可选)
/*	int ksize = 3;Mat g_gray;Mat G_kernel = getGaussianKernel(ksize,0.3*((ksize-1)*0.5-1)+0.8);filter2D(gray,g_gray,-1,G_kernel);//Sobel算子(x方向和y方向)Mat sobel_x,sobel_y;Sobel(g_gray,sobel_x,CV_16S,1,0,3);Sobel(g_gray,sobel_y,CV_16S,0,1,3);Mat abs_x,abs_y;convertScaleAbs(sobel_x,abs_x);convertScaleAbs(sobel_y,abs_y);Mat grad;addWeighted(abs_x,0.5,abs_y,0.5,0,grad);Mat img_bin;threshold(grad,img_bin,0,255,CV_THRESH_BINARY |CV_THRESH_OTSU);
*///二值化Mat gray_bi;threshold(gray,gray_bi,0,255,CV_THRESH_OTSU);//灰度拉伸
//	Mat equ_img;
//	equalizeHist(gray_bi,equ_img);float num[256], p[256],p1[256];memset(num,0,sizeof(num));// 清空三个数组memset(p,0,sizeof(p));memset(p1,0,sizeof(p1));long wMulh = gray_bi.cols * gray_bi.rows;for (int i = 0; i < gray_bi.cols; i++){for (int j = 0; j < gray_bi.rows; j++){int v = gray_bi.at<uchar>(j,i);num[v]++;}}for (int i = 0; i < 256; i++)//存放图像各个灰度级的出现概率{p[i] = num[i] / wMulh;}for (int i = 0; i < 256; i++)//求存放各个灰度级之前的概率和{for (int k = 0; k <= i; k++){p1[i]+=p[k];}}for (int x = 0; x < gray_bi.cols; x++){for (int y = 0; y < gray_bi.rows; y++){int v = gray_bi.at<uchar>(y,x);gray_bi.at<uchar>(y,x) = p1[v]*255 + 0.5;}}//边缘增强Mat gray_c;Canny(gray_bi,gray_c,50,150,3);//水平投影和垂直投影int imgR[400] = {0};bool tag = false;int imgTop =0;int imgBottom = 0;int img_h1,img_h2;for (int ht = 0; ht < gray_c.rows; ht++){for (int wt = 0; wt < gray_c.cols; wt++){if (gray_c.at<uchar>(ht,wt) != 0){imgR[ht]++;}}if ( (!tag)&& imgR[ht] > 10){img_h1 = ht;tag = true;}if (tag && imgR[ht] <10){img_h2 =ht;tag = false;}if (img_h2-img_h1<50 && img_h2 - img_h1 >25){imgTop = img_h1;imgBottom = img_h2;break;}}int imgR_w[300] = {0};int img_w1 = 0;int img_w2 = 0;int imgRight = 0;int imgLeft = 0;bool tag2 = false;for (int wt_new = 2; wt_new < gray_c.cols; wt_new++){for (int ht_new = imgTop; ht_new < imgBottom; ht_new++){if (255 == gray_c.at<uchar>(ht_new,wt_new)){imgR_w[wt_new]++;}}if ( (!tag2) && (imgR_w[wt_new-2] +imgR_w[wt_new-1] + imgR_w[wt_new])/3 > 5){img_w1 = wt_new;tag2 = true;}if (tag2 && (imgR_w[wt_new-2] +imgR_w[wt_new-1] + imgR_w[wt_new])/3 <5){img_w2 =wt_new;tag2 = false;}if (img_w2 - img_w1 <180 && img_w2-img_w1 > 70){imgLeft = img_w1;imgRight = img_w2;break;}}Mat imgroi;//imgroi = car_img( Rect(imgTop,imgLeft,imgRight-imgLeft,imgTop-imgBottom));imgroi = car_img( Rect(imgLeft,imgTop+10,115,30));////第三部分:字符分割//二值化Mat roi_g, roi_bi;cvtColor(imgroi,roi_g,CV_BGR2GRAY);threshold(roi_g,roi_bi,0,255,CV_THRESH_OTSU);bool lab = false;	//是否进入一个字符分割状态bool black = false;	//是否发现黑点bool change = false;int xnum = 0;int rect_left;int rect_right;//Rect selection;Mat fg1,fg2,fg3,fg4,fg5,fg6,fg7;for (int wt = 0; wt < roi_bi.cols; wt++){int count = 0;for (int ht = 0; ht < roi_bi.rows; ht++){if ((255==roi_bi.at<uchar>(ht,wt)) && (!change)){count++;change = true;}else if((0==roi_bi.at<uchar>(ht,wt))&&(change)){count++;change = false;}}if (!lab&&(count>5)){rect_left = wt - 3;lab = true;}if (lab&&(count<5)&&(wt>(rect_left+8))&& (xnum<7) ){rect_right = wt + 2;lab = false;CvPoint pt1,pt2;pt1.x = rect_left;pt1.y = 0;pt2.x = rect_right;pt2.y = roi_bi.cols-1;int s_x = pt1.x + 1;int s_y = pt1.y;int s_width = rect_right - rect_left +1;int s_height = roi_bi.rows -1;if (xnum == 0){fg1 = roi_bi(Rect(s_x,s_y,s_width,s_height));} if (xnum == 1){fg2 = roi_bi(Rect(s_x,s_y,s_width,s_height));}			if (xnum == 2){fg3 = roi_bi(Rect(s_x,s_y,s_width,s_height));}			if (xnum == 3){fg4 = roi_bi(Rect(s_x,s_y,s_width,s_height));}if (xnum == 4){fg5 = roi_bi(Rect(s_x,s_y,s_width,s_height));}if (xnum == 5){fg6 = roi_bi(Rect(s_x,s_y,s_width,s_height));}if (xnum == 6){fg7 = roi_bi(Rect(s_x,s_y,s_width,s_height));}xnum++;}}cvWaitKey(50000);return 0;
}

本文地址:http://blog.csdn.net/linj_m/article/details/17761655

更多资源请 关注博客:LinJM-机器视觉 微博:林建民-机器视觉




http://chatgpt.dhexx.cn/article/JaVYctIj.shtml

相关文章

基于matlab的车牌识别

20221126 新增 首先说一下这个工程的思路&#xff0c;很多朋友妄想直接拿着工程用&#xff0c;那是不可能的&#xff0c;自己学去叭&#xff0c;我是先将车牌号预处理之后&#xff0c;整个图片干净一点之后&#xff0c;进行每个字符的切割&#xff0c;但是是很投机取巧的方法&a…

车牌识别步骤及部分代码

目录(?)[-] 车牌预处理字符分割归一化处理细化处理字符特征提取神经网络训练车牌图像识别结果测试 1.车牌预处理 车牌预处理过程的好坏直接影响到车牌图像进行后期处理过程&#xff0c;比如车牌字符分割等。车牌预处理也是尽可能的消除噪声&#xff0c;减少后期处理带来的不必…

车牌识别(毕业设计+代码)

简介与效果 用python3opencv3做的中国车牌识别&#xff0c;包括算法和客户端界面&#xff0c;只有2个文件&#xff0c;一个是界面代码&#xff0c;一个是算法代码&#xff0c;点击即可出结果&#xff0c;方便易用&#xff01; 大致的UI界面如下&#xff0c;点击输入图片&#…

真香!用Python检测和识别车牌(附代码)

车牌检测与识别技术用途广泛&#xff0c;可以用于道路系统、无票停车场、车辆门禁等。这项技术结合了计算机视觉和人工智能。 本文将使用Python创建一个车牌检测和识别程序。该程序对输入图像进行处理&#xff0c;检测和识别车牌&#xff0c;最后显示车牌字符&#xff0c;作为…

写好 API 接口文档,想清楚这几点

我在开始一个新的接口之前&#xff0c;需要进行以下判断&#xff1a; 请求协议是不是 HTTP、https? 请求体和响应格式是什么&#xff08;XML、JSON、FormData、Raw&#xff09;? API 是不是 RESTful 风格&#xff1f; 如果上面三个问题的答案都清楚了&#xff0c;就可以开…

API调用,API传参,面向对接开发,你真的会写接口文档吗?

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e2; &#x1f497; 你正在阅读 【梦想橡皮擦】 的博客 &#x1f44d; 阅读完毕&#xff0c;可以点点小手赞一下 &#x1f33b; 发现错误&#xff0c;直接评论区中指正吧 &#x1f4c6; 橡皮擦的第…

接口接口文档

一、接口简介 API&#xff08;Application Programming Interface&#xff09;即应用程序接口&#xff0c;可以任务是一个软件组件或一个Web服务与外界进行交互的接口&#xff0c;在这里接口可以和API划等号。 接口可以简单的理解为一个黑盒子&#xff0c;从一边输入参数&…

【编程规范】 后端API接口设计编写与文档编写参考

文章目录 0 统一规范0.1 理清业务流程0.2 定义前后端开发的接口规范0.3 定义接口文档 1 后端接口编写1.0 后端接口介绍1.0.1 接口交互1.0.2 返回格式1.0.3 CODE状态码1.0.4 Message&#xff08;Msg&#xff09;1.0.5 DATA 1.1 数据校验与异常处理1.1.1 参数校验设计1.1.2 全局异…

如何根据接口文档,写一个接口类(举例说明)

直接先贴图&#xff08;文中的url做了修改&#xff09;&#xff1a; 代码如下&#xff1a; FeignClient(name "Z_KPI_API",url "${Z.KPI.url:http://11.11.111.111:8080}",fallback ZApiClientFallback.class) public Interface ZApiClient{PostMappin…

Postman写接口文档

文章目录 参考首先了解什么是postman那么后端如何用postman编写接口文档&#xff0c;并且让前端或者其他开发人员也一起同步协作呢&#xff1f;1. 注册登录postman2. 创建项目工作区3. 编写接口文档4. 添加协作人&#xff08;这一步也可以在创建工作区之初就操作&#xff09;5.…

怎么写一份好的接口文档?

编写一份优秀的接口文档会让软件开发中变得更加轻松&#xff0c;更有效率。这可是关键任务&#xff0c;写得好不仅可以帮助开发人员更好地理解和使用 API 接口&#xff0c;还可以提高整个团队的协作效率。 大家可以在线感受一下优秀的接口文档是怎样的&#xff1a;https://pets…

接口文档的使用

请求参数&#xff08;(GET方法就是Query参数&#xff0c;POST方法就是 Body参数) 在 axios中通过headers选项设置Headers请求头参数 在 axios中通过data选项设置Body请求体参数 在 axios中通过params选项设置Query参数 如果有 Query 参数&#xff0c;axios 会在内部把这个对象…

接口文档包含哪些内容?怎么才能写好接口文档?十年测试老司机来告诉你

目录 接口文档结构 参数说明 示例 错误码说明 语言基调通俗易懂 及时更新与维护 总结 那么我们该如何写好一份优秀的接口文档呢&#xff1f; 接口文档结构 首先我们要知道文档结构是什么样子的。接口文档应该有清晰明确的结构&#xff0c;以便开发人员能快速定位自己需…

详解接口文档的编写

正规的团队合作或者是项目对接&#xff0c;接口文档是非常重要的&#xff0c;一般接口文档都是通过开发人员写的。一个工整的文档显得是非重要。下面我总结下自己看到的优秀接口文档。 一、背景介绍 接口&#xff1a;API API&#xff08;Application Programming Interface&…

如何写好API接口文档

日常项目开发的过程中&#xff0c;接口文档是必不可少的。后端工程师与前端工程师之间需要接口文档来定义数据传输协议、系统对外暴露接口需要文档来说明、系统之间相互调用需要文档来记录接口协议等等。对于一个完整的项目&#xff0c;接口文档是至关重要的。那我们如何写好一…

什么是接口文档

一、什么是接口文档&#xff1f; 在项目开发中&#xff0c;web项目的前后端分离开发&#xff0c;APP开发&#xff0c;需要由前后端工程师共同定义接口&#xff0c;编写接口文档&#xff0c;之后大家都根据这个接口文档进行开发&#xff0c;到项目结束前都要一直维护。 二、为…

如何正确规范写接口文档

前言 正规的团队合作或者是项目对接&#xff0c;接口文档是非常重要的&#xff0c;一般接口文档都是通过开发人员写的。一个工整的文档显得是非重要。下面我将我看到的一篇接口文档做一个总结 开始吧&#xff01;&#xff01;&#xff01; 接口1&#xff1a; 查询排重接口 接…

接口文档编写步骤与格式

接口文档编写步骤与格式 1. 基本步骤 梳理需求 依据业务写汉字版的接口文档。&#xff08;可以减少在实际开发过程中的数据库调整&#xff09; 写接口文档的过程中&#xff0c;会联想到需要上面样的数据。进而推出数据库设计。 数据库设计完毕。 正式的接口文档。 前端和后…

什么是接口文档,如何写接口,有什么规范?

一、什么是接口文档&#xff1f; 在项目开发中&#xff0c;web项目的前后端分离开发&#xff0c;APP开发&#xff0c;需要由前后端工程师共同定义接口&#xff0c;编写接口文档&#xff0c;之后大家都根据这个接口文档进行开发&#xff0c;到项目结束前都要一直维护。 二、为…

前后端接口文档编写

前后端分离需要编写接口文档&#xff0c;便于前后端工程师根据接口调用数据库和系统的功能。 接口分为四部分&#xff1a;方法、uri、请求参数、返回参数 1、方法&#xff1a;新增post、修改put、获取get、删除delete 2、uri:以"/"开头定义接口 3、请求参数和返回参…