资讯详情

资讯详情

MATLAB车牌识别系统实现:从定位分割到模板匹配的完整流程

简介基于MATLAB的车牌识别系统源码包面向高校学生与图像处理入门开发者常用于毕业设计及课程实践。资源覆盖从车牌图像预处理、定位、字符分割到数字与汉字识别的完整流程并附有数字模板、省份汉字样本及对应查询表方便快速定位模板。zip压缩包共62个文件以10个m源程序为主体辅以bmp模板图片、jpg测试图片、asv自动保存文件与xls数据表整体仅554KB目录结构清晰易检索。已有248人学习下载适合希望快速掌握MATLAB图像处理与模式识别原理的读者。通过运行源码可直观查看从读入车牌、灰度化二值化、边缘检测、模板匹配到字符输出的全过程还可利用样本库调整参数理解不同光照角度下的优化策略为毕业设计提供可直接改用的基础代码。1. 车牌识别MATLAB项目先跑通定位-分割-识别闭环再谈改算法车牌识别系统MATLAB实现听起来像是课程设计里的老选题但真把代码跑起来的人都知道翻车点全在前半步车牌框不出来后面所有模块都白搭。这套资源给的是一个完整的定位-分割-识别闭环从edge算子、形态学处理到模板匹配全都在MATLAB里一步步能跑通适合第一次接触图像处理的人照着搭也适合熟手拿来做调参底子。拿到手之后建议不要直接喂测试集先把自带样例图走一遍流程再换自己的图看中间输出。2. 车牌定位边缘检测、形态学闭运算与候选框筛选的完整流程定位是整个系统里最值得花时间的一步。后面分割和识别用的都是定位裁剪出来的区域定位框偏了、大了、倾斜了字符分割就会粘连或漏切识别率在第一步就已经锁死了上限。车牌定位常见的思路有两种一种是边缘密度优先一种是颜色区域优先。这套资源里主用边缘检测加形态学方案颜色信息作为二次校验先按这个顺序拆开讲。2.1 定位思路边缘优先还是颜色优先车牌在自然图像里最稳定的特征不是颜色而是密集的直线边缘群。字符密集排布、边框横平竖直、字符与底色的灰度差明显这三个条件叠加起来让车牌区域在边缘图里表现为一块纹理密度远高于周围环境的高响应块。颜色定位在蓝底车牌的理想场景里看着很直接但一旦碰上黄牌、白牌、路灯色温偏色、车身颜色接近车牌底色HSV阈值的鲁棒性立刻暴露。我一般会把边缘检测作为粗定位主干原因是它对光照和车型的依赖小对“边缘密集”这个结构的依赖非常强。MATLAB里edge函数直接可用不用自己写卷积核先看一眼边缘图就知道这个思路在这张图上成不成立。% 读图、灰度化、边缘检测 img imread(sample_car.jpg); gray rgb2gray(img); edgeImg edge(gray, canny, [0.08 0.2]); imshow(edgeImg);这段代码是定位链路的第一步edge函数里的两个阈值是Canny算子的双阈值。低阈值设0.08左右用来保留车灯、车窗、文字轮廓这些弱边缘高阈值设0.2左右用来抑制背景纹理噪声。两个阈值差距拉得太大边缘图会成一片雪花差距太小又会把车身的渐变阴影当成边缘。车牌字符的笔画边缘强度稳定基本落在0.1到0.3之间所以这个参数组在大多数马路场景下都能用。2.2 灰度化与增强让边缘检测在阴天和晚上也能用灰度化这一步看起来没有必要但实际上它把RGB三通道压缩成单通道后边缘检测的响应会收敛很多。彩色图像的R、G、B三个通道各自都带边缘响应三个边缘图叠加后噪声会被放大三倍而车牌识别需要的边缘结构在灰度图里一点不会少。rgb2gray之后我通常还会补一步局部对比度增强不是做不做的问题而是选哪个方案。用histeq做全局均衡化光线好的白天没问题傍晚逆光时高光区域会被吹白车牌区反而变成一块白板。adapthisteq是基于局部块的对比度增强更适合汽车场景这种光照不均匀的情况。% 局部对比度增强 enhanced adapthisteq(gray, NumTiles, [8 8], ClipLimit, 0.02); % 增强后的边缘检测 edgeImg edge(enhanced, canny, [0.05 0.15]);NumTiles设为8×8把图像切成64个小块分别做直方图均衡ClipLimit控制每个灰度级的最大提升幅度设0.02不容易放大局部噪点。阴天、傍晚、地下车库这三类场景加了这步之后边缘图的信噪比会有肉眼可见的提升。夜间强光直射车牌的图片这一步只能减轻问题真正解决要靠后面避坑部分里单独讲的伽马校正。2.3 形态学闭运算把离散边缘拼成候选块边缘检测完成后车牌区域的真实状态通常是字符笔画的断裂边缘、边框的长直线段、字符与边框之间的空隙。要拿连通域把它框出来得先把这些碎片接上。闭运算即imclose是这个阶段的核心先膨胀再腐蚀能把相邻的像素群连成片同时不改变整体的面积级别。车牌边缘的特点是横向连接需求远大于纵向同一排字符之间的断裂需要横向填补而上下边框和字符之间不能连成一整块否则候选框会吞掉整台车。所以结构元素要用横向矩形。% 横向矩形结构元素做闭运算 se strel(rectangle, [3 15]); closedImg imclose(edgeImg, se); filledImg imfill(closedImg, holes);这里的结构元素是3×15像素的矩形3是纵向高度对应字符边缘间隔15是横向宽度用来跨过字符笔画之间的空白。闭运算之后车牌区域内部基本是一个连通的大块再用imfill把内部孔洞填满后面regionprops统计外接矩形时就不会把字符间隙当成多个区域。如果结构元素选成3×3的正方形字符边缘之间多半接不上定位框会碎成好几块选成15×15的方块又会让车灯和保险杠连成一片。2.4 连通域筛选面积、宽高比、矩形度三重过滤形态学处理后的连通域里车牌候选块一般会有五到十个真车牌通常夹在其中。筛选条件要按三个维度写面积、宽高比、矩形度。面积范围不能写死成像素数否则换一张高分辨率图整套参数失效。我一般用相对面积也就是候选块面积占整张图像面积的比例落在0.3%到5%之间。车牌不会太大也不会太小这个范围能把大部分车身形变的块、树影块排除掉。宽高比是第二个硬条件。标准蓝牌的宽高比在3.1左右考虑到拍摄透视我通常放宽到2.5到4.5。矩形度定义为候选块面积与最小外接矩形面积之比字符填充整齐的假车牌和真车牌都能到这个指标但随机树影、车灯高光块的矩形度通常低于0.5过不了这一关。% 标记连通域并提取候选框 [labeledImg, numObj] bwlabel(filledImg); props regionprops(labeledImg, Area, BoundingBox); imgArea numel(gray); candidateBoxes []; for k 1:numObj ratioArea props(k).Area / imgArea; if ratioArea 0.003 || ratioArea 0.05 continue; end box props(k).BoundingBox; whRatio box(3) / box(4); if whRatio 2.5 whRatio 4.5 candidateBoxes [candidateBoxes; box]; end endbwlabel把二值图里的独立连通域编号regionprops返回每个区域的面积和最小外接矩形。BoundingBox的格式是[x, y, width, height]所以代码里box(3)除以box(4)就是宽高比。计算相对面积时用整张灰度图的像素数量做分母分母不随图像分辨率变化同一套阈值在不同相机上表现一致。循环里先卡面积再卡宽高比顺序如果反了计算量会多出一个数量级。一轮筛选后如果还有多个候选我会在候选框内做字符级验证对每个候选区域单独跑一遍垂直投影车牌区域应该出现七个左右明显的字符峰。这个验证丢进后面的识别模块里做定位阶段不急着把候选列表砍到只剩一个。3. 字符分割从二值化、连通域到归一化的实现细节定位给出的是车牌的矩形区域但该区域里除了七个字符还有边框、铆钉、间隔符和底色噪声。字符分割的任务是把七个字符从这块区域里单独抠出来并且统一尺寸交给识别模块。这一步做崩了会出现两种情况字符被切掉半边或者两个字符连成一个块。3.1 二值化阈值固定阈值为什么容易翻车定位裁剪出的车牌子图底色可能是蓝、黄、白、黑中的任意一种而且受打光影响同一块车牌左侧亮右侧暗是常事。固定阈值比如全局取100在非受控光照下根本撑不住。我一般优先用graythresh算大津阈值再对裁剪图做二值化。graythresh的核心思路是最大化前景和背景之间的类间方差它只依赖灰度直方图的形状对光照变化的适应能力在全局阈值算法里是比较强的。% 裁剪车牌候选区域并做二值化 cropImg imcrop(img, candidateBoxes(1, :)); cropGray rgb2gray(cropImg); thresh graythresh(cropGray); bw imbinarize(cropGray, thresh);这里有个细节candidateBoxes里可能有多个候选框不能默认第一个就是真车牌。我会把它们全部送进同一个分割流程每个候选框都会形成一组分割结果加识别置信度最后取置信度最高的那一组。imcrop的坐标直接用BoundingBox但需要确保裁剪位置加上宽高后不超出原图边界边缘情况在MATLAB里会抛错写代码时应该先用min和max把边界夹住。提示imcrop之前先判断 box(1)box(3) 是否超过原图列数、box(2)box(4) 是否超过原图行数超了就截断不要直接调用。3.2 去掉边框和铆钉的干扰二值化之后的图像里车牌边框和铆钉往往还挂在字符旁边。边框横向贯穿整个车牌图像和第一个、第七个字符紧挨在一起铆钉则出现在第二字符和第七字符附近形成孤立的圆形高亮点。这两类杂物如果不删除连通域分割时会把边框和字符连成一片。处理边框的常用做法是裁剪时直接把外边缘收缩几个像素。车牌拍摄成像时边框宽度和字符笔画宽度相近收缩量取图像高度的5%左右比较合理。铆钉不能靠裁剪解决要靠面积筛选。在连通域统计后把面积小于最大字符面积三分之一的连通块全部去掉铆钉和微小噪点在这一步基本会被清光。实际操作中我先后标记、统计、再过滤不要先过滤再做标记容易把还连在一起的字符块错误切开。% 二值图清理先开运算去除细线再做连通域标记 bwClean imopen(bw, strel(rectangle, [2 2])); [labeledChar, numChar] bwlabel(bwClean); charProps regionprops(labeledChar, Area, BoundingBox); maxArea max([charProps.Area]); keepIdx find([charProps.Area] maxArea * 0.3);imopen是开运算先腐蚀后膨胀作用是抹掉细线和孤立亮点。这里结构元素用2×2既能去掉一像素宽的毛刺也不会伤到字符主体。maxArea取最大连通块面积当作参照把面积小于它30%的块删除在实际测试里能干掉大部分铆钉和边框残片。如果铆钉和字符笔画相连光靠面积删不掉需要配合形态学做高帽变换这个坑放在避坑部分展开。3.3 连通域标记与字符排序清洗完成的二值图每个字符应该是一个独立的连通域。去掉干扰后车牌区域的连通域数量一般是七个或者更多一点。七个字符从左到右排列但regionprops返回的连通域顺序是按扫描行序不是按坐标排序必须显式按BoundingBox的x坐标从小到大排否则识别结果会乱成一串。% 按x坐标排序 charBoxes reshape([charProps(keepIdx).BoundingBox], 4, []); [~, sortIdx] sort(charBoxes(:, 1)); charBoxes charBoxes(sortIdx, :);这里有个常见误操作直接用sort对候选框排序但连通域的BoundingBox是一个4×n矩阵直接sort()会把每一列都排一遍。reshape先把它整理成n行4列再对第一列排序才是按字符的x位置排序。排序之后还要过滤掉明显是间隔符的小块。间隔符通常出现在第二个字符和第三个字符之间它的x坐标落在两者之间面积明显小于字符。判断方法是用字符宽度的中位数乘以0.3做阈值太小的直接丢掉。3.4 字符归一化给识别模块统一输入尺寸分割出的字符图长得不一样有的大有的小笔画粗细也不同直接拿去做模板匹配会出现尺寸不匹配导致的误判。归一化的目标是让每个字符图统一到一个固定尺寸并且保留笔画宽窄的原始比例。尺寸选多大我习惯用40×20高40宽20这个比例和真实车牌字符的高宽比接近缩得太小会丢失B和8这种细结构的差异放得太大又会让模板匹配的计算量白白上涨。% 字符归一化到40x20 function charNorm normalizeChar(charImg) charNorm imresize(charImg, [40 20], bilinear); charNorm imbinarize(charNorm, 0.5); endimresize的第三个参数选bilinear而不是nearest因为nearest会产生锯齿在后面的模板匹配里锯齿会被误当成笔画特征。归一化之后再二值化一次是为了洗掉缩放过程中产生的中间灰度让字符变成纯粹的黑白像素。字符图中间如果有孤立小洞不要在这一步用中值滤波去补它会同时抹掉字符的锐利边缘。4. 字符识别模板库构建、匹配算法与拒识阈值的取舍字符分割拿到的七个归一化字符图最后一步要映射到字符标签。后两个字符是字母和数字混合第一个字符是省份简称汉字第三个位置在部分车牌上是字母。模板匹配是这套资源里的识别主干把每个候选字符图和模板库里所有模板比较取相似度最高的作为识别结果。算法不复杂但模板库怎么建、相似度怎么定义、得分太低怎么处理这三个问题直接决定最终准确率。4.1 模板库怎么建字符集覆盖与多字体策略国内车牌的字符集包括31个省级简称汉字、24个英文字母原车牌里不用I和O避免和数字1、0混淆和10个数字。模板库不能只收标准打印体因为实际车牌字符在不同批次、不同工艺下笔画粗细变化明显骨架相同但胖瘦不同的字符模板匹配得分会差一截。常见的做法是每个字符准备2到3个模板标准体、粗笔画体、轻微倾斜体。模板多了之后匹配计算量会线性增加但现代PC上七个字符、每个字符匹配不到一百个模板耗时几乎可以忽略。% 读取模板文件中的所有模板 templateDir templates; templateFiles dir(fullfile(templateDir, *.png)); templateSet []; for i 1:numel(templateFiles) tImg imread(fullfile(templateDir, templateFiles(i).name)); tImg imresize(tImg, [40 20], bilinear); tImg imbinarize(tImg, 0.5); templateSet{i} tImg; templateLabel{i} templateFiles(i).name(1); end模板文件命名最好是“京_standard.png”这种格式文件名第一个字符就是标签方便程序直接按文件名提取。多模板怎么做匹配常见做法是同一个标签的所有模板都参与相似度计算取相似度最高的模板得分作为这个标签的代表得分而不是把多个模板的得分平均。取最大值能保留模板集中最接近实际样本的那一个平均反而会把多个模板的差异抹平拉低得分。4.2 模板匹配实现相关性匹配与普通距离的一票否决模板匹配最简单的距离算法是逐像素异或统计两个二值图不相等的像素越少相似度越高。但在实际图片里字符笔画粗细差异会导致这种统计不抗干扰。corr2即二维相关系数计算时会对两个图像分别减去各自均值再归一化所以对整体亮度偏移不敏感。对字符这种小尺寸图形用corr2比用异或更稳定。% 模板匹配计算归一化相关系数 function [label, score] matchChar(charNorm, templateSet, templateLabel) bestScore 0; bestIdx 1; for i 1:numel(templateSet) s corr2(charNorm, templateSet{i}); if s bestScore bestScore s; bestIdx i; end end label templateLabel{bestIdx}; score bestScore; end这个函数的生产环境版本我不会在函数里打印任何内容识别结果和得分都返回到主流程统一处理这样批量跑测试集时候更好定位。corr2返回值的范围是-1到1正常字符匹配的得分在0.4以上同一字符不同模板之间的得分通常在0.7到0.95之间。如果得分低于0.4要么字符分割出的图片质量太差要么模板库缺失该字符的变体。4.3 拒识阈值识别不出来的字符不如不识别很多课程设计里的模板匹配实现没有拒识环节最高分是谁就输出谁哪怕最高分只有0.2也会强行输出一个字符。这种实现放在几十张样例图里看着没问题放到路测数据里会把沪识别成浙、把Q识别成O。我的做法是给最终匹配结果加一个底线得分0.5是底限低于0.5直接标为拒识。拒识的字符不参与最终车牌号拼接宁可少一位不可错一位。% 主流程用拒识阈值控制输出 [label, score] matchChar(charNorm, templateSet, templateLabel); if score 0.5 resultChar ?; else resultChar label; end拒识阈值不是越高越好。阈值提到0.8所有带污损的真字符都会被拒误识率虽然降了但大量真字符变成问号整体识别率反而下降。这个阈值得放在不同光照样本上试出来我一般会在测试集上跑一遍统计正确识别字符的分数分布再把拒识阈值设在出现明显误识的那个分数下沿。4.4 模板匹配的边界什么时候该换更强的识别方案模板匹配适合字符变形小、字体统一的场景。停车场、小区出入口这类固定相机位置、固定拍摄角度的场景模板匹配的准确率能上到95%以上前提是模板库覆盖到位。手机随手拍、车牌倾斜超过十度、严重褪色、运动模糊这类样本模板匹配会明显不够用。如果在实际测试中发现误识主要来自模板匹配本身而非上游分割通常下一步是换成局部二值模式或方向梯度直方图特征再配合一个线性分类器。这套资源的识别模块里预留了特征接口归一化后的字符尺寸固定为40×20换成其它分类器时输入数据不用改格式。我见过同事的做法是把分割后的字符图直接拉平成800维向量喂进一个全连接网络这种用法在模板匹配改为神经网络时同样成立。5. 避坑记录五个容易让识别率归零的MATLAB细节这部分是实测里最值得看的。不少资源在样例图上跑得漂亮换个场景就翻车原因往往不是算法思路错了而是几个细节没处理。下面五条是按出现频率排的坑每条都按现象、原因、解决的顺序展开可以直接对照自己的复现过程查。5.1 现象定位框飘到路牌或车灯上带字的窄矩形广告牌、车尾灯高光区在形态学处理后都会形成和车牌相似的高密度连通域。现象就是candidateBoxes里第一候选不是车牌实际车牌排在第三个或第四个候选位置直接用candidateBoxes(1,:)裁剪就崩了。原因边缘检测只能保证车牌是边缘密集区但它不是唯一具备这种特征的区域。路牌有大量文字边缘车灯是强边缘加高亮区域二者都能通过面积和宽高比筛选。解决不要只靠位置选候选要把每个候选区域放进分割识别链路最终按识别得分排序。另一个更直接的办法是加一道颜色校验利用车牌底色在HSV空间的聚集性。蓝色车牌在HSV里集中在H值为0.55到0.7的范围黄色车牌集中在0.1到0.2这两个色相范围可以先把非蓝非黄的候选块直接去掉。% 颜色校验统计候选区域内的蓝色像素比例 hsvImg rgb2hsv(img); blueMask (hsvImg(:, :, 1) 0.55 hsvImg(:, :, 1) 0.7) ... (hsvImg(:, :, 2) 0.3); for k 1:size(candidateBoxes, 1) box candidateBoxes(k, :); cropMask blueMask(round(box(2)):round(box(2)box(4)), ... round(box(1)):round(box(1)box(3))); if sum(cropMask(:)) / numel(cropMask) 0.1 % 该候选区域蓝色占比超过10%保留 end end这里的蓝颜色阈值不能沿用BGR顺序的旧代码MATLAB的rgb2hsv输出顺序是H、S、V很多从C移植过来的代码容易在这一步把H值取错。蓝色占比阈值取0.1太低会把偏蓝的车身放进候选框太高又会漏掉褪色严重的旧车牌。5.2 现象分割时字符和边框粘连成一整块连通域统计出来的不是七个字符而是边框加字符合体的一两个超大块。现象是字符个数只有四五个而且外接矩形比正常字符宽很多。原因二值化阶段边框的灰度值和字符相近Canny或者二值化后字符图里边框根本没被分离干净。解决在连通域步骤之前先对裁剪图再腐蚀一次把横向细线腐蚀掉。车牌边框的横向宽度一般不到字符笔画宽度的一半用2×3的结构元素腐蚀一次就能把它和字符断开。如果断不开说明原图片是低分辨率远景图可以改走颜色校验方案先粗定位再用角点修正倾斜。% 对二值图做针对性腐蚀 seBreak strel(rectangle, [2 3]); bwNoBorder imerode(bwClean, seBreak);imerode的结构元素2×3不是随便选的。2像素高度对应边框细线3像素宽度对应边框横向片段腐蚀后字符主体瘦一圈但保持连通边框则从根上断掉。这里如果结构元素用成3×3字符笔画整体会明显变细后面模板匹配的得分会被拉低不少。5.3 现象白色车辆的车牌找不出来白车身蓝车牌是很有迷惑性的组合。起步阶段看边缘图蓝车牌和白车身之间的灰度差并不大边缘响应弱车牌区域在边缘图里密度低形态学闭运算后根本拼不出一个候选块。原因边缘检测依赖灰度梯度蓝色车牌的灰度值和白色车身其实很接近梯度小边缘响应弱。边缘密度优先的思路在低对比度边界上失效这不是算法参数问题是原理问题。解决定位阶段把颜色信息从二次校验提到粗定位候选。对于蓝色车牌直接取蓝色像素掩模形态学处理后做连通域得到的蓝色块候选就是车牌的强先验。白车场景用颜色补一刀效果立竿见影。% 用蓝色像素掩模补定位 hsvImg rgb2hsv(img); blueMask (hsvImg(:, :, 1) 0.55 hsvImg(:, :, 1) 0.7) ... (hsvImg(:, :, 2) 0.4) (hsvImg(:, :, 3) 0.15); se strel(rectangle, [5 5]); blueCandidate imclose(blueMask, se);这段代码里对饱和度和明度的约束比5.1节严格因为白车场景需要排除车身表面的反光噪点饱和度低于0.4的高亮区即使色相接近蓝色也大概率是灰白反光形成的错觉。明度大于0.15是为了排除夜间接近黑色的暗部。5.4 现象B和8、O和0这两对最常混淆匹配结果里B和8分错的次数最多O和0次之。这两个相似度极高的对手在模板匹配得分上的差距常常不到0.03仅靠相关度无法稳定区分。原因模板匹配对形状相似度敏感但B和8、O和0的骨架差异小都包含闭合曲线区域模板匹配得分天然扎堆。解决对这两对字符加一个专用的几何特征判别。B和8可以用闭合区域数量区分B有左上角一个封闭洞加右侧两个开放凹口8有两个封闭洞这个结构差异稳定。二值图上用bwlabel统计字符内部的孔洞数量代码量小且可靠。% 用内部孔洞数量区分B和8 function isEight checkHole(charImg) invImg ~charImg; invLabeled bwlabel(invImg, 4); holeNum max(invLabeled(:)) - 1; % 字符外背景也算一个连通域 isEight (holeNum 2); end用4连通标记外背景字符内的孔洞能独立计数8字形内部两个洞所以holeNum等于2B字形左上角只有一个洞所以holeNum等于1。注意不要用8连通8连通时两个孔洞会和外部背景相连统计结果全部失效。O和0的区分则要看右侧弧度或内部宽度特征O的左右边缘均匀0的左侧边缘通常更直。5.5 现象夜间图片识别准确率骤降夜间图片在edge和imclose这套链路里表现得特别差要么定位框找不到要么二值化后整个车牌糊成一团。原因夜间车牌受大灯强光影响车牌区域过曝字符和底色的灰度差被压缩再加上路灯偏色传统灰度图信息量不足。解决先把增强步骤换成伽马校正压高光再进灰度化。伽马值取0.5到0.7之间能把过曝区域拉回可处理的动态范围。注意不要在伽马校正前做直方图均衡化两个操作叠加会把暗部噪点放大成伪边缘让边缘图变成雪花图。% 伽马校正把过曝拉回 gamma 0.6; gammaImg imadjust(gray, [], [], gamma); edgeImg edge(gammaImg, canny, [0.05 0.12]);imadjust的第四个参数就是伽马值小于1时整体变亮大于1时变暗。夜间场景里我一般把低阈值从0.08降到0.05多保留一些若隐若现的边缘高阈值保持0.12上下避免整体边缘量过载。这张图里的车牌区域在边缘图上可能仍然是短板但只要定位框能框出来分割和识别环节在夜间反而稳定因为夜间车牌区域的二值化对比度通常比白天更高。6. 批量验证与脚本化把单张跑通变成整个文件夹稳定复现调参阶段最大的麻烦不是算法本身而是经验判断的不可复现。你手动改一次阈值在一张图上效果好在另一张图上可能直接翻车。我会把整个流程封装成一个批量脚本把每张测试图的中间结果全部落盘这样每次调参之后肉眼扫一遍中间图就能看出是哪一步出的问题。批量脚本的输入是一个文件夹路径输出是一个文本结果表加一堆中间过程图。代码核心就是读图、跑定位、分割、识别、保存结果五步和前面的流程代码完全一致只是多了一层文件循环。% 批量识别遍历文件夹下所有图片 testDir test_images; outDir output; files dir(fullfile(testDir, *.jpg)); resultsTable table(); for i 1:numel(files) img imread(fullfile(testDir, files(i).name)); % 定位、分割、识别主流程前面各章的代码整合 [plateNum, confidence] runPlateRecognition(img); % 保存中间过程图 imwrite(img, fullfile(outDir, [files(i).name _input.jpg])); % 记录结果 resultsTable [resultsTable; table({files(i).name}, ... {plateNum}, confidence)]; end writetable(resultsTable, fullfile(outDir, results.csv));confidence在批量脚本里很重要我要求识别结果必须带得分一起输出否则看不出哪些样本是低分蒙对的。跑完一批图之后按confidence升序排序列出结果分数最低的那些样本就是要人工介入调参数的源头。中间过程图虽然会增加磁盘占用但它是调参时的后悔药比任何日志都好用。另一个值得养成的习惯是把验证集分成三块白天正常光照、傍晚逆光、夜间灯照。每次改完参数三块各抽十张跑一遍不能只看总体准确率这三块的准确率要分别统计。某次我在傍晚样本上调参把总体准确率从80%拉到86%结果白天样本准确率反而掉了7个点就是因为只盯着总体数字没分场景看。从那以后我每次改参数都强制走一遍三场景分开统计的流程每张图的中间输出也全部留档。如果你准备把这份车牌识别流程用起来我建议第一件事就是跑通这里的批量脚本再慢慢替换成自己的样本。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →