尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

夜间车牌识别MATLAB实现:图像预处理与定位分割全流程解析

夜间车牌识别MATLAB实现:图像预处理与定位分割全流程解析 简介本资源是一套面向计算机视觉初学者与MATLAB图像处理实践者的夜间车牌识别完整实现方案聚焦低光照、反光、阴影等复杂夜间场景下的车牌定位与字符识别难题适用于智能交通监控、停车场管理系统等实际应用开发参考。压缩包仅含2个核心文件1个MATLAB主程序.m文件与1张测试用夜间车牌图像.jpg总大小34KB轻量简洁便于快速运行与代码级学习其中.m文件封装了图像预处理自适应直方图均衡化高斯滤波、基于边缘与颜色特征的车牌粗定位、连通域分析驱动的字符分割以及采用模板匹配或简易SVM实现的字符识别全流程。目前已有279人学习下载代码结构清晰、注释充分配套实测图像可直接验证效果特别适合理解车牌识别四阶段逻辑、掌握MATLAB图像处理工具箱关键函数调用及夜间图像增强策略的实际落地方法。 看到“夜间车牌识别MATLAB代码.rar”这个文件名我太有共鸣了。这个题目几乎是图像处理课程设计和毕业设计里的常青树每年都有人到处找源码、下压缩包结果解压出来要么是白天场景的代码一放到夜间图片上直接翻车要么代码跑起来各种报错缺工具箱、缺函数折腾几天最后还是没跑通。这篇东西我想认真聊聊夜间车牌识别这整条链路。你搜这个标题大概率不是真的只想要一个rar文件而是想要一套能跑通、能出结果、能写进报告里的完整方案。这篇文章会从夜间图像的预处理开始到车牌定位、字符分割、字符识别把每一步的原理、代码、参数调优和踩坑经验都拆开讲一遍。无论你是做课程作业、毕业设计还是单纯想入门MATLAB图像处理都可以照着这套思路复现出属于自己的车牌识别程序而且专门针对夜间场景做了适配。1. 项目需求与整体技术路线1.1 夜间车牌识别难在哪先说一个很多人容易忽略的前提夜间车牌识别和白天车牌识别本质上不是同一道题。白天光照均匀车牌底色和字符的颜色对比非常明确用颜色特征或者简单的灰度阈值就能很干净地分割出车牌。但到了夜间情况就完全不一样了。我总结夜间场景的三大难点。第一是整体光照不足车牌区域的灰度值普遍偏低字符和底色之间的对比度被压缩得很小直接用固定阈值二值化字符很容易断成一截一截的。第二是局部强光干扰对面车大灯打过来、路灯照射角度刁钻、路面反光、车牌表面反光都会造成局部过曝或光晕让原本简单的“蓝底白字”变成一团亮斑。第三是成像噪声明显夜间摄像头为了补偿进光量往往会拉高ISO图像上会出现明显的颗粒噪声这些噪声在后边缘检测阶段极容易被当成伪边缘导致定位误判。所以一套“白天能跑”的车牌识别代码到了夜间大概率会输出一个空的BoundingBox或者把车灯、车身反光条、甚至路牌当成了车牌。如果你下载的rar里没有针对夜间的预处理模块那它基本就是废的。1.2 为什么选MATLAB而不是YOLO我清楚热搜词里也有“yolo 车牌识别”很多同学会犹豫既然深度学习这么火是不是直接上YOLO更省事我的观点是分场景看。YOLO的优势很明显泛化能力强白天、晚上、雨天、不同角度都能扛但代价是你要准备标注数据集、搭训练环境、调参、部署模型。在课程设计或毕业设计里这一整套流程的工程量会大很多而且答辩的时候老师更关心的往往是“你用了什么原理、每一步为什么这么设计”而不是“你调了一个多厉害的预训练权重”。传统图像处理方案在这一点上天然有优势每个环节都是透明可解释的灰度图、边缘图、连通域、投影曲线都可以可视化出来放在报告里老师一眼就能看懂。MATLAB做这件事尤其合适。它的Image Processing Toolbox把这些操作都封装成了现成函数十几行代码就能完成一个预处理或定位步骤开发效率非常高。而且MATLAB的矩阵运算语义让图像处理代码读起来非常直观不用像C那样关心内存和指针。对于教学演示、算法验证和课程项目来说MATLAB几乎是最优解。所以这篇博文的完整技术路线是输入夜间图像 → 图像增强预处理 → 边缘检测形态学定位车牌区域 → 字符分割 → 字符模板匹配识别 → 输出车牌字符串。这条链路每一步都有明确的数学原理支撑也都有对应的MATLAB函数可以调用后面我会逐个拆开讲。2. 夜间图像预处理把暗部细节挖出来2.1 先用CLAHE做局部对比度增强预处理是整个夜间识别流程里最关键的一步前期图像质量上去了后面定位和分割的难度会直线下降。很多人一上来就写histeq(gray)做全局直方图均衡但我在夜间场景实测下来效果并不好。原因是夜间图像里暗像素占绝对多数全局直方图均衡会把大量灰度级分配给暗区亮区被压缩结果是噪声被过度放大图像整体发灰字符边缘反而模糊了。更加合适的做法是用限制对比度自适应直方图均衡也就是CLAHEMATLAB里对应adapthisteq函数。CLAHE和普通直方图均衡的区别在于它把图像分成一个个小块Tile在每个小块内部做直方图均衡然后通过双线性插值消除块间边界同时对每个小块的对比度拉伸幅度做了限幅避免噪声被无限放大。这个特性天然适配夜间图像因为它能同时照顾到暗区和亮区的细节。我常用的参数是这样enhanced adapthisteq(gray, NumTiles, [8 8], ClipLimit, 0.02);这里NumTiles是分块数默认8×8实际使用中如果是分辨率较高的图像可以适当增加到16×16让局部对比度增强得更细如果图像本身就很模糊分块太多反而会产生块状伪影所以8×8是稳妥起点。ClipLimit是对比度限制阈值默认0.01夜间场景我建议从0.02起步如果图像整体太暗可以调到0.03但不要再高否则背景噪声就会被放大出来。这一步做完之后车牌字符和底色的明暗对比会有肉眼可见的提升原本灰蒙蒙的图像会变得层次分明后面处理起来会顺手很多。2.2 顶帽变换与伽马校正处理不均匀光照CLAHE能解决整体对比度不足但夜间图像还有一个头疼的问题是光照不均匀。比如车灯直射车牌一角导致半边亮半边暗或者车牌上方被路灯照到、下方完全是黑的。这种不均匀光照会让二值化阈值顾此失彼。这时我会用形态学顶帽变换来处理MATLAB里就是imtophat。顶帽变换的公式是原图减去开运算结果。开运算是先腐蚀后膨胀会去掉图像中比结构元素小的亮细节保留大面积背景光照。原图减掉这个背景剩下的就是被不均匀光照淹没的细节。打个比方开运算相当于用一块大抹布把图像大面积擦平顶帽变换就是“原图减去抹布擦过的背景”留下的全是小尺度的细节比如车牌字符笔画、边缘纹理而大面积光晕和暗影都被滤掉了。se strel(disk, 15); enhanced imtophat(enhanced, se);这个结构元素半径15是个经验值我一般取字符笔画宽度的2到3倍。如果夜间车牌字符因为低照度变得特别细半径可以降到10如果图像分辨率高、车牌在画面里占得比较大半径可以增加到20甚至25。原则很明确结构元素要比车牌字符大、比光照变化区域小才能准确分离字符和背景光照。如果预处理做完之后图像还是偏暗我会再加一步伽马校正。gamma 1能提亮暗部、压缩亮部适合让车牌字符从暗背景里浮现出来。但我提醒一句伽马校正和CLAHE一样都会放大噪声所以我的建议是能不做就不做只有在图像暗到CLANE之后字符依然不清晰时才考虑避免过度处理。另外有一个顺序问题我实测下来先CLAHE再顶帽变换的效果比先顶帽再CLAHE更好。原因是如果图像太暗顶帽变换会把暗区的细节连同背景一起滤掉反而丢了信息先做CLAHE把细节显出来再做顶帽只滤大尺度光照信息保留更完整。这套组合拳打完夜间车牌图的预处理就基本到位了。3. 车牌定位边缘与形态学的组合拳3.1 为什么夜间不能靠颜色定位车牌白天定位车牌最常用的方法是颜色阈值法把RGB转到HSV或LAB颜色空间提取蓝色区域再用形态学处理得到车牌候选框。这个方法在白天确实又快又准因为蓝底车牌的色相特征非常明显。但夜间这条路走不通。我实测过夜间图像在HSV空间里蓝色通道的S饱和度极低因为暗光环境下彩色信息本来就弱再经过AWB白平衡漂移车牌底色的色相可能偏到完全不是蓝色。除非你专门装了红外补光灯且摄像头支持彩色夜视否则颜色阈值法在夜间几乎必挂。所以夜间定位我改用“结构特征纹理特征”的思路。车牌区域是什么是一块矩形区域里面有多个字符字符之间有间隔整体形成密集的竖直边缘纹理。在图像处理里这意味着车牌区域在边缘图中会表现为一块高密度、高连通性的亮斑区并且这个区域的宽高比非常有辨识度一般车牌宽高比在3到5之间。这个思路的好处是不依赖光照颜色只要边缘能检测到定位就有戏。而夜间图像经过第一节的预处理增强后边缘信息会被清晰保留所以这条路线在工程上更稳。3.2 边缘检测、闭运算与候选框筛选流程整个定位阶段我分五步走每一步都用MATLAB现成函数实现代码不长但每一步都有讲究。第一步做竖直边缘检测。车牌字符大多是横竖笔画组合竖直边缘最密集所以我用Sobel算子的竖直方向模板bwEdge edge(enhanced, sobel, vertical);edge函数会自动选阈值夜间图像我建议用默认自动阈值不要自己设置固定阈值因为不同图片亮度差异太大了。如果车牌在图像里很小也可以考虑用canny得到更完整的边缘但Canny产生的边缘点更多对噪声也敏感后续闭运算容易把干扰区域也连接起来我一般从Sobel竖直边缘开始。第二步做闭运算连接断裂边缘。字符边缘在夜间可能断断续续的字符之间的空隙也会把同一车牌的边缘分成好几段所以需要把邻近的边缘连成块。闭运算是先膨胀后腐蚀可以填补小空洞、连接断开的区域se strel(rectangle, [15 15]); bwClose imclose(bwEdge, se); bwClose imfill(bwClose, holes);这里结构元素尺寸很关键。15×15是我在分辨率约1000像素宽图像上的经验值。如果图像分辨率低、车牌占比小这么大的结构元素会把整个车牌区域膨胀成一个很大的块甚至把旁边区域都连进来分辨率高则可以适当加大。一个更智能的做法是根据图像宽度动态设置seSize round(size(gray,2)/50)再和5取最大值这样不同分辨率图都能自动适配。imfill填充内部空洞是因为车牌字符内部有封闭笔画填充后车牌区域更完整。第三步用bwareaopen去掉面积太小的连通域这些大概率是路牌文字、车身贴纸或者噪声边缘。我一般设面积为200像素分辨率高时按round(totalPixels/2000)动态调整。第四步用regionprops提取每个连通域的BoundingBox然后根据车牌的几何先验筛选候选框stats regionprops(bwClose, BoundingBox, Area); imgArea size(gray,1) * size(gray,2); candidates []; for k 1:length(stats) w stats(k).BoundingBox(3); h stats(k).BoundingBox(4); ratio w / h; areaRatio stats(k).Area / imgArea; if ratio 2 ratio 6 areaRatio 0.003 areaRatio 0.08 candidates [candidates; k]; end end宽高比2到6覆盖了标准车牌和部分倾斜透视压缩后的车牌面积占比0.3%到8%用来排除太小的干扰块和太大的车身反光区域。筛选完之后如果有多个候选框我就选面积最大的那个作为车牌。第五步是略微外扩裁剪区域把车牌完整提取出来。我习惯在BoundingBox四周各扩5到10像素防止边缘刚好卡掉字符的一部分x max(1, floor(bestBox(1) - 5)); y max(1, floor(bestBox(2) - 5)); w min(size(gray,2) - x, floor(bestBox(3) 10)); h min(size(gray,1) - y, floor(bestBox(4) 10)); plateImg imcrop(originalImg, [x y w h]);注意裁剪来源要用原始彩色图不要用增强后的灰度图这样后面二值化时还能利用彩色信息做判断。3.3 定位不准时的三个调整方向如果上面这一套跑下来定位失败不要急着改参数先分析具体是哪种情况。我归纳了三个高频问题候选块全无、候选块过多、候选框偏大或偏小。候选块全无大概率是边缘检测太弱了。夜间图像在预处理后如果依然很暗Sobel竖直边缘可能只检测到零星几个点。这时候我会把edge的算子换成canny或者先对增强图再做一次伽马校正提亮让边缘响应更强。如果还是不行问题可能出在预处理参数上回看第二节的ClipLimit是否设得太低。候选块过多说明有大量干扰区域满足筛选条件。通常是闭运算结构元素太大把路牌、车身装饰线也连接成了矩形块。解决办法是缩小闭运算结构元素或者提高面积占比阈值下限。还有一个我经常用的技巧就是在筛选时增加一个先验车牌通常出现在图像的中下区域极少出现在顶部天空或远处背景所以可以对BoundingBox中心位置加一个粗略约束直接排除掉一批干扰。候选框偏大或偏小要分情况看。偏大说明边缘连通域把车牌连带周边区域一起包进来了常见原因是闭运算结构元素太大、或者车牌周围有强纹理比如保险杠格栅。偏小说明车牌字符边缘没有完全连成一体常见原因是字符笔画太细、边缘断裂严重可以适当加大膨胀尺寸后再闭运算。如果在候选阶段发现车牌是倾斜的宽高比会偏离标准范围导致被筛掉。这时候可以在第五步之前加入倾斜校正用regionprops提取连通域的Orientation属性得到车牌倾角再用imrotate做旋转校正或者用仿射变换做透视校正。倾斜矫正对后续字符分割的影响非常大我建议只要发现倾角超过5度就一定要处理否则分割时投影法会完全失效。4. 字符分割与识别4.1 二值化与去干扰定位到车牌区域之后接下来的工作就是把一个个字符单独切出来再送进识别模块。这个阶段的第一件事是二值化。MATLAB里最常用的是Otsu全局阈值法自动计算最佳分割阈值grayPlate rgb2gray(plateImg); th graythresh(grayPlate); bwPlate imbinarize(grayPlate, th);但夜间车牌有个特殊情况如果车牌表面被车灯直射整个区域过曝字符和底色的灰度差会变小Otsu阈值可能把字符和底色混在一起。我在这种情况下会放弃全局阈值改用对预处理后的图像先做顶帽变换再二值化或者直接降采样后用局部自适应阈值。另外要注意底色和字符的亮度关系中国车牌标准是蓝底白字字符亮于底色所以二值化后字符是1白色、背景是0。但如果你处理的图片里是黄底黑字或者黑底白字需要根据情况做~反色否则后面投影法会完全错乱。这里我建议写代码前先imshow看一眼二值图确认字符是白、背景是黑再往后走。二值化之后要去除干扰。车牌框上的铆钉、边缘、残留边框都会干扰字符分割。我的做法是先做连通域分析用bwareaopen删除面积小于30像素的小块再用水平投影找到字符主体区域把上下边框和多余的空白裁掉horzProj sum(bwPlate, 2); rowStart find(horzProj 0, 1, first); rowEnd find(horzProj 0, 1, last); bwPlate bwPlate(rowStart:rowEnd, :);左右边框我一般在垂直投影分割时顺带处理因为边框会让垂直投影的波谷变浅影响切分。4.2 垂直投影分割的细节与坑去完干扰之后字符分割我用的是垂直投影法。原理很简单统计二值图像每一列上白色像素的数量字符笔画区域白色像素多字符间隔区域白色像素几乎为零。这样从左到右扫描投影向量连续的非零区间就是一个字符的边界。但实际操作有几个坑相当经典。第一个坑是汉字被拆开。比如“京”、“沪”、“苏”这类结构复杂、左右有分离部分的汉字垂直投影在汉字中间可能出现明显的零值波谷导致一个字被拆成两截。我的对策是设定一个最小字符宽度阈值如果切出来的片段宽度小于整体字符平均宽度的三分之一就把它和相邻片段合并。还有一个更简单的方法中国民用车牌固定是7位字符第一位汉字、第二位字母、第三到七位字母数字混合我可以在切割时用期望字符数7做约束如果切出来的片段数超过7就计算每个片段的宽度把明显偏小的片段合并到相邻片段。第二个坑是字符和边框粘连。如果二值化之后左边框或者右边框和第一个/最后一个字符连在一起垂直投影波谷会消失。我的处理顺序是先去掉上下边框再用连通域分析找到图中最左侧和最右侧的连通域如果它们的宽度超过剩余字符宽度的1.5倍就判定为边框并切除。这个方法不能100%有效但大部分标准车牌图像都能处理好。第三个坑是字符断裂。夜间图像字符笔画可能因为阈值分割问题出现局部断裂导致同一个字符产生多个连通片段投影时表现为一个字符被切成几段。我会在二值化之后、投影之前对图像做一次形态学膨胀让断开的笔画重新连接起来。注意膨胀核不能太大我用3×3的矩形核就够了太大容易让相邻字符粘连。投影分割后的代码骨架大概是这个样子vertProj sum(bwPlate, 1); inChar false; charStart []; charEnd []; for col 1:length(vertProj) if vertProj(col) 0 ~inChar inChar true; charStart [charStart, col]; elseif vertProj(col) 0 inChar inChar false; charEnd [charEnd, col]; end end然后根据合并规则处理片段再用imcrop把每个字符图像裁出来。最后做一个字符归一化把每个字符统一缩放到固定的高度和宽度比如20×10像素这样识别模块的输入就统一了。4.3 模板匹配识别怎样让结果更稳字符识别部分对于课程设计和毕业设计来说模板匹配是最直观、最好解释、也最容易实现的方法。思路就是准备一套标准字符模板把切割出来的字符和模板逐一比较相似度最高的那个字符就是识别结果。归一化后的字符图像尺寸建议统一为20×10高度×宽度这个比例接近中国车牌字符的实际比例。然后和模板比对最常用的相似度度量是归一化互相关系数MATLAB里可以直接用corr2或者normxcorr2score corr2(charImg, templateImg);corr2计算的是两张图的皮尔逊相关系数对整体亮度变化不敏感适合处理夜间图像那种灰度不均的情况。我建议比较前先把字符图像和模板图像都做一次二值化然后再计算相关系数。因为夜间灰度图像里的光照渐变会影响相关系数二值化之后只剩形状信息鲁棒性更好。模板库怎么建我建议自己从真实车牌里收集。不同省简称汉字、24个字母I和O一般不出现、10个数字加起来大概60类左右。每类多收集几个不同风格的样本比如粗体、细体、不同字体识别时对每个模板算相关系数取平均值最高的类别作为最终结果。这个方法比单模板匹配要稳很多我实测能把识别率从70%左右拉到85%以上。还有一个小技巧汉字、字母、数字分开匹配。车牌的7位字符中第一位肯定是汉字第二位肯定是字母第三到七位是字母数字混合。所以在识别时第一位只在汉字模板库里比对第二位只在字母模板库里比对后面几位才在全部字母数字库里比对。这样做的好处是汉字“皖”和字母“H”在某些字体下很像如果不分开匹配很容易误判分开之后误判概率大幅下降。模板匹配的局限也很明显它对字体变形敏感如果车牌子被刮花、字体特异、或者倾斜矫正没做好识别率会掉得很快。如果你的测试集里出现了大量这类样本建议在这个环节换成基于统计特征的分类器比如用HOG特征SVM或者直接上简单的CNN。但作为课程项目模板匹配已经足够了而且它的原理在报告里特别好讲。5. 完整MATLAB代码结构与核心代码解析5.1 主函数一个入口跑完整流程前面几节讲的是单点技术这节我把整个流程串成一个完整的MATLAB程序。我习惯把整个项目拆成主函数和几个子函数预处理、定位、分割、识别各写一个函数主函数只做流程控制。这样一个模块一个模块调试哪里出问题就去改哪个函数比写一个大脚本舒服得多。主函数代码function result night_plate_recognition(imgPath) % 夜间车牌识别主函数 % 输入imgPath 图像路径 % 输出result 车牌字符串 img imread(imgPath); if size(img, 3) 3 gray rgb2gray(img); else gray img; end % 1. 预处理 enhanced preprocess(gray); % 2. 车牌定位 plateImg locatePlate(enhanced, img); if isempty(plateImg) warning(未定位到车牌区域); result ; return; end figure, imshow(plateImg), title(定位到的车牌); % 3. 字符分割 chars segmentChars(plateImg); if isempty(chars) warning(字符分割失败); result ; return; end % 4. 字符识别 result recognizeChars(chars); fprintf(识别结果%s\n, result); end主函数里我每个步骤结束后都会检查返回结果是否为空这样可以快速定位是哪一步出了问题。调试的时候也可以用figure把每一步的中间结果打出来可视化方式排查效率非常高。5.2 定位与分割子函数实现预处理函数和定位函数的代码在前文已经给出了核心部分这里我把它们补完整方便你直接复制参考。定位函数完整版function plateImg locatePlate(enhanced, originalImg) % 边缘检测 bwEdge edge(enhanced, sobel, vertical); % 动态计算闭运算结构元素大小 seSize max(round(size(enhanced, 2) / 50), 5); se strel(rectangle, [seSize seSize]); bwClose imclose(bwEdge, se); bwClose imfill(bwClose, holes); % 去除小连通域 minArea round(size(enhanced, 1) * size(enhanced, 2) / 2000); bwClose bwareaopen(bwClose, minArea); % 连通域分析与筛选 stats regionprops(bwClose, BoundingBox, Area); imgArea size(enhanced, 1) * size(enhanced, 2); bestBox []; bestArea 0; for k 1:length(stats) box stats(k).BoundingBox; w box(3); h box(4); ratio w / max(h, 1); areaRatio stats(k).Area / imgArea; if ratio 2 ratio 6 areaRatio 0.003 areaRatio 0.08 if stats(k).Area bestArea bestArea stats(k).Area; bestBox box; end end end if isempty(bestBox) plateImg []; return; end % 外扩裁剪 x max(1, floor(bestBox(1) - 5)); y max(1, floor(bestBox(2) - 5)); w min(size(originalImg, 2) - x, floor(bestBox(3) 10)); h min(size(originalImg, 1) - y, floor(bestBox(4) 10)); plateImg imcrop(originalImg, [x y w h]); end这里闭运算结构元素大小是动态计算的seSize max(round(图像宽度/50), 5)。我的思路是图像宽度50等分大概对应车牌宽度的量级这样不同分辨率的图像都能保持相近的归一化效果。如果你的摄像头分辨率特别高比如4000像素宽seSize会到80左右可能偏大这时可以测试一下如果候选块连成一片就调小分母。分割函数里我补上投影分割和合并逻辑function chars segmentChars(plateImg) grayPlate rgb2gray(plateImg); th graythresh(grayPlate); bwPlate imbinarize(grayPlate, th); % 如果字符是暗色黄底黑字等反色 % if mean(bwPlate(:)) 0.5 % bwPlate ~bwPlate; % end % 形态学修复断裂 bwPlate imdilate(bwPlate, strel(square, 3)); bwPlate bwareaopen(bwPlate, 30); % 去掉上下边框 horzProj sum(bwPlate, 2); rowStart find(horzProj 0, 1, first); rowEnd find(horzProj 0, 1, last); if isempty(rowStart) chars {}; return; end bwPlate bwPlate(rowStart:rowEnd, :); % 垂直投影分割 vertProj sum(bwPlate, 1); inChar false; cuts []; for col 1:length(vertProj) if vertProj(col) 0 ~inChar inChar true; cuts(end1, 1) col; elseif vertProj(col) 0 inChar inChar false; cuts(end, 2) col - 1; end end if inChar cuts(end, 2) length(vertProj); end % 片段合并过滤过窄片段 avgWidth mean(cuts(:,2) - cuts(:,1) 1); chars {}; for i 1:size(cuts, 1) segWidth cuts(i,2) - cuts(i,1) 1; if segWidth avgWidth * 0.3 continue; end charImg bwPlate(:, max(cuts(i,1)-1,1):min(cuts(i,2)1,end)); chars{end1} imresize(charImg, [20 10]); end end这段代码里我做了两个关键处理一个是在垂直投影前先做一次3×3膨胀用来修复夜间字符断裂另一个是合并过窄片段时用平均宽度的0.3作为阈值保证汉字不会被拆成碎片。你实际调试时如果发现字符被拆开优先调整膨胀核尺寸和这个阈值。5.3 运行环境与参数调节入口最后提醒一下运行环境。这套代码需要MATLAB R2016a以上版本Image Processing Toolbox是必需的adapthisteq、imtophat、regionprops、imbinarize这些函数都来自这个工具箱。教育版和正版授权版都能跑如果你的MATLAB版本比较老注意imbinarize在老版本里叫im2bw需要做一下替换。运行时如果图像分辨率很高比如单反拍的4000×3000建议先缩放一下。我一般把图像长边缩放到1000到1600像素之间再送进识别流程否则闭运算、连通域分析这些操作在大图上会非常慢而且很多基于像素的经验参数都要跟着改一遍。缩放这一步对识别精度的影响不大因为在车牌识别这个场景里车牌区域在整个画面中的相对大小不会因为缩放而改变。参数调节的入口我总结为四个预处理阶段的ClipLimit、NumTiles定位阶段的闭运算结构元素大小和面积阈值分割阶段的膨胀核大小和合并阈值识别阶段的模板库。每次只调一个参数观察它对结果的影响这样你才能建立起“参数-效果”的直觉而不是盲目乱试。6. 夜间场景高频问题与排错实录6.1 高频问题速查表我把夜间车牌识别里最常遇到的问题整理成了下面这张表你可以直接对照排查。现象可能原因解决方向整体识别结果为空定位阶段未找到候选框检查预处理是否过暗改用Canny边缘调大ClipLimit定位框框住车灯或反光区干扰区域边缘密集且形状接近车牌增加位置先验约束调严面积占比阈值用灰度方差滤除均匀亮块定位框包含半个车身闭运算结构元素过大边缘连成片调小seSize或在筛选时增加宽高比上限字符分割出6个或8个片段字符断裂或字符粘连调整膨胀核大小调整合并阈值检查边框是否去除干净第一个汉字被拆成两半汉字内部垂直投影出现零值波谷降低合并阈值按期望字符数7做合并修正识别结果错字多模板库样本太少或字体不匹配扩充模板库多模板投票汉字和字母数字分开匹配二值化后字符和背景混在一起车牌表面反光或过曝先顶帽变换再二值化或用局部自适应阈值图片很暗但处理后噪声爆炸CLAHE的ClipLimit设太高降低ClipLimit在CLAHE前先做降噪滤波这张表是我实际调试中反复踩过的坑里面每一行背后都是一个真实场景。建议你调试时遇到问题先对着表找原因比自己瞎试参数高效得多。6.2 一次雨夜车牌的完整排错过程分享一个真实案例有个雨天夜里的测试图车灯直射车牌路面反光严重车牌上全是水珠。我第一次跑这套流程时定位框直接框到了车灯下方的反光条上车牌区域完全没被选中。排查过程是这样的我先看了预处理后的增强图发现车牌区域因为水珠反光字符边缘被强光淹没边缘图里车牌区域的响应反而不强。而反光条区域是一块均匀高亮的长条闭运算后形成了宽高比非常接近车牌的大块连通域于是被误判成了候选框。针对这个情况我做了三个调整。第一在预处理阶段把顶帽变换的结构元素半径从15降到10优先保留更细的字符边缘。第二在筛选阶段增加了一个图像标准差约束计算每个候选框对应原图区域的灰度标准差车牌区域因为字符笔画多方差应该大于均匀反光区域。如果候选框的标准差很低说明里面是平整的亮块直接排除。第三把位置先验加了进去限定候选框中心必须在图像高度的20%到80%之间。调整之后再跑定位框准确落在了车牌区域。虽然分割阶段因为水珠导致“京”字出现了一处断裂但膨胀修复后还是正确切出了7个字符最终识别结果完全正确。这个案例说明夜间识别遇到的问题通常不是单一原因而是多个因素叠加需要一层一层的筛选条件把它筛出来。6.3 几条写在最后的实战建议整个项目做下来我最大的体会是夜间车牌识别真正的瓶颈不在算法模型有多先进在于你能否把每个环节的中间结果搞清楚。第一一定要可视化中间结果。我调试时每个步骤都会用figure打印出来增强图、边缘图、闭运算图、候选框、二值图、分割出的字符。这不是浪费时间是让你能看到算法“在想什么”。很多时候一眼扫过去就知道该调哪个参数比一遍遍跑全流程快得多。第二参数不要写死尽量根据图像尺寸动态计算。不同测试图片分辨率不一样固定参数在不同图上表现差异很大。我上面给出的seSize max(round(图像宽度/50), 5)就是一种动态计算方式你可以根据自己的测试集微调这个换算公式。第三始终以“定位成功”和“分割正确”为最高优先级。识别环节错了只影响一个字符但定位错了或者分割错了整个流程就白跑。所以先保证前两步的稳定性再回头优化识别率。第四如果要做扩展我建议往两个方向走。一是把字符识别从模板匹配换成HOG特征SVM或者一个简单的CNN识别率会有明显跃升二是把整条流程封装成一个函数批量处理一个文件夹里的夜间图片再加上简单的车牌字符后处理逻辑比如根据车牌规则修正非法字符就可以作为一个小型演示系统了。深度学习方向可以参考YOLO车牌检测的思路但先把传统方法跑通你会对图像处理的每一步都有真正的理解。最后再分享一个小经验调试时准备一组固定的测试图片至少5到10张覆盖不同角度、不同光照、不同背景每次修改参数后完整跑一遍这组图记录每张图的通过情况。这样你不会陷入“改好了这张、弄坏了那张”的循环整个项目的稳定性会肉眼可见地提升。本文还有配套的精品资源点击获取
返回列表