【matlab】车牌识别实现探讨

这个是去年完成一个课程大作业时利用课余时间完成的matlab车牌识别程序,虽然本科阶段也接触过《数字图像处理》这门课,但是当时的学习也停留在了比较浅的阶段,又一直对这个课题挺感兴趣的,于是提交题目时就报了车牌识别。

很早之前的东西了,相对于现在的智能算法以及计算机视觉相关方面的研究,肯定是不够看的,不过这样接触一下也是挺有意思的。

正文

什么背景了意义了就不多说了,网上一大堆,这里直接进入正题:

首先,当初一开始也没想从头开始,先在网上找了相关的资料,其中也参考了csdn上的以下资源:

以及一个开源的中文车牌识别系统 EasyPR,一开始的时候想尝试来着,车牌搜集资料时,用了接近两天的时间,尝试在64维WIN10以及Visual Studio 2019 –OpenCV 3.48环境下,调试前面第一个具有学习网络的中文车牌识别系统。

从配置OpenCV编程环境→用小程序测试→下载开源项目→学习手册→载入项目→配置参数→消错补缺→回溯OpenCV版本→配置调试→回溯VS版本→交叉变量调试→陷入调试死循环。其间不断查资料调bug,不断地尝试,最后还是放弃深入学习了。不过作者本身的意思路是没有问题的,在matlab上也可以实现准确率较高的测试。

基本步骤

大体上,车牌识别可以分为以下几个基本环节:

  1. 图像预处理
  2. 车牌初步定位
  3. 图像水平、垂直倾斜角度调整
  4. 字符分割及归一化
  5. 字符识别显示结果

具体内容

要是需要上面相关文字叙述的话,也可以在网上找到很多例子,这里直接讲我觉得还比较实用的算法思想,完整的代码会在最后给出。

1.准备工作

搜集整理了不同样式共50张车牌图片,统一编号存放待用:

在程序中实现手动选择测试样本,使得在程序运行后,能够主动打开窗口要求选取需要测试的图片,并且生成对应的展示窗口,具体的语句不再详细描述,原本的注释加上我后来学习时的看懂问题应该不大

%景象匹配与目标识别大作业程序
%2019年12月13日
%Jonny Su
%根据hsv色彩定位法定位车牌,将图像转为HSV模型,获取每个像素点的HSV值,判断是否为蓝色像素点,统计每行每列蓝色像素点的个数,从而获取车牌位置
%根据该位置对原始彩色图像进行剪裁,并进行二值化,滤波、去点处理,为字符分割做准备
%按顺序切割出7个字符,并保存为7张图片
%分别提取图片和模板进行比较,将比较结果最接近的确定为对应模板,并最终显示车牌识别结果
cd D:\AEEEE\Course\图像处理&景象匹配\大作业\program\景象匹配\测试图片	%转换到自己的路径下
% path(path,'测试图片')   %修改文件的搜索路径
[pictureName] = uigetfile('*.jpg', '请选择图片');    %手动选取,更直观
tic;            %开始计时
I=imread(pictureName);                      %读入车牌照片
A=isstrprop(pictureName,'digit');   %存储具有编号信息的位置
B=pictureName(A);   %提取编号信息
C=str2double(B);    %将编号转换为字符串
figure(1),imshow(I),title(['这是',num2str(C),'号样本的原图']);  %绘图,并且标注是几号测试对象

代码效果:
在这里插入图片描述

2.根据HSV色彩定位法定位车牌

将图像转为HSV模型,获取每个像素点的HSV值,判断是否为蓝色像素点,统计每行每列蓝色像素点的个数,从而获取车牌位置。

在这里插入图片描述

  1. 函数imread支持多数流行的图像/图形格式,包括JPEG、JPEG2000和TIFF。使用函数imread可将图像读入MATLAB环境,该函数的基本语法为
imread(‘filename’)
  1. 使用im2double函数可将图像数据格式转为double类。对应的值会归一化到范围[0,1]。
