MATLAB车牌识别实战:边缘检测定位与模板匹配字符识别
发布时间:2026/10/16 15:38:13 锦皓数字建站

简介这套资源是基于MATLAB实现的车牌识别完整源码包主要面向计算机视觉初学者、图像处理课程学生以及毕业设计人员。项目围绕车牌识别全流程组织包含图像灰度化与二值化、中值滤波去噪、Canny边缘检测、霍夫变换定位、连通域分析、垂直投影字符切割、特征提取与分类识别等关键代码模块并配有主程序main.m及形态学、倾斜校正、裁剪、投影、字符识别等独立脚本。压缩包共69个文件含44个bmp字符模板、14个jpg测试图片、10个m源码文件及1个说明txt整体大小4.51MB目录区分“字符模板”“测试图片”“源代码”等模块便于按步骤对照学习。已有539人浏览学习。这套代码覆盖面完整读者既可以整体运行查看识别效果也可以分步调试理解每个算法的MATLAB实现方法还能利用模板库自行扩展不同省份字符。对完成课程设计或入门图像处理与模式识别均有较强参考价值。1. 基于MATLAB的车牌识别识别率瓶颈在定位不在字符基于MATLAB的车牌识别听起来是被做烂的题目但真拿一张路边随手拍的照片跑一遍你会发现多数开源Demo连车牌在哪都找不准。我拆这个资源包时印象最深的一点是整个流程中定位环节占了近一半的代码量字符识别反而是最短的一段。这套实现做的事情很直接——输入一张包含车牌的普通图像输出车牌字符串覆盖灰度化、边缘检测、形态学定位、字符分割、模板匹配一条完整链路。它适合正在做图像处理课程设计、毕设选了这个方向、以及想不依赖深度学习框架快速搭一个可解释识别基线的读者。跑通之后你再替换成深度学习识别器前面的定位分割模块仍能直接复用。2. 车牌定位Sobel边缘与形态学闭运算的双重筛选车牌定位是整个识别流程里最值得细看的模块因为后续分割和识别的误差基本都在这里开始累积。一个直觉是“先把彩色图转成二值图再找矩形”实际做一遍就会发现户外光照、车身反光、路牌干扰都会让简单二值化直接翻车。这里把定位分成两层筛选先用边缘检测提取车牌边框特征再用形态学闭运算把散碎边缘连成候选块最后用几何约束过滤非车牌区域。2.1 灰度化与Sobel边缘检测为什么不用Canny大多数输入图像是彩色照片。直接对彩色图做形态学处理有两个问题三个颜色分量的噪声会叠加亮度差异对结果的影响没有灰度图直观。因此第一步永远是灰度化把后续所有操作的输入维度降低。% 读取原图 img imread(plate_sample.jpg); % 彩色转灰度 gray rgb2gray(img); % 水平方向的Sobel边缘检测灵敏度阈值0.15 bwEdge edge(gray, sobel, 0.15);三个步骤分别对应读取、降维、提取边缘。这里用edge(gray, sobel, 0.15)第三个参数是灵敏度阈值值越大被判定为边缘的像素越少值越小边缘越密集。阈值0.15是一个经验起点实际调试时按0.05的步长从0.1扫到0.3再用可视化叠加确认效果会比死记一个阈值可靠得多。为什么选Sobel而不是Canny车牌上下边框的水平边缘非常连续Sobel对水平梯度的响应强、边缘线条粗、断裂少后续闭运算更容易缝合Canny的滞后双阈值对参数更敏感户外环境下容易把车身纹理、树叶阴影也判定成边缘产生大量碎片。边缘图里的碎片越多闭运算越容易把干扰区域和车牌区域合并在一起定位框随之整体偏移。一个容易被忽略的细节是Sobel默认提取整幅图像所有方向的边缘实际车牌区域主要依赖水平边框所以也可以用fspecial自定义一个水平方向的Sobel核来实现定向提取。这会让边缘图更干净代价是丢失垂直边框信息折中做法是先用默认方向提取让形态学闭运算去做连接。2.2 形态学闭运算与孔洞填充把散点连成候选块边缘图是一堆散碎的短线段车牌区域的长边框和字符纹理被道路标线、车身装饰条打断是常事。形态学闭运算就是干这个的先膨胀再腐蚀。膨胀把相邻边缘点缝合起来腐蚀再把扩张出去的多余毛刺收回来整体面积变化不大但内部连通性大幅提升。% 定义矩形结构元素宽45、高15 se strel(rectangle, [45, 15]); % 闭运算 bwClose imclose(bwEdge, se); % 填充内部孔洞让车牌区域变为实心块 bwFill imfill(bwClose, holes);结构元素尺寸是闭运算的唯一参数也是定位精度最敏感的参数。宽45、高15是按常规车牌尺寸推算的车牌物理尺寸约440mm×140mm宽高比约3.1:1字符间隙在宽度缩放到约600像素时大约为15像素左右。结构元素太窄字符之间的缝隙缝合不上车牌区域会断成两块太宽路牌、车灯、车身装饰条会被误并为同一个连通块。如果不确定尺寸常见做法是对多个尺寸各做一次闭运算把所有候选区域都交给下一层几何筛选去淘汰而不是在一开始就赌一个参数。孔洞填充在闭运算之后针对的是车牌内部字符与字符之间的空隙。填充之后车牌区域从“空心矩形框”变成“实心矩形块”这就是后续 regionprops 能正确计算长宽比的前提。如果跳过这一步计算出的区域面积会明显小于实际车牌面积下限阈值就不好设。2.3 连通域筛选用长宽比和面积过滤非车牌区域闭运算结束后图中通常还残留多个连通块。接下来用几何属性做第二层筛选。车牌是一个宽高比稳定的矩形这两个约束能排除掉大部分干扰区域。% 标记连通域 labeled bwlabel(bwFill); % 提取每个连通域的包围框与面积 stats regionprops(labeled, BoundingBox, Area); for k 1:numel(stats) w stats(k).BoundingBox(3); h stats(k).BoundingBox(4); ratio w / h; area stats(k).Area; % 保留宽高比2.5~5.5、面积大于2000的候选块 if ratio 2.5 ratio 5.5 area 2000 candidate stats(k).BoundingBox; % 从原图中裁出候选区域交给后续分割 plate_region imcrop(gray, candidate); end end宽高比范围为什么给到2.5到5.5而不是严格的3.1因为车辆停放角度、拍摄视角都会改变车牌在画面中的投影比例车头正对镜头时接近3.1斜向拍摄时可能被压扁到2.4侧向拍摄时可能拉长到4以上。范围太窄会把倾斜拍摄的车牌过滤掉范围太宽会混入大量长条形干扰物。面积阈值2000同样不是通用值它依赖图像分辨率。这里建议按“字符密集程度”来辅助判断车牌区域内部边缘像素密度远高于周边可以在筛选结果里再加一条边缘密度条件。面积筛选之后还有一个可选的第三层校验候选区内部字符纹理的方差。车牌的纹理特征是字符与底色交替出现灰度直方图呈现双峰结构而车身面板、路牌是平滑区块。用std2计算候选区灰度方差能过滤掉一部分外形像车牌、内部却是空白的干扰物。加上这一层之后定位的误判率通常能再降一截。3. 字符分割垂直投影与连通域校验少一个都翻车定位裁出的车牌区域包含一串字符字符分割的任务是把它们逐个切出来。常见做法是垂直投影法统计每一列的黑色像素数量字符之间存在空隙空隙处的像素数会形成波谷从这个谷底切开。但直接套投影法在汉字车牌上必然踩坑因为汉字内部笔画密集投影波谷比字母数字复杂得多。3.1 垂直投影法按列像素密度切分字符先对车牌区域做二值化再统计每一列中前景像素的数量。字符区内列像素值高字符间隙处列像素值接近零投影曲线会呈现连续的高峰—低谷形态。波谷位置就是候选分割点。% 车牌区域二值化灰度大于0.6置为1 plate_bw im2bw(plate_region, 0.6); % 统计每一列的前景像素数假设前景为黑色像素 proj sum(plate_bw 0, 1); % 找到投影值为零的列位置 zero_cols find(proj 0);这里的im2bw(plate_region, 0.6)是固定阈值二值化0.6表示灰度归一化到0~1区间后的分割阈值。如果车牌图像亮度均匀0.6左右效果不错如果光照不均固定阈值会让部分字符区域变白、背景变黑投影曲线失真。更稳的替代方案是graythresh自动计算Otsu阈值或者用imtophat做顶帽变换先校正亮度不均。投影值计算中plate_bw 0表示取黑色像素这是基于“白底黑字”车牌的二值化约定。二值化方向搞反了投影曲线就会变成倒置形态这是新手最常犯的错误之一。3.2 连通域分析做二次校验两个经典分割错误投影法对“京”“鲁”这类结构复杂的汉字非常不友好。汉字内部笔画密集投影到列方向时笔画之间常常没有清晰的零值空隙导致一个汉字被误判成两个字符。另一种情况恰好相反数字“1”宽度窄、面积小在投影图中呈现的波峰不明显容易被当作噪声丢弃。% 对二值图做连通域标记 cc bwconncomp(plate_bw 0); stats_char regionprops(cc, BoundingBox, Area); % 按包围框左边界排序 boxes reshape({stats_char.BoundingBox}, [], 1); positions cellfun((b) b(1), boxes); [~, order] sort(positions); sorted_boxes boxes(order);这段代码的作用是把二值图中所有连通的字符块找出来按水平位置从左到右排序再按位置和宽度依次排列成字符串顺序。连通域方式天然规避了“汉字被投影切碎”的问题——汉字笔画虽然密集但在二维空间里仍然是连通的整体会形成一个大的连通块。需要补的校验条件有两个连通块的高度要达到车牌区域高度的50%以上宽度不能小于整体宽度的5%否则判定为噪声或边框残留。一个常见的隐藏坑是二值化后车牌边框和字符粘连。如果车牌边缘框没有被完全去掉边框会与第一个字符或最后一个字符形成一个连通块导致分割结果出现“7个字符加上一条边框”。解决方式是在连通域分析之前用imclearborder清除与图像边界相连的对象或者在投影分割前先对车牌区域做一次腐蚀操作把边框与字符的连接点断开。3.3 尺寸归一化分割完必须做的统一化处理切割出的字符宽度、高度各不相同模板匹配要求输入尺寸一致因此每一张字符图都要归一化到统一尺寸。常见做法是缩放到48×24像素这个尺寸能保留足够的笔画细节又不会让匹配计算量过大。% 将单个字符图像归一化为48×24 char_norm imresize(char_img, [48, 24], bilinear); % 统一转成二值图消除灰度差异 char_bw im2bw(char_norm, 0.5);尺寸归一化有两个细节。第一缩放算法的选择双线性插值计算平滑适合字符这种笔画型图像最近邻插值会保留锯齿对笔画细节不利。第二二值化阈值统一使用0.5是为了让模板匹配阶段只比较笔画的形状特征而不是比较灰度深浅。如果字符图像本身是白底黑字统一二值化之后再送入匹配。对于含有边框噪声的字符块可以在归一化后加一步开运算把细小的毛刺去掉。4. 字符识别模板匹配池设计与拒识阈值怎么定字符识别这层在这套MATLAB实现里用的是模板匹配。模板匹配的思路非常朴素把分割出的字符与模板库中的每一个模板做相似度计算取相似度最高者作为识别结果。朴素不代表简单模板库怎么构建、匹配算法怎么选、阈值怎么设直接决定识别的上限。4.1 构建模板库样本来源与命名规范模板库的质量决定匹配识别的质量这是整个识别模块最容易被低估的部分。模板来源有两个方向一是从同一拍摄设备采集的清晰车牌中手动切割字符二是对已有的字符图像做几何变换生成多尺度变体。% 模板文件夹结构示意 % templates/ % hans/ Wangzi/ shuzi/... % 读取模板 template_dir templates/; files dir(fullfile(template_dir, *.bmp)); for i 1:numel(files) [~, name, ~] fileparts(files(i).name); template_img imread(fullfile(template_dir, files(i).name)); template_bw im2bw(template_img, 0.5); templates.(name) template_bw; end命名规范建议采用“字符名称_编号”的格式比如“京_01.bmp”“A_03.bmp”。这样在匹配结束后可以直接从变量字段名提取识别结果避免维护额外的映射表。每个字符的模板建议准备5到10个变体样本覆盖不同光照、不同粗细的印刷体字形。同一个字符不同模板之间匹配分数取最高值。4.2 汉字的特殊处理第一字符单独匹配不混池车牌字符串的排列规则是“汉字字母数字”其中第一个字符固定为汉字。如果把汉字和字母数字混在同一个模板池里匹配宽度差异和结构差异会造成分数偏向识别率反而不如分组匹配。% 识别第一个字符只用汉字模板池 char1_score zeros(1, numel(han_templates)); for k 1:numel(han_templates) char1_score(k) match_score(char_bw_1, han_templates{k}); end [~, idx1] max(char1_score); char1 han_labels{idx1}; % 识别后续字符字母数字模板池 for j 2:7 % ... 同上逻辑从 alnum_templates 中选最高分 end这里的关键是分池匹配。第一个字符只与汉字模板比较后六个字符只与字母数字模板比较。汉字和字母数字在字形结构上差异显著混池匹配偶尔会出现“某个字母恰好比某个汉字更相似”的误判分池后这个概率被彻底排除。汉字识别还有一个进阶技巧对候选前三名做局部区域二次比对。汉字中“鲁”和“辽”、“沪”和“护”字形高度接近整体匹配分数可能只差零点几此时截取字符的右下角区域做局部匹配局部差异会被放大能有效区分相似字形。4.3 匹配分数与拒识阈值识别不出时要敢于不认模板匹配的输出是一组相似度分数但分数本身没有绝对意义。同一字符在不同光照条件下提取出的二值图可能差很多导致最高分仅有0.5与此对应一个噪声块也可能和某个模板意外相似到0.7。因此拒识阈值要按“最低可接受分数”反推。% 将最佳匹配分数转为置信度并判断阈值 best_scores zeros(1, 7); % ... 在匹配循环中填充 best_scores if best_scores(j) 0.6 result_char{j} ?; % 拒识 else result_char{j} best_label; end阈值0.6是底线实际项目里通过一批测试集的分数分布来标定收集300张车牌图片统计所有正确匹配的分数取最低值向下偏移0.05作为拒识阈值。识别不出时输出“?”不要强行给一个低置信的答案这样才能保证整体识别结果的可靠度。5. 常见问题排查五个翻车点与对应修改方案这里整理五条拆解和调试这套MATLAB车牌识别资源时最常遇到的踩坑记录。每一条都是现象、原因、解决三个层次对应到具体代码行和参数。5.1 定位框总是切掉车牌的一部分现象输出候选框时车牌左边或右边被切掉分割出来的字符数量不对。原因形态学闭运算的结构元素尺寸偏小车牌边缘与字符区域之间的缝隙没有被完全缝合整个车牌被拆成多个连通块筛选时只留下了面积最大的那一块。解决把strel(rectangle, [45, 15])的宽度加到60甚至80重新做闭运算和区域筛选或者对多个尺寸做闭运算后合并候选区域。5.2 二值化之后车牌反白投影曲线全部倒置现象分割阶段投影图中的波峰波谷与预期完全相反字符区域的数据反而接近零。原因二值化约定方向与sum(plate_bw 0, 1)不一致原本是黑底白字的车牌转换成了白底黑字统计黑色像素时统计到的是背景。解决先查看plate_bw中字符区域的值是多少要么把二值化取反plate_bw ~plate_bw要么把统计条件改为sum(plate_bw 1, 1)保证统计的是字符像素。5.3 分割出来只有四个字符中间的字母丢了现象车牌印刷清晰但分割结果缺少“·”分隔符后面的字母或数字连通域分析也没有找到。原因字符宽度过窄被当作噪声过滤或者字符与边框粘连开运算处理时整个字符被腐蚀掉。解决检查连通域筛选的宽度阈值把最小宽度从5%降到3%以下对二值化后的车牌区域用imdilate轻微膨胀一次把断裂笔画连上再做连通域分析。5.4 汉字识别结果全错字母数字基本对现象前六个字符识别正常但第一个汉字永远不对。原因模板库中汉字样本与测试图像字体差异太大或者汉字模板在归一化时保持了原始尺寸没有统一缩放到48×24。解决汉字模板统一用imresize(汉字, [48, 24], bilinear)做归一化同时保证模板和分割字符使用相同的二值化阈值0.5如果字体差异确实太大补充该字体的模板样本。5.5 数字8经常被识别成3字母G和6混淆现象单独跑每个字符的匹配分数都超过0.7但结果就是错。原因模板匹配对笔画粗细和闭合程度敏感字符“8”和“3”在二值化后轮廓相似尤其当字符笔画偏细时“8”的下半环容易被腐蚀成开放结构。解决在匹配前对字符做统一细化处理把图像线宽归一化到固定粗细或者改用归一化互相关系数normxcorr2替代简单的像素差匹配减少灰度绝对差异的影响。6. 脚本化批量验证把识别率从“玄学”变成可统计单张图片跑出结果只能叫“能运行”不能叫“可用”。我一般会把识别流程封装成一个函数再写一个批量脚本跑整个测试集用统计数字判断识别率到底是多少。封装函数的意义在于所有参数集中管理调参只改一处不用每次打开主脚本翻半天。function result plate_recognition(imgPath) % 统一封装输入图像路径输出识别结果 % result.success: 是否识别成功(1/0) % result.plate: 识别出的车牌字符串 % result.confidence: 7个字符置信度的均值 img imread(imgPath); % ... 定位、分割、识别流程返回 result end批量验证脚本遍历测试集目录对每张图调用函数并把每张图的识别结果、置信度、是否成功写入表格。这里刻意保留成功和失败样本的完整记录不能只统计成功率数字不然永远不知道失败样本长什么样。files dir(testset/*.jpg); results table(); for i 1:numel(files) [~, name, ~] fileparts(files(i).name); r plate_recognition(fullfile(files(i).folder, files(i).name)); results [results; {name, r.plate, r.success, r.confidence}]; end % 统计整体成功率 success_rate sum(results.Var3) / height(results); disp([识别成功率: , num2str(success_rate)]);跑完一轮之后我会把所有失败样本的关键参数记录成一致性表格是哪一步失败的定位、分割、识别、该步当时的阈值参数、失败原因描述。参数自检的价值在这个环节才真正体现出来——如果三张失败图都是定位阶段被长宽比筛掉的那就是宽高比范围的问题如果都是识别置信度低那就是模板库覆盖不足。从那以后我每次拿到一组新测试集都会强制自己走一遍这张自检表再谈识别率。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。