Matlab车牌识别全流程:预处理、分割与模板匹配实战解析
简介:基于Matlab的车牌识别课程设计报告以docx文档形式提供,内容覆盖车牌识别系统的完整设计流程:从系统组成、车辆检测与图像采集,到车牌定位、字符分割与字符识别算法,并配有详细设计步骤、流程图和Matlab源代码片段,适合信息科学与工程专业学生完成课程设计或进行图像处理、模式识别实践。报告重点阐述了垂直投影法字符分割、模板匹配与神经网络识别等核心方法,同时包含开运算背景分离、二值化等预处理实现,可帮助读者理解车牌识别从图像输入到号码输出的完整链路。资源共1个docx文件,压缩包大小约1.37MB,属于轻量级文档型资料;已有116人学习下载,在同主题课程设计报告中有一定参考价值。读者可用于对照自己的设计方案、补充算法原理说明,也可借鉴其中的Matlab代码快速搭建车牌识别原型系统。
1. 这份Matlab车牌识别课程设计:定位、分割、识别一条龙
车牌识别是图像处理课设里的常青树,很多学生第一次接触“图像处理到底怎么落地”就是从这个题目开始的。这套Matlab车牌识别课程设计报告,从一张普通照片出发,把灰度化、背景相减、二值化、Canny边缘检测、形态学开闭运算、区域宽高比筛选、倾斜校正、投影分割、模板匹配这些环节完整走了一遍,报告文档和源代码都是齐的。它的价值不在于识别率有多高,而在于把“定位-分割-识别”这条链路讲得可以一步步复现。适合两类人:一是刚拿到课设题目、想快速跑通全流程的学生;二是工作中需要处理图像定位和字符分割、想借一套现成思路的从业者。下面我把这条链路拆开讲,重点落在参数怎么设、哪几步容易翻车。
2. 图像预处理与二值化:灰度化、背景相减和阈值公式拆解
报告从imread('3.jpg')读入图像开始,但真正决定后续成败的是预处理阶段三个操作:灰度化、开运算背景估计、减法增强。这三个操作合起来做了一件事:把一张光照复杂、背景杂乱的照片,变成“亮字符、暗背景”的理想二值图。
2.1 灰度化与背景估计:为什么要先做一次开运算
彩色图像三通道信息量大,直接处理会拖慢速度,更重要的是车牌识别本身依赖的是亮度和形状特征,而不是颜色。rgb2gray是Matlab标准灰度化接口,内部按亮度加权公式0.299R + 0.587G + 0.114B对三通道加权求和。这一步没有玄学,就是降维提速。
Scolor = imread('3.jpg'); % 读入彩色原图 Sgray = rgb2gray(Scolor); % RGB转灰度,后续所有处理都在单通道上进行 figure, imshow(Sgray), title('原始黑白图像'); s = strel('disk', 13); % 圆盘结构元素,半径13 Bgray = imopen(Sgray, s); % 开运算估计背景亮度 figure, imshow(Bgray), title('背景图像');imopen是形态学开运算,先腐蚀后膨胀。腐蚀把细小的亮目标抹掉,膨胀再把大尺度的背景结构恢复回来。disk半径13的意思是:直径26像素以下的亮细节会被腐蚀掉,只留下缓慢变化的背景亮度场。这个13是经验值,和图像分辨率强相关。如果原图是千万像素级别的大图,13可能偏小,背景里会残留车体轮廓;如果是三四百像素的小图,13可能偏大,连车牌底板都被“背景化”了。
Egray = imsubtract(Sgray, Bgray); % 原图减背景,突出比背景亮的目标 figure, imshow(Egray), title('增强黑白图像');imsubtract把原图灰度值和背景估计值逐像素相减。实际效果是:车身、路面这类缓变区域被压暗,而白色车牌字符、车牌底板边缘这类比局部背景亮的目标被保留下来。报告把这步叫“图像增强”,本质是高通滤波的形态学版本。
2.2 背景相减与阈值二值化:level 那行代码的来历
增强之后就要二值化。报告里这段代码是很多人看不懂的地方:
fmax1 = double(max(max(Egray))); % 增强图的最大灰度值 fmin1 = double(min(min(Egray))); % 增强图的最小灰度值 level = (fmax1 - (fmax1 - fmin1)/3) / 255; % 阈值落在动态范围高端1/3处 bw22 = im2bw(Egray, level); % 大于level置1,小于level置0 bw2 = double(bw22); % 转成double方便后续计算 figure, imshow(bw2), title('图像二值化');把 level 的公式展开:level = (max - (max-min)/3) / 255,也就是(min + 2*(max-min)/3) / 255。意思是阈值取在灰度动态范围从低到高的 2/3 处,换句话说,只保留灰度最高的那 1/3 区域。为什么这么设?因为在增强图里,车牌字符是整幅图最亮的目标,取高端阈值能最大限度把字符和亮噪点分开。除以 255 是因为im2bw的 level 参数必须是 [0,1] 区间的归一化值。
这个公式是典型的工程经验值,不是统计意义上的最优阈值。它隐含一个假设:字符比背景亮,而且亮得足够多。光照条件一变,这个阈值就会飘。比如强逆光下车身反光,max 被高光点拉高,阈值跟着升高,真字符可能被误杀;阴天整体灰度偏低,动态范围被压缩,阈值可能过低,路面纹理全变成前景。所以复现时第一件事是打印fmax1、fmin1和level三个值,看二值图里车牌字符是不是完整白色。
2.3 Canny 边缘检测与形态学滤波:让车牌区域连成块
二值图里字符是实心的白色块,直接做区域标记会出现一个字符被算成多个连通域的情况。报告的处理是先对二值图做边缘检测,把实心块变成轮廓,再用形态学操作把轮廓重新焊接成车牌形状。
grd = edge(bw2, 'canny'); % Canny边缘检测,提取二值图轮廓 figure, imshow(grd), title('图像边缘提取'); bg1 = imclose(grd, strel('rectangle',[5,19])); % 闭运算:焊接口字断裂 figure, imshow(bg1), title('图像闭运算[5,19]'); bg3 = imopen(bg1, strel('rectangle',[5,19])); % 开运算:去掉小噪点 figure, imshow(bg3), title('图像开运算[5,19]'); bg2 = imopen(bg3, strel('rectangle',[19,1])); % 竖直细长条开运算:按高度筛 figure, imshow(bg2), title('图像开运算[19,1]');这里三个结构元素各有分工。第一个是[5,19]矩形,宽19高5,横向长纵向短。闭运算先把字符轮廓内部断裂和字符间的小缺口焊起来,形成横向条带;接着同样的矩形做开运算,把比这个尺寸小的孤立噪点清掉。第三个[19,1]是宽1高19的竖直细长条,开运算要求图中结构能完整容纳一个19像素高的竖条才保留,于是细横线、短划痕这类噪声被剔除,而车牌字符行因为高度接近19像素被保留。
这组5和19的数字不是随便拍的,它们对应的是“字符在小分辨率图像里的线宽量级”。如果你的图像采集距离远、车牌在画面里占比小,19这个数可能要缩到12甚至8;如果车牌占据画面很大,19会不够用,字符行会被误删。改参数的标准只有一个:让处理后只剩车牌区域这个连通块,车身其他纹理全部消失。
3. 车牌候选区域筛选:区域标记、宽高比与先验知识
形态学做完,图像里剩下的白色连通块通常不止车牌一个——车灯、保险杠上的反光条、车身文字都可能残留。这一步要把真正的车牌区域从这些候选块里挑出来,靠的是两个先验:连通域面积和宽高比。
3.1 bwlabel 与区域特征统计:老接口 imfeature 的替代方案
报告里用的是bwlabel加imfeature,前者标记连通域,后者计算区域特征。问题是imfeature在Matlab新版本里已经被移除了,直接跑会报“未定义函数”。必须换成regionprops,字段名和返回结构基本一致。
[L, num] = bwlabel(bg2, 8); % 8连通标记,num是连通域个数 RGB = label2rgb(L, 'spring', 'k', 'shuffle'); % 伪彩色显示每个连通域 figure, imshow(RGB), title('图像彩色标记'); stats = regionprops(L, 'Area', 'BoundingBox'); Area = [stats.Area]; % 每个区域的像素面积 BoundingBox = reshape([stats.BoundingBox], 4, num)'; % 每行[x y width height]bwlabel的8表示8邻域连通,即上下左右加四个对角方向都算连通。对车牌字符和边框来说,8连通容易把断开的笔画重新接上,适合后续整体提取。regionprops返回的BoundingBox格式是[x, y, width, height],x和y是区域最小外接矩形的左上角坐标,width和height是宽和高,单位是像素。
label2rgb是纯可视化辅助,把每个连通域染成不同颜色,方便肉眼确认“到底哪些块被标记出来了”。这一步对调试非常有用,尤其当筛选结果不对时,先看彩色标记图能立刻判断是形态学参数的问题还是筛选阈值的问题。
3.2 宽高比筛选:把 3.14:1 用进 BoundingBox
车牌筛选最核心的规则是宽高比。我国蓝底白字车牌标准尺寸是440mm×140mm,宽高比约3.14:1。加上车牌边框、铆钉和字符区域的实际边界,二值化后提取的候选块宽高比通常会落在2.5到4.5之间。报告里这部分只画了流程图,没有给出具体阈值,实际操作时我一般用下面这组条件:
candidates = []; for k = 1:num w = BoundingBox(k,3); h = BoundingBox(k,4); ratio = w / h; % 车牌宽高比约3.14,放宽到2.5~4.5;面积下限防小噪点 if ratio > 2.5 && ratio < 4.5 && Area(k) > 500 candidates = [candidates; k, BoundingBox(k,:)]; end end注意Area的单位是像素个数,不是物理面积,所以下限500这个值完全取决于图像分辨率。更稳的做法是先把所有连通域的Area打印出来,看车牌区域和其他噪点的面积分布,取一个能明显分开两者的值。宽高比的上下限同样要结合图片里的车牌倾斜程度:如果车牌倾斜超过10度,外接矩形的宽高比会被拉大,上限4.5可能不够,建议打印所有候选块的宽高比再定阈值。
筛选逻辑还有一个隐藏点:如果二值化做得不好,车牌字符和底板没有被形态学焊成一个连通块,而是碎成几块,那么每一块的宽高比都不可能是3.14。这种时候不要急着调筛选参数,回头查第2章的形态学结构元素尺寸。
3.3 灰度子图与二值子图:为投影分析准备的输入
找到候选区域后,要用BoundingBox从原灰度图和二值图中分别裁剪出子图。报告里灰度子图叫subcol,二值子图叫sbw1,后面所有投影和分割都在二值子图上进行。
% 假设筛选出的最佳候选区为box = [x y w h] box = candidates(1, 2:5); % 取第一个候选区,实际应比较所有候选 subcol = imcrop(Sgray, box); % 从灰度图裁出车牌区域 sbw1 = imcrop(bw2, box); % 从二值图裁出对应区域 figure, subplot(1,2,1), imshow(subcol), title('灰度子图'); subplot(1,2,2), imshow(sbw1), title('二值子图');这里有个容易被忽略的细节:裁剪必须用同一个box同时作用于灰度图和二值图,保证两个子图严格对齐。灰度子图是给人看的,用来确认“这确实是车牌”;二值子图是给后续算法用的,投影、拟合、分割全部基于它。如果二值子图里字符连成一条黑带或者碎成麻点,不要继续往下走,先回头调整二值化阈值。
到这一步,图像已经从“一张照片”变成了“一块像车牌的二值区域”。接下来最考验代码功力的是投影分析和倾斜校正,这也是报告里代码最乱、变量最容易对不上的部分。
4. 倾斜校正与字符分割:投影峰谷分析的完整链路
投影分析的核心思想很朴素:把二值图像按行或按列累加白点数量,字符区域会在投影曲线上形成峰,字符间隙形成谷。问题是报告里markrow、markcol这一堆变量的赋值过程被省略了,直接照跑必然报错。下面这套是我按相同逻辑重写的等价实现,能直接跑通。
4.1 水平投影与峰谷分析:markrow 系列的现代写法
% sbw1为第3章得到的车牌二值子图 histrow = sum(sbw1, 2); % 水平投影:每一行白点数量求和 figure, bar(histrow), title('水平投影(含边框)'); % 投影序列转0/1:超过峰值20%的行算字符行 projMask = histrow > max(histrow) * 0.2; d = diff([0; projMask; 0]); % 前后差分找跳变沿 rowTop = find(d == 1); % 上升沿:字符带起始行 rowBot = find(d == -1) - 1; % 下降沿:字符带结束行sum(sbw1, 2)对每行求和,得到的是行号到白点数的映射。一个标准车牌的投影曲线从上到下应该是:上边框峰、字符峰群、下边框峰。max(histrow)*0.2这个阈值把投影曲线压成0/1序列,再用diff找前后状态变化的位置——从0变1是行带开始,从1变0是行带结束。
rowTop和rowBot就是报告里markrow和markrow1想表达的东西。如果这段代码跑出来rowTop和rowBot长度一大串,说明二值图里残留的噪声行太多,往下走之前先把第2章的形态学参数重新过一遍。
4.2 车牌旋转校正:poly1 拟合、弧度转角度与 imrotate
水平投影曲线如果峰谷不明显,比如字符行整片糊成一个宽峰,最常见的原因是车牌倾斜。倾斜车牌在水平投影上各字符行错位叠加,峰被抹平。报告的解法是线性拟合车牌边缘,算出倾角,再把图像转回来。
% 逐行找第一个白点坐标,拟合车牌上边缘直线 xdata = zeros(size(sbw1,1),1); ydata = (1:size(sbw1,1))'; for r = 1:size(sbw1,1) c = find(sbw1(r,:) == 1); if ~isempty(c) xdata(r) = c(1); % 该行第一个白点的列号 end end valid = xdata > 0; % 剔除全黑行 fresult = fit(xdata(valid), ydata(valid), 'poly1'); % 一阶多项式拟合 angle = atan(fresult.p1) * 180 / pi; % 斜率转角度,弧度换度 sbw = imrotate(sbw1, angle, 'bilinear', 'crop'); % 旋转校正 figure, imshow(sbw), title(['车牌旋转角: ', num2str(angle), '度']);fit是曲线拟合工具箱的函数,poly1表示拟合y = p1*x + p2这种一次直线,p1就是斜率,也就是tan(theta)。atan(p1)得到弧度,乘以180/pi转成角度。imrotate三参数分别是旋转角度、插值方式、是否裁剪。bilinear双线性插值比默认最近邻更能避免边缘锯齿,crop让输出图像和原图同尺寸。
这里有两个坑。第一,fit要求输入是列向量,xdata和ydata维度必须一致,所以要先剔除全黑行。第二,拟合出来的角度正负取决于车牌往哪边歪,不要手动把角度改成绝对值,直接交给imrotate就行,它会按反方向转正。
4.3 去边框与垂直投影:字符中心、字符宽度怎么算
旋转校正后重新做水平投影,这次的目标是精确去掉上下边框,拿到纯字符带。
histrow2 = sum(sbw, 2); projMask2 = histrow2 > max(histrow2) * 0.2; d2 = diff([0; projMask2; 0]); rowtop = find(d2 == 1); rowbot = find(d2 == -1) - 1; maxhight = max(rowbot - rowtop + 1); % 字符行高度 % 取最宽的字符带来裁剪 [~, idx] = max(rowbot - rowtop + 1); sbw2 = sbw(rowtop(idx):rowbot(idx), :); % 去掉上下边框的字符带 histcol = sum(sbw2, 1); % 垂直投影:每列白点数量 figure, bar(histcol), title('垂直投影(去水平边框后)');垂直投影的目标是找到每个字符的左右边界。字符列白点多形成峰,字符间隙形成谷。同样用跳变检测:
projCol = histcol > max(histcol) * 0.15; dCol = diff([0, projCol, 0]); colTop = find(dCol == 1); % 每个字符带的起始列 colBot = find(dCol == -1) - 1; % 每个字符带的结束列 width = colBot - colTop + 1; center = colTop + width / 2; % 字符中心位置 gap = diff(center); % 相邻字符中心距离 [~, maxGapIdx] = max(gap); % 最大间距通常出现在第一个字符之后max(gap)找的是相邻字符中心距离最大的位置。因为车牌第一个字符是汉字,宽度通常比后续字母数字窄,它和第二个字符之间的中心距会被拉大。报告里for k = findmax-1:findmax+5就是这个逻辑:从最大间距的前一个字符开始,连续取7个,正好覆盖整个车牌的7个字符位。如果你的图片里汉字本身很宽,或者有间隔符干扰,这个假设会失效,到时候打印center和gap人工核对。
4.4 切字符并归一化:22×14 标准子图的由来
拿到每个字符中心和最大字符宽度后,就可以切割了:
maxwidth = max(width); % 最大字符宽度 l = 1; for k = (maxGapIdx - 1) : (maxGapIdx + 5) cleft = center(k) - maxwidth / 2; % 切割左边界 cright = center(k) + maxwidth / 2 - 2; % 右边界,减2是避免重叠 cleft = max(cleft, 1); cright = min(cright, size(sbw2, 2)); SegBw1 = sbw2(:, cleft:cright); % 原始尺寸字符块 SegBw2 = imresize(SegBw1, [22 14]); % 归一化为22行14列标准子图 figure, imshow(SegBw2); l = l + 1; endmaxwidth是垂直投影里最宽字符的宽度,用它作为切割窗口的参考宽度,能保证每个字符都被完整框住。cleft和cright用字符中心加减半个最大宽度得到,最后用max、min把边界限制在图像范围内,防止越界。
22×14 是报告定的标准子图尺寸,22对应字符高度,14对应字符宽度。不同图像里车牌大小差异很大,直接拿原始尺寸去匹配模板没有任何意义,必须统一缩放。imresize默认最近邻插值,在缩放比例不是整数时容易产生锯齿,建议改成imresize(SegBw1, [22 14], 'bilinear')。
5. 字符识别:模板匹配、神经网络与失败案例复盘
字符分割完成,最后一步是识别。报告同时提到了模板匹配和BP神经网络两条路线,并给出了一个完整失败案例。这一章把两条路线的原理和取舍讲清楚。
5.1 模板匹配流程与样本库组织
模板匹配的思路是准备一个样本库,每个样本是已知字符的22×14二值图,然后把待识别字符和库中所有样本逐像素比较,误差最小的就是结果。报告里的实现是“样本与数据库中的图片相减,找误差最小的图片”,对应代码如下:
function ch = matchChar(cutImg, sampleDir) cutImg = imresize(cutImg, [22 14]); % 统一尺寸 cutImg = im2double(cutImg); files = dir(fullfile(sampleDir, '*.jpg')); bestErr = inf; ch = ''; for i = 1:length(files) t = imread(fullfile(sampleDir, files(i).name)); t = imresize(t, [22 14]); t = im2double(t); err = sum(sum(abs(cutImg - t))); % 绝对差求和,SAD误差 if err < bestErr bestErr = err; ch = files(i).name(1); % 样本文件名首字符即类别 end end end误差度量用绝对差求和(SAD)还是平方差求和(SSD)都可以,SSD对差异更敏感,匹配结果更尖锐,但受离群像素影响更大。样本库文件名直接编码字符类别,比如首字符是“0”的样本就命名为0_1.jpg、0_2.jpg,匹配结束后取文件名首字符作为识别结果。报告的imwrite里用了F:\MATLAB\work\sam\image这种绝对路径,复现时建议改成相对路径加fullfile,否则换电脑必崩。
模板匹配最怕两件事:样本量不足和样本与待识别字符风格不一致。同一个字符,不同字体、不同笔画粗细、不同车牌材质反光,在22×14的归一化图像上可能差出几百个像素的SAD误差。样板库每个字符最好放10张以上从不同车牌上切下来的真实样本。
5.2 神经网络替代方案:四个子网分开训练
报告提到两种神经网络做法:一种是先提取特征再训练分类器,另一种是直接把图像像素送入网络让网络自己学特征。报告中用的是后者更适用的方案:字符图像直接拉成向量输入BP网络。
% 示意:把22x14二值图拉成308维向量,输入BP网络 trainX = reshape(sampleImgs, 308, []); % 每列一个样本,共308维 net = feedforwardnet(60); % 单隐含层,60个神经元 net = train(net, trainX, trainY); % trainY是类别标签的独热编码注意报告把样本分成汉字、字母、字母数字、数字四组,分别训练四个子网。原因是汉字类别多且结构复杂,字母和数字存在大量相似形(比如0和O、1和I),混在一起训练会让网络难以收敛。拆成子网后,每个子网的类别数减少,类间距离更大,识别率能明显提升。
直接用像素向量训练的好处是不用手工设计特征,坏处是需要的样本量很大。课设级别的样本量(每类几十张)下,效果未必比模板匹配好。实际复现时我建议先用模板匹配跑通全流程,再视识别率决定要不要上神经网络。
5.3 失败案例复盘:G→B、K→A、0→8 的根因
报告里有一个很有价值的失败案例:某次识别把“G”误识成“B”,“K”误识成“A”,“0”识别成“8”。这三类错误代表了模板匹配的三种典型失败模式。
G和B在22×14分辨率下实在太像:右侧都是竖线加弧线,唯一明显区别是B中间有一横隔出两个孔,G没有。如果模板匹配只看整体像素差,这两个字符的SAD误差会非常接近。K和A同理,左侧竖线加右侧斜线的骨架几乎重叠,区别只在K的斜线更低、A有一横。0识别成8则通常是分割阶段的问题——字符0内部残留了噪点,二值化后噪点把0的中间连了一条横杠,0就变成了8。
这三类错误提示一个关键原则:识别错了先别急着换识别算法,把二值化后的字符图打出来看。如果字符本身是脏的、断的、粘连的,再强的识别器也救不回来。匹配前加一步重心对齐,把归一化字符的白色像素重心平移到图像中心,能缓解笔画粗细不均带来的整体偏移误差;再用bweuler计算字符的欧拉数(连通域数减孔洞数),把B、8这类双孔字符和G、0这类少孔字符先分开,再做模板匹配,能显著压低相似字符的误判。
6. 换个底板就翻车?避坑清单与再工程验证
这套报告默认处理蓝底白字车牌。实际复现时,代码里还有几处“时代印记”和“环境依赖”,直接把报告代码跑起来大概率会断。下面是照着复现时最值得注意的四类问题。
6.1 四条常见问题
现象1:新版Matlab运行到imfeature直接报错。原因:imfeature是远古接口,在新版本中已被regionprops完全替代。解决:把Feastats = imfeature(L,'basic')改为stats = regionprops(L,'Area','BoundingBox'),字段名Area和BoundingBox不变,取值方式从[Feastats.Area]改为[stats.Area]。
现象2:二值化后字符变成黑色,背景变成白色。原因:level公式假设字符比背景亮。遇到车身亮、车牌在阴影里时,字符灰度低于底板,二值化结果整体反转。解决:先打印增强图Egray,确认字符和底板的灰度关系。如果反转了,在二值化后加一行bw2 = 1 - bw2。
现象3:旋转校正后车牌上下边框被裁掉,字符缺一半。原因:imrotate(..., 'crop')在旋转角度大时,会把四角空白裁掉,同时把有效字符行也切了。解决:旋转前先用padarray向四周扩一圈,转完再按投影结果取有效行。
sbw_pad = padarray(sbw1, [20 20], 0, 'both'); sbw = imrotate(sbw_pad, angle, 'bilinear', 'crop'); % 旋转后重新做水平投影取rowtop/rowbot,不要沿用原来的值现象4:识别结果反复把0输出成8,把G输出成B。原因:模板库样本太少、没有做字符重心对齐、相似字符缺少粗分类。解决:匹配前把字符白色像素重心平移到中心,再用欧拉数分桶:双孔字符单独建库,单孔和无孔字符分两组匹配。这一步对0/8、G/B这类组合的改善非常明显。
6.2 换底板颜色与完整验证流程
报告总结里写了一句很关键但容易被忽略的话:算法对黑底白字车牌原则上可直接适用;对白底黑字、黄底黑字车牌,需要调整定位算法,并将图像反转(0变1、1变0)。实操时白底黑字只需在二值化前后把图反过来就行:bw2 = ~bw2。黄底黑字除了反色,定位阶段还要把基于“车牌区域相对暗”的假设改成基于边缘密度,因为黄色底板在灰度图上亮度很高,和车身反光区域难区分。
验证流程建议做成一个固定的检查顺序:
| 测试场景 | 预期结果 | 关键检查点 |
|---|---|---|
| 正对、光照均匀 | 直接识别成功 | 二值图字符完整 |
| 水平倾斜5~10度 | 校正后分割成功 | 拟合角度正负正确 |
| 阴影遮挡一半车牌 | 增强后仍能定位 | 阈值level是否被拉高 |
| 字符污损或褪色 | 分割存在断笔 | 模板匹配误差偏大 |
我的习惯是把每张测试图的中间结果全部落盘:level值、候选区宽高比、旋转角度、每个字符的匹配误差前三名。这样识别错了翻日志就能定位是哪一步挂的,而不是对着最终结果瞎猜。从那以后我每次跑这种定位-分割-识别流程,都强制走一遍“先把中间结果全部落盘、再谈识别率”的流程。很多识别错误不是算法不行,是前面某一步的参数被环境带偏了;打印出中间图,十分钟就能找到断点。这套Matlab车牌识别课程设计的代码和报告,适合作为你第一个图像识别全流程的练手项目,希望帮到你。
本文还有配套的精品资源,点击获取