%%  %%%%根据hsv色彩定位法定位车牌%%%%
cd D:\AEEEE\Course\图像处理&景象匹配\大作业\program\景象匹配\字符模板 
Image=im2double(I); %把图像数据类型转换为double类型 
Image=rgb2hsv(Image); %将RGB图像转化为hsv模型,H色相,S饱和度,V亮度 
[y,x,~]=size(Image);  
Blue_y = zeros(y, 1);  
p=[0.56 0.71 0.4 1 0.3 1 0];    %蓝色点范围
for i = 1 : y  
    for j = 1 : x  
        hij = Image(i, j, 1);  %取出每个像素点的H、S、V值
        sij = Image(i, j, 2);  
        vij = Image(i, j, 3);  
        if (hij>=p(1) && hij<=p(2)) &&( sij >=p(3)&& sij<=p(4))&&(vij>=p(5)&&vij<=p(6)) %若像素点的HSV处于蓝色的HSV范围 
            Blue_y(i, 1) = Blue_y(i, 1) + 1;%统计每行的蓝色像素点数目   
        end  
    end  
end  
[~, MaxY] = max(Blue_y);%获取蓝色像素点最多的行号 

运行结果:

在这里插入图片描述

3.根据Radon变换理论调整倾斜角度

Radon变换的本质是将原来的函数做了一个空间转换,即,将原来的XY平面内的点映射到AB平面上,那么原来在XY平面上的一条直线的所有的点在AB平面上都位于同一点。记录AB平面上的点的积累厚度,便可知XY平面上的线的存在性。

详细的数学理论不再赘述可以参考这篇文章, 总结一句来说,randon变换就是在其不倾斜的时候水平轴上的各点的线积分。

  • **水平倾斜角度的调整:**先将图像逆时针旋转90°,当对于倾斜的图像进行radon变换之后,变换之后的结果在其倾斜角度的位置表现出最大的落差。因此,可以通过一阶微分函数对其进行求导然后求出其绝对值的累加和,寻找得到最大值的角度就是索要求得角度,再通过旋转变换imrotate得到修正后的图形
  • **竖直倾斜角度的调整:**对于竖直角度,我们采用和水平角度一样的方式,求radon变换,然后求其一阶导数绝对值的累加和,最大值就是我们所要求的倾斜角度,不过和水平不一样的是,竖直方向其实是同一行的元素之间的错位偏移
%% %%%%车牌倾斜角度的调整%%%%
% pictureOut = rotate(I2)
% I2= rgb2gray(I2);
%水平方向调整
T=affine2d([0 1 0;1 0 0;0 0 1]);
pictureTr=imwarp(I2,T);              % 图像转置,顺时针旋转90°调整水平方向
theta = -20 : 20;                                          %设置倾斜角度的范围
r1 = radon(pictureTr, theta);                        %radon变换确定倾斜角
result1 = sum(abs(diff(r1)), 1);                      %求出行倒数绝对值的累加和,最大的对应倾斜角
rot1 = find(result1==max(result1))-21;
pictureRo = imrotate(I2, rot1);
%     figure, imshow(pictureRo), title('调整水平角度之后的图像');

运行结果:

在这里插入图片描述

4.图像预处理

  1. 根据如下图所示的实际车牌比例,来去除边框。在程序中设置的数值则依据图中所标记的标准数值

  2. 使用rgb2gray函数可将图像转换为灰度图。

  3. 使用bwareaopen函数,可以删除二值图像中面积小于一个定值的对象,默认情况下使用8邻域。

在这里插入图片描述

程序框图:

在这里插入图片描述

边界校正:

%%  %%%%字符分割前的图像处理%%%%
%边界校正
% [y,x,z]=size(pictureLo);%I2有y行x列
[y,x,z]=size(I2);%I2有y行x列
PX1=round(x*5/440); %根据实际车牌比例,将边框部分去除 经验值
PX2=x-round(x*5/440);
PY1=round(y*16/140);
PY2=y-round(y*16/140);
fprintf('校正后  左边界=%d、右边界=%d、上边界=%d、下边界=%d',PX1,PX2,PY1,PY2);
%彩色图像车牌部分截取
dw=I1(PY1:PY2,PX1:PX2,:);%对边界进行截取
% dw= pictureLo; 
figure(4),imshow(dw),title(['对',num2str(C),'号进行边界校正结果']);

得到灰度图:

imwrite(dw,'dw.jpg');%把截取后的彩色图像新创建一张图片,命名为dw
a=imread('dw.jpg');
b=rgb2gray(a);      %灰度图像
% b=a;      %灰度图像
imwrite(b,'1.车牌灰度图像.jpg');

得到二值图像:

