
简介本资源聚焦夜间低照度场景下的车牌识别技术面向图像处理初学者、计算机视觉学习者及智能交通系统开发者解决光照不足导致的对比度低、噪声干扰强等实际识别难题。压缩包共3个文件包含1个实操讲解MP4视频演示MATLAB全流程实现、1个核心算法脚本chepai.m涵盖预处理、Canny边缘检测定位、连通域分割及字符识别逻辑和1张典型夜间车牌测试图总大小7.58MB结构精炼便于快速上手。已有128人下载学习资源内容紧扣工业应用需求覆盖直方图均衡化增强、高斯滤波降噪、二值化处理、模板匹配定位、投影分析分割及SVM字符分类等关键技术环节所有代码均基于MATLAB Image Processing Toolbox与Machine Learning Toolbox编写注释清晰可直接运行调试并拓展至其他复杂光照场景。1. 夜间车牌识别为什么难以及方案怎么选先把这个项目说清楚基于MATLAB的夜间车牌识别本质是一套完整的数字图像处理流程——从夜间采集到的车辆图像里先定位到车牌区域再把车牌上的字符一个个切出来最后识别成文本。难点全在“夜间”两个字上环境光不足、车灯过曝、路灯色偏、阴影干扰各个环节都会让图像质量断崖式下跌。这套项目我当年是在MATLAB R2018b上跑的用到的工具箱就是Image Processing Toolbox代码量不大但每一步的参数调整都挺磨人。先说结论夜间车牌识别适合谁来参考三类人。第一类是正在做MATLAB图像处理大作业的学生这个题目几乎是课程设计的标配第二类是做智能交通、停车场道闸相关项目的工程师虽然工业落地现在更多用YOLO这类深度学习方案但理解传统图像处理的链路仍然很有价值第三类是刚接触图像处理、想系统走一遍“预处理→定位→分割→识别”完整流程的初学者。这套流程一旦跑通你对灰度变换、边缘检测、形态学操作、连通域分析这些基础概念的理解会扎实很多。1.1 夜间场景的三个核心难点白天做车牌识别图像光照均匀、对比度高直接灰度化后二值化就能看到清晰的字符轮廓。夜晚则完全不同我实际调试时遇到的主要有三类干扰。低照度与动态范围不足。夜间图像整体偏暗灰度值集中在低区间车牌的蓝色底和白色字符之间的对比度被压缩。如果摄像头素质一般还会伴随大量传感器噪点这些噪点在后续边缘检测时会被放大成大量伪边缘。直接用全局阈值做二值化很容易把车牌区域淹没在黑暗背景里。车灯强光与反光。这是夜间特有的问题。迎面车辆的大灯直射、路面反光、车牌表面的镀层反光会造成局部区域过曝。过曝区域的灰度值直接被推到255附近字符细节全部丢失。更麻烦的是反光会导致车牌区域内的灰度分布极不均匀一半亮一半暗单一阈值根本无法同时兼顾。色偏与复杂光源。夜间的光源种类多——路灯是钠灯的暖黄色LED灯是冷白色车灯偏白偏蓝。不同色温的光源会让车牌颜色产生偏移蓝色车牌的蓝色通道值在不同光源下差异很大。如果在HSV空间做颜色阈值分割会发现色相范围很难用一个固定区间覆盖所有场景。1.2 传统图像处理 vs 深度学习为什么还选MATLAB传统方案2024年以后工业上做车牌识别基本都用YOLO系列检测车牌位置配合CNN或者LPRNet做字符识别准确率可以做到99%以上。那为什么这个项目还要用MATLAB传统图像处理方案这里有一个被很多人忽略的现实问题深度学习方案对数据量和算力的要求和学生课程设计、个人练习的场景根本不匹配。传统方案的核心优势是“可见即可调”。每个环节都透明可控边缘检测效果不好就换算子或者调阈值形态学操作结果不理想就调整结构元素尺寸连通域筛选条件不对就改长宽比范围。你清楚地知道每一步做了什么、为什么这么做、效果不好是哪里出了问题。而深度学习方案是个黑盒训练数据不够时泛化能力很弱换个场景就失效出问题时排查链路完全不可见。MATLAB在这条链路里还有一个天然优势图像处理工具箱函数齐全且调试体验极佳。imshow可以实时查看每步结果变量窗口可以随时检查矩阵的尺寸和数值分布这对于理解算法内部发生了什么非常有帮助。相比之下用OpenCV在C里做同样的事情光是在命令行里反复imshow和waitKey就够折腾半天的。2. 预处理把夜色里的车牌“捞”出来整个识别流程的第一步是预处理。这一步不做好的话后面所有环节都会出问题。我用一句话总结预处理的目标让车牌区域和背景的差异最大化同时抑制噪声干扰。2.1 灰度化与光照补偿直方图均衡化到底改了什么拿到夜间图像的第一步是读入并转为灰度图。MATLAB里的rgb2gray本质是按亮度公式加权求和Gray 0.299R 0.587G 0.114B。这个加权系数不是随便定的它模拟了人眼对绿色最敏感、对蓝色最不敏感的特性。但灰度化之后夜间图像的直方图通常集中在低灰度区间比如0到80之间对比度很差。此时直接做后续操作效果都不理想需要先做光照补偿。我试过两种方案直方图均衡化histeq和自适应直方图均衡化adapthisteq。histeq的原理是把灰度直方图映射成均匀分布拉伸动态范围到0~255。效果是整体对比度显著提升但问题是它会放大噪声尤其是夜间图像本来噪点就多均衡化后噪点变得更明显。adapthisteq则把图像分成小块分别做均衡化对局部细节的增强效果更好但处理速度慢且容易在块边缘产生不自然的过渡。实际项目中我建议先用histeq做全局增强再用高斯滤波或者中值滤波去噪。滤波核大小取3×3到5×5之间比较合适核太小去噪不干净核太大字符边缘会被模糊掉。这一步的代码如下img imread(night_car.jpg); gray rgb2gray(img); enhanced histeq(gray); filtered imgaussfilt(enhanced, 1.2); % 高斯滤波sigma取1.2左右注意imgaussfilt的sigma参数。我见过很多人用fspecial自己造高斯核但实际上MATLAB的imgaussfilt更高效、边界处理更合理。如果在旧版本上没有这个函数可以用filtered imfilter(enhanced, fspecial(gaussian, [5 5], 1.2));替代。2.2 边缘增强与二值化全局阈值和自适应阈值怎么选预处理阶段的另一个关键操作是二值化。二值化的本质是把灰度图变成0和1两个值为后续的边缘检测和形态学操作做准备。夜间图像二值化最大的坑是“一刀切”失效——全局阈值要么保住了亮区域丢了暗区域要么反过来。MATLAB里最简单的全局阈值是im2bw(gray, level)level可以手动指定也可以通过graythresh函数自动计算Otsu阈值。Otsu算法的原理是最大化类间方差它会自动找到一个把前景和背景分得最开的阈值。但Otsu有一个前提假设图像灰度直方图呈双峰分布。夜间图像往往不是双峰而是单峰偏暗、拖个长尾此时Otsu算出来的阈值会偏向灰度值较高的区域导致暗部细节丢失。我的做法是分两步走。先用Otsu算一个初始阈值然后手动调整。实测下来夜间车牌图像的阈值在0.35到0.55之间效果较好前提是经过histeq增强后。如果需要自适应能力可以用adaptthresh函数bw imbinarize(filtered, adaptthresh(filtered, 0.4));其中0.4是灵敏度参数值越大保留的细节越多同时噪声也越多。这个函数对光照不均的图像效果明显优于全局阈值但代价是速度慢一些对于单张图像处理来说可以忽略。这里有一个容易被忽视的关键点二值化之后要做取反操作确保车牌字符是白色1背景是黑色0。因为后续的形态学处理和连通域分析默认操作对象是白色区域。如果车牌区域的字符是白的但底是深色的二值化后字符是1、底是0那定位出来就不是车牌连通的区域而是字符本身了。这个方向性错误我犯过好几次每次都要在代码里检查一下figure; imshow(bw);肉眼看一眼车牌区域应该是白底黑字还是黑底白字确认后再往下走。3. 车牌定位形态学操作与连通域筛选细节预处理完成后下一步从整张图里把车牌区域“框”出来。这一步是整个项目的核心环节也是最考验参数调试经验的部分。3.1 边缘检测算子怎么选Sobel、Canny还是Laplacian定位车牌的思路基于一个先验知识车牌区域内部有多个字符字符边缘密集形成“高频纹理区”。用边缘检测算子把图像中的边缘提取出来再进行形态学闭运算让字符边缘连成一片就能得到一个矩形的候选区域。MATLAB里的edge函数支持多种算子我实测下来的感受是Sobel算子简单快速对水平边缘和垂直边缘响应较好但对噪声敏感度偏高。夜间图像噪点多时Sobel会产生大量伪边缘反而干扰后续处理。Canny算子双阈值处理对噪声抑制效果最好边缘连续性强。但Canny的滞后阈值不好调低阈值太高则边缘断裂低阈值太低则噪声残留。Laplacian算子二阶微分算子对噪点极度敏感夜间场景基本不建议用。我在这个项目里最终选择的是Sobel但做了一步关键优化先用高斯滤波把噪点压下去再做边缘检测。edges edge(filtered, sobel);实测下来Canny在这个场景的效果确实更精细但对夜间车灯过曝区域会产生大量密集边缘反而不利于后续的形态学连接。Sobel输出的边缘虽然粗一些但大块连续区域的轮廓比较干净对于定位车牌这种“找大矩形”的任务反而更合适。这里有一个很多人不知道的经验edge函数默认Sobel阈值是自动计算的。如果觉得边缘太密可以手动指定阈值edges edge(filtered, sobel, 0.05);阈值越低保留的边缘越多阈值越高保留的边缘越少。夜间图像建议先试默认值如果边缘过多就逐步调高直到“能看清车牌轮廓但背景边缘明显减少”为止。3.2 形态学闭运算结构元素用什么形状、多大尺寸边缘检测得到的结果是细线字符的边缘线之间还有空隙和断开。需要用形态学闭运算imclose把相邻的边缘“缝合”起来形成一个完整的连通区域。闭运算 先膨胀后腐蚀它的效果是填充小缝隙、保持原区域面积基本不变。这里的核心参数是结构元素strel的形状和大小。我调试的结果是直接用矩形结构元素尺寸大约为字符宽度的一半。为什么如果结构元素太小缝隙填不上如果太大会把周围的干扰区域也连进来。车牌字符宽度大约是15~25像素视图像分辨率而定所以结构元素宽度取8~12像素比较合适。se strel(rectangle, [11 11]); closed imclose(edges, se);注意这里用的是默认的矩形结构元素。有人喜欢用strel(line, len, deg)这种方向性结构元素比如横向线段结构元素它对连接水平方向的字符边缘很有效但对垂直方向的断裂无能为力。个人建议先用矩形结构元素“打底”如果发现某些字符横向断开再补一次水平方向的闭运算。闭运算之后还需要做一次空洞填充imfill(closed, holes)。因为车牌字符是空心结构比如汉字“京”、数字“0”字符内部是背景如果不填充空洞字符内部的孔洞会导致连通域分析时把整个车牌区域拆成多个小区域。这一步看似细节但直接决定后续能不能框出完整的车牌。3.3 连通域筛选长宽比、面积和填充度的标准怎么定闭运算和填充之后的图像会得到若干白色的连通区域其中可能包含车牌候选区也可能包含车身边缘、路面标线、灯柱等干扰区域。用bwlabel标记连通域再用regionprops提取每个区域的属性然后按车牌的先验特征筛选。筛选条件我按优先级排列面积车牌区域占整张图的比例有范围。假设图像尺寸是1080×720车牌大约占2%到6%的面积。过小的直接排除过大的也不可能是车牌。长宽比标准车牌尺寸是440mm×140mm长宽比约3.14:1。考虑拍摄角度和透视变形这个比值在1.8到4.5之间都算合理。如果比值差太多基本可以排除。填充度区域面积占其外接矩形面积的比例。车牌区域是近似实心的矩形填充度应该在0.5以上。纹理复杂、形状不规则的干扰区域填充度通常低于这个值。labeled bwlabel(closed, 8); stats regionprops(labeled, Area, BoundingBox, FilledArea, Extent); candidates []; for k 1:numel(stats) w stats(k).BoundingBox(3); h stats(k).BoundingBox(4); ratio w / h; areaRatio stats(k).Area / (size(img,1) * size(img,2)); if ratio 1.8 ratio 4.5 areaRatio 0.015 areaRatio 0.08 if stats(k).Extent 0.45 candidates [candidates; stats(k).BoundingBox]; end end end如果筛选后仍有多个候选区域可以用启发式策略挑“最像车牌”的那个面积最大的优先长宽比最接近3.14的优先。或者直接显示所有候选框人工确认——在调试阶段这一步能帮你快速判断筛选条件是否合理。3.4 倾斜校正仿射变换解决拍摄角度问题摄像头通常不会正对车牌拍摄而是有一定俯仰角或侧倾角导致车牌在图像中呈梯形或平行四边形。如果不做校正字符分割时投影法的效果会大打折扣。校正的思路是检测车牌的四个角点拟合出四边形的倾斜角度然后用仿射变换或透视变换把车牌区域拉正。MATLAB里可以用fitgeotrans搭配imwarp实现透视校正。具体操作是手工点选四个角点或者用角点检测算法自动找然后映射到固定尺寸的矩形上。这个方法在做毕业设计等需要展示效果时特别加分但要注意如果车牌区域倾斜角度很小5度以内不做校正也能正常分割不必过度优化。4. 字符分割与识别从像素到文本定位到车牌之后后续的任务就是把车牌区域里的字符逐个提取出来然后识别成文本。理论上讲这一步比定位简单但字符粘连、边框干扰、汉字结构复杂等问题依然有不少坑。4.1 投影法分割水平投影和垂直投影的配合标准车牌由7个字符组成第一个是汉字省份简称第二个是字母发牌机关代号后面五个是字母和数字混合。分割字符常用的是投影法先把车牌区域转为二值图车牌字符应为白色然后做水平投影按行累加像素值找到字符的上下边界再做垂直投影按列累加像素值找到每个字符的左右边界。plate_gray imcrop(img, plateBox); plate_bw imbinarize(plate_gray); % 去除边框取中间区域避开上下边框 % 水平投影定位字符上下边界 rowProfile sum(plate_bw, 2); % 垂直投影定位每个字符左右边界 colProfile sum(plate_bw, 1);垂直投影的典型结果是“峰-谷-峰-谷”的交替模式字符区域是峰字符间隔是谷。找谷底的边界就能把字符切成单个。但这里有个经典问题如果字符之间存在粘连比如字符间有污渍或边框残留谷底不为零直接找谷底就会把两个字符切成一个。解决方法是设置一个阈值大于某个值的列才认为是字符区域然后取连续区域的起始和结束位置。阈值一般取最大投影值的10%~20%具体需要根据实际效果调整。第二个经典问题是“1”字太窄。车牌里第一个字母是省份简称的拼音首字母如果恰好是“I”这种细窄字符垂直投影时会因为宽度太小被当成噪声过滤掉。解决方法是分割后检查字符数量如果不足7个就回溯调整阈值把更窄的列段也保留下来。这个回溯逻辑写起来不复杂但对准确率的提升非常明显。最后一个容易被忽略的点是去除边框。车牌外面有一圈细白边如果不先去除垂直投影时边框会和第一个字符粘连在一起导致分割结果错误。我的做法是取车牌区域后先腐蚀掉1~2像素的边缘或者直接取横向中间70%的区域再投影。4.2 尺寸归一化与模板匹配字符分割完成后每个字符图像的尺寸不一致。有些宽一些、有些窄一些必须统一归一化到固定大小才能和模板匹配计算相似度。我统一缩放到42×24像素高×宽和后续模板库保持一致。模板匹配的算法有两种选择一种是直接在MATLAB里用corr2计算两个图像的相关系数相似度最高的模板就是识别结果另一种是把字符图像和模板都转为特征向量用欧氏距离或余弦相似度匹配。corr2的实现更简单但要求模板尺寸和输入尺寸完全一致且对字体粗细变化比较敏感。我试下来发现corr2对标准印刷字体效果不错但遇到车牌字体边缘模糊时容易把“0”识别成“O”、“8”识别成“B”。更稳健的方案是用OCR工具箱MATLAB的Computer Vision Toolbox自带ocr函数对标准车牌字体的识别率很高。但OCR输出的结果可能包含置信度较低的字符且不能自定义字符集。实际项目中我的做法是“双保险”ocr为主模板匹配为辅两者结果不一致时以置信度更高的为准。results ocr(plate_gray, CharacterSet, ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789); recognizedText results.Text;注意汉字“京”“沪”“粤”等不在默认字符集里需要在CharacterSet参数里加上汉字或者单独做汉字模板匹配。这是很多初学者忽略的细节——省份简称用模板匹配处理后面的字母数字用OCR两条路并行。4.3 识别准确率怎么进一步提升如果做完一轮识别发现准确率不理想最常见的瓶颈有两个。第一个是字符分割不干净尤其是“川”这种结构复杂的汉字分割时容易把内部笔画断裂的部分误判成间隙。第二个是字符归一化后的清晰度不够如果原本字符区域只有20像素高放大到42像素后会出现锯齿模板匹配的相关系数自然会下降。一个有效的改进策略是在分割后对单个字符做一次中值滤波和锐化减少放大产生的锯齿。另一个策略是建立多套模板比如粗细两种字体、带边框和不带边框的模板匹配时分别计算取最高的相似度。这些优化做下来识别率可以从60%多提升到85%左右。再往上提升就得靠引入CNN分类器了那属于另一个话题。5. 常见问题与排查技巧实录夜间车牌识别的坑实在太多我在调试过程中积累了不少排错经验这里整理成速查表方便你照着排查。现象可能原因排查思路车牌区域定位不到边缘检测后伪边缘过多或过少检查二值化阈值观察edges图像调整edge阈值定位到多个区域形态学结构元素过大干扰区域连片缩小结构元素尺寸收紧长宽比和面积筛条件定位框只框到半个车牌车牌区域亮度不均部分区域二值化后丢失改用adaptthresh局部自适应二值化字符分割后数量不对字符粘连或细窄字符被过滤降低投影阈值的判断标准增加回溯逻辑字符识别结果混入汉字误差模板匹配对汉字结构敏感汉字单独建模板库字母数字使用OCR图像处理速度很慢adapthisteq和imfill是性能瓶颈全局histeq代替adapthisteq减少imfill调用范围5.1 夜间反光场景的终极处理方案如果车牌区域因为反光变成一片惨白什么边缘、投影、模板匹配全都失效我试过几个方案最终留下一个最有效的组合多帧融合。拍摄连续3~5帧图像每帧反光的位置可能不同取这些帧的中值或者均值可以显著减轻反光区域的影响。这个方案在道闸场景下比较实用因为车辆是静止或低速运动的多帧融合不会产生严重的运动模糊。如果只有单帧图像还有一个曲线救国的思路不直接处理反光区域而是把注意力放在反光区域边缘的字符轮廓上。反光往往只覆盖部分字符未反光的字符仍然清晰先把清晰的字符识别出来剩余字符根据车牌编码规则做推断比如第一位是省份简称、第二位是发牌机关字母、后面是数字字母组合。虽然不如强行识别反光区域可靠但作为兜底方案很实用。5.2 针对“MATLAB在虚拟机上运行慢”的优化建议很多人在虚拟机的Ubuntu里跑MATLAB发现图像处理步骤一多就卡得不行。我在实际测试里发现几个优化点避免在循环里反复调用imshow。每imshow一次就会刷新一次图像窗口这个开销非常大。调试阶段集中用figure展示中间结果确认无误后就把imshow注释掉。优先用向量化操作替代for循环。比如regionprops的输出可以直接用[stats.Area]获取所有面积值不必再写循环。adapthisteq和imfill这种函数对小尺寸图像支持很好但对1080p以上的图像速度明显变慢。可以先把图像resize到合适尺寸比如宽度600像素再做处理保证定位准确率的前提下大幅提升速度。如果机器的CPU核心数多可以尝试parfor并行处理多张测试图片但要先确认每张图片的处理逻辑是独立的。我测试过parfor在图像处理任务上的加速效果在4核虚拟机中大概能提升1.5倍左右不夸张但能感受到差异。5.3 用到的工具箱和相关函数汇总这个项目完整跑下来用到的主要函数都集中在Image Processing Toolbox里。我汇总一下核心函数方便你在MATLAB里查帮助文档灰度化rgb2gray增强histeq、adapthisteq滤波imgaussfilt、imfilter、medfilt2二值化imbinarize、graythresh、adaptthresh、im2bw边缘检测edge形态学strel、imclose、imopen、imfill、imdilate、imerode连通域分析bwlabel、regionprops、bwareaopen图像裁剪imcrop图像变换fitgeotrans、imwarp、imresize识别ocr、corr2如果在运行过程中遇到“未定义函数”的报错基本就是缺少对应的工具箱。检查方式是在命令行输入ver查看已安装的工具箱列表。6. 项目延展从24个样本到批量处理项目做完基本功能后很多人会想扩展成“批量识别多张图片”的模式。这里有一个容易踩的坑不要把单张图片的处理逻辑直接套到批量上。因为每张图片的亮度、角度、车牌位置都可能不同固定的阈值参数无法泛化。我的做法是在主流程里增加一个“参数自适应”环节读取图片后先根据图像的灰度均值自动调整二值化阈值方向再根据检测结果动态调整筛选条件。比如第一轮没检测到车牌区域时自动降低长宽比限制——有可能是车牌在图像里有较大倾斜视觉上的长宽比偏离了标准值。这种“失败重试”机制比用一套死参数跑所有的图片可靠得多。另外输出结果的组织也要提前规划。推荐用结构化表格记录每次识别的文件名、定位框坐标、识别文本、置信度、耗时时长。MATLAB的writetable可以直接把table类型导出为CSV或Excel后续分析改进时非常方便。我自己的习惯是每次批量测试都导出CSV然后对比不同参数组合下的识别率变化用数据说话来调参而不是靠感觉。项目跑通之后还可以往两个方向做进一步扩展。第一个方向是加一个简单的图形界面GUIDE或App Designer让用户选择图片、显示中间结果、输出识别文本作为大作业提交展示时观感会好很多。第二个方向是把识别结果和车辆进出时间、车牌号绑定存储到数据库或Excel里做成一个雏形的停车场管理系统——这块在课程设计答辩时也更容易展现完整度。最后分享一个我踩过很多次坑之后才养成的习惯每次调整任何一个参数都要把当前图片、参数和结果截图保存下来。你可能会觉得“这次只是顺手改一下阈值没必要记录”但等你改了四五个参数之后就很难记得是哪个改动让效果变好的了。有记录地调参比凭感觉调参效率高出一倍不止。如果你在照着这个流程调试时遇到了什么奇怪的图像现象欢迎带着截图和参数来交流这类问题通常看一眼中间结果就能定位。本文还有配套的精品资源点击获取