g_max=double(max(max(b)));%得出剪裁后灰度图矩阵的最大值,并变成双精度浮点型
g_min=double(min(min(b)));%得出剪裁后灰度图矩阵的最小值,并变成双精度浮点型
T=round(g_max-(g_max-g_min)/2); % T为二值化的阈值,round用于取整
d=(double(b)>=T);  % d:二值图像
imwrite(d,'2.车牌二值图像.jpg');
figure(5),subplot(3,1,1),imshow(d),title([' ',num2str(C),'号车牌的二值图像']);

均值滤波:

% 滤波
h=fspecial('average',3);%创建一个二维滤波器,average是类型,3是参数
d=im2bw(round(filter2(h,d)));%filter2进行滤波处理,im2bw使用阈值变换法把灰度图像转换成二值图像
imwrite(d,'4.均值滤波后.jpg');

去除原点:

%去点处理
d=cut(d);       %子程序内
[m,n]=size(d);  
d(:,round(n*122/430):round(n*137/430))=0;%去除中间的点
d=bwareaopen(d,65);%用于删除二值图像中面积小于一个定值(此处为65)的对象,默认情况下使用8邻域
figure(5),subplot(3,1,2),imshow(d),title([' ',num2str(C),'号车牌的去点处理']);

边缘剪切:

%上下边框处理,找到上下边界处像素值小于20的行,并将整行设为零
s=zeros(1,m);
i=1;
while i<=m
    s(i)=sum(d(i,:));
    i=i+1;
end
j=0;c=zeros(1,m);%c矩阵用于记录下边框处像素值小于20的行,用于确定边框与字符间空隙的位置
while j<m
    while s(j+1)>20&&j<m-1
        j=j+1;
    end
    if j<round(m*12/140) %如果是上边框
        d((1:j),:)=0;
    else j>round(m*115/140)%如果是下边框
        c(j)=j;%将下边界处理中像素值小于20的行记录下来
        d(j,:)=0;
    end
    j=j+1;
end
jj=round(m/2);%这里是为了找到下边界处理中,被记录下来的最小行(即边框和字符间空隙的上沿),所以从中间开始往下找,直到找到的第一个非零数
while c(jj)==0
      jj=jj+1;
end
d((jj:m),:)=0;%将这一行以下皆设为0
d=cut(d);
figure(5),subplot(3,1,3),imshow(d),title([' ',num2str(C),'号车牌的上下边框处理']);
imwrite(d,'5.切割前.jpg');

运行结果:

在这里插入图片描述

5.字符分割

第一个M函数cut()函数:

在这里插入图片描述
第二个子函数extract()函数:

在这里插入图片描述
剩余字符的分割,直接依次调用extract函数即可。

部分程序:

%%  %%%%字符分割%%%%
[~,n]=size(d);
% 切割出 7 个字符
% 第一个字符,
y1=n*15/440;y2=0.25;flag=0;word1=[];
while flag==0
    [m,~]=size(d);
    left=1;wide=0;
    while sum(d(:,wide+1))~=0 % 找到像素和为零的列
        wide=wide+1;
    end
    if wide<y1   % 若这一列的位置在图像左侧前10列之内则认为是左侧干扰
        d(:,(1:wide))=0;%把左侧干扰全部变为0,并将其切割掉
        d=cut(d);
    else
        temp=cut(imcrop(d,[1 1 wide m]));%将一个字符剪切下来
        [m,~]=size(temp);
        all=sum(sum(temp));%第一个字符的像素值总和
        two_thirds=sum(sum(temp((round(m/3):2*round(m/3)),:)));%单个字符中段1/3图像的像素值总和
        if two_thirds/all>y2%验证是否为字符
            flag=1;word1=temp;   %获取第一个字符word1
        end
        d(:,(1:wide))=0;d=cut(d);%当提取第一个字符结束或者不满足上述条件时,将该区域变为0,并切除
    end
end

% 分割出第二个字符
[word2,d]=extract(d);

运行结果:
在这里插入图片描述

6.字符识别

最常用的就是模板匹配法,肯定没有EasyPR那样的准确度和智能,但是好处是不需要太多的样本

  1. 首先建立标准的字符模板库(共65个字符模板),字符尺寸统一使用 。字符模板统一命名为“字符模板”+“字符代码”,如字符模板1、字符模板鄂、字符模板A等.
  2. 建立自动识别字符的代码表,采用字符类数组char。
  3. 在进行匹配前必须先进行字符图像归一化,使图像字符大小跟模板图像大小一致。使用imresize函数可对图像进行缩放处理。此处缩放与字符模板保持一致,统一使用 。
  4. 使用corr2函数计算分割出的每个字符与字符模板库中的每个字符的相关系数大小,并保存在一个数组中。
  5. 使用find函数找出相关系数最大值所在的位置,从而按照顺序,在自动识别字符代码表中找到对应的字符,并输出便实现了字符的识别。

字符模板:

在这里插入图片描述
程序框图:

在这里插入图片描述
建立初始字符串变量:

%%  %%%%字符识别%%%%
%建立自动识别字符代码表  
liccode=char(['0':'9' 'A':'H' 'J':'N' 'P':'Z' '藏川鄂甘赣桂贵黑沪吉冀津晋京辽鲁蒙闽宁青琼陕苏皖湘新渝豫粤云浙']);  %建立自动识别字符代码表  
SubBw2=zeros(40,20);

运行结果:

在这里插入图片描述

7. 输出结果实现编号

在这个程序中,因为样本本身比较多,虽然样本自己带有固定格式的编号,一开始也是手动选取的,但是难免会有程序跑了一会然后发现忘了自己在测那张图的时候,而且在写报告展示的时候如果能直接从结果中看到测试的编号,会省事不少,其实在这个程序的修改中自己也有穿插这方面的实现代码。

在第一开始的准备工作中,调用的测试样本不同格式的信息也被存储到变量A ,B ,C 当中:

[pictureName] = uigetfile('*.jpg', '请选择图片');    %手动选取,更直观
tic;            %开始计时
I=imread(pictureName);                      %读入车牌照片
A=isstrprop(pictureName,'digit');   %存储具有编号信息的位置
B=pictureName(A);   %提取编号信息
C=str2double(B);    %将编号转换为字符串

之后在绘图的过程中,只需要在title命令里加入特定的语句,就可以实现每个测试样本都有带相应编号的标题,方便测试:

figure(1)
imshow(I);
title(['这是',num2str(C),'号样本的原图']);  %绘图,并且标注是几号测试对象

不过这里需要注意title后面的格式比平时用的多了一个括号[],以及num2str©以外的内容都用单引号括住再用逗号分开
运行结果:

在这里插入图片描述
以上就是程序中比较关键的内容

结论

车牌识别系统的关键技术是车牌区域定位,字符分割和字符识别。

运用基于蓝色象素点统计特性的方法对车牌是蓝色的车牌进行定位,对车牌定位准确率较高。

本程序所采用的二次水平投影分析和阈值技术有效检测了车牌图像的上下左右边框、旋转角度,准确实现的车牌字符的分割,对多个车牌进行实验,均有较高的正确率。

模板匹配法虽然识别率低,但实现简单,计算量小,只需计算模板字符与待识别字符的相关系数的大小进行比较即可,而且车牌字符是有阿拉伯数字,英文大写字母,还有部分汉字,虽有字库量不大,字符较规整时对字符图像的缺损、污迹干扰适应力强且识别率较高。

本次组对50幅包含车牌的图像进行了程序验证,其中完全识别无误的有28幅,大部分能识别的有8幅,不能识别的有14幅。识别率在72%左右,运行结果较理想。不过有条件的话,可以进一步改进,提高识别率。

程序之间的对比,其中前面的几个就是从网上找的其他版本的车牌识别程序,在文章一开头也做了声明,都调通后大致分了下类然后做了对比:

在这里插入图片描述

结尾

学习心得:

  • 本身因为专业和导师,做控制方向比较多,但是此次的经历让我对图像处理和计算机视觉方面也有了一定的了解;
  • 在编程过程中,重要的是心中的逻辑性、符合计算机语言处理的思考,以及平日里真正的操作和积累;
  • 在网络上有很多值得学习和讨论的新的算法和资源,如何去将自己的时间抓住,利用好能够得到的资源提升自身的能力,跟紧前沿的技术和算法的进步,是值得学习的点。

写在最后:程序有借鉴也有自己思考的东西,仅供参考,如果前面声明还不够的话参考程序的作者可以私聊沟通。主要目的是为了互相学习共同进步,也激发新的思考。

发布了8 篇原创文章 · 获赞 0 · 访问量 1569

猜你喜欢

转载自blog.csdn.net/qq_39295220/article/details/104630793