资讯动态

基于MATLAB形态学的车牌定位与字符提取实践

发布时间:2026/9/16 16:20:39 来源:尧图企业网站定制
简介面向MATLAB图像处理学习者与开发者这份资源以形态学处理为核心完整演示车牌定位与车牌提取流程覆盖灰度化、直方图均衡化、边缘检测、腐蚀膨胀、开闭运算及轮廓筛选等关键环节适合计算机视觉方向入门及课程设计参考。包体共11个文件压缩后约2.28MB包含讲解教程视频、可运行脚本及多张测试图像便于边学边练教程配有操作演示与代码结果解释。目前已有124人学习。通过研读和运行脚本可掌握imerode、imdilate、bwmorph等常用形态学函数在车牌分割中的实际用法并了解从图像预处理到车牌裁切的完整工程思路为后续字符识别和智能交通应用提供扎实基础。1. 车牌定位的工程起点形态学处理在 MATLAB 里的真实价值很多人在接触车牌识别时第一反应是直接上深度学习网络。但在课程设计、毕业设计以及实际嵌入式部署场景里基于形态学处理的车牌定位和车牌提取仍然是性价比最高的传统方案它不依赖 GPU不需要标注数据用 MATLAB 的imerode、imdilate、bwmorph等一组经典算子就能在百万像素图像上跑出稳定结果。这套资源里的Runme.m和car.jpg演示的正是这个思路先用灰度化和边缘检测把字符轮廓强调出来再通过膨胀、闭运算把零散边缘连成完整的矩形区域最后用几何特征从候选区里挑出车牌。适合三类人想快速完成课程设计的学生、需要理解传统图像处理 pipeline 的算法工程师以及想在深度模型前加一层候选区域预处理的开发人员。2. 形态学基础与 MATLAB 函数选用策略从膨胀腐蚀到开闭运算2.1 结构元素如何决定车牌区域的连通性形态学处理的核心不是算子本身而是结构元素。MATLAB 里用strel定义结构元素常见有square、rectangle、line和disk。在车牌定位场景中矩形结构元素是最直接的选择因为车牌字符横向排列字符之间存在固定间隔膨胀操作需要在横向覆盖足够长的距离才能把被字符间隔切断的边缘重新连接成一个整体。这里有一个容易被忽略的问题结构元素尺寸是针对图像分辨率的相对值。资源中的car.jpg如果是 1600x1200 左右的画面用[3 15]的矩形做闭运算比较合适如果换成 800x600 的缩略图同样的结构元素会把整个车头都连成白色区域导致后续连通域分析时误检。所以在实际代码里最好把结构元素尺寸写成基于图像宽度比例的变量而不是写死。2.1.1 矩形结构元素与车牌字符的几何匹配普通蓝底白字车牌上字符宽高比约 1:2字符行高度远小于整体宽度。定义strel(rectangle, [n m])时第一个参数控制纵向尺寸第二个参数控制横向尺寸。做边缘连接时横向尺寸 m 要能覆盖相邻字符之间的空隙通常取 10~25 像素纵向尺寸 n 只要 2~5 像素目的是让膨胀只沿字符排列方向延伸而不是把车牌上下沿的噪声区域一起吞入。如果结构元素的长宽比例设置错误例如使用 5x5 方形的结构元素在路灯或车灯反射较强的图片上膨胀会把左右车灯与车牌误连接成一个大块导致矩形度下降。相反[2 20]这样过扁的结构元素又可能在字符笔画较粗的图片上把字符中间的空洞保留让车牌区域出现大量空洞。因此形态学处理首先是结构元素的设计问题然后才是算子调用问题。2.2 MATLAB 形态学函数参数速查下表整理车牌定位与提取流程中最常用的形态学函数及关键参数方便在实验时快速对照。bwmorph是综合形态变换内部封装了细化、骨架化、端点移除等操作在车牌字符细化时反而更有用。函数操作关键参数典型用途imerode腐蚀strel对象去除细小边缘点imdilate膨胀strel对象连接断裂字符边缘imopen开运算strel对象去除孤立噪声碎片imclose闭运算strel对象填充字符内部空洞bwmorph形态变换clean、majority、thin清除孤点、细化字符开闭运算的顺序值得专门说明。imopen先腐蚀后膨胀会去掉前景上的小凸起但对车牌这种大面积连通区域来说总体形状几乎不变imclose先膨胀后腐蚀能把字符之间的细缝填补上。对于带有反光的路况图先开后闭比先闭后开更稳因为开运算先去掉了由噪点生成的伪前景闭运算再去补真前景内部的空洞二者各司其职。MATLAB 还提供了imreconstruct用于形态学重建但车牌定位场景中很少用到因为重建对结构元素选择更敏感且计算开销大。相比之下bwmorph的majority操作在二值图像中会把某个像素周围 3x3 邻域内超过半数的值赋给该像素用于填充微小孔洞很有效。只是要注意这个操作会轻微改变字符边缘使用次数过多会让字符变胖导致后续切分时相邻字符粘连。2.3 开闭运算组合消除噪声与连接断裂边缘的代码模板下面的代码可以在Runme.m中直接替换预处理部分输入是灰度图输出是用于连通域定位的形态学增强图。% 读入原始图像并转为灰度 img imread(car.jpg); gray rgb2gray(img); % 边缘检测Sobel 算子阈值 0.08 edge_map edge(gray, sobel, 0.08); % 步骤1闭运算连接字符边缘 se1 strel(rectangle, [3 15]); close_map imclose(edge_map, se1); % 步骤2开运算去除孤立噪声点 se2 strel(rectangle, [3 3]); clean_map imopen(close_map, se2); % 步骤3大尺寸膨胀把车牌候选区变成实心块 se3 strel(rectangle, [5 25]); dilated_map imdilate(clean_map, se3);这段代码的三个步骤有明确分工。第一步[3 15]的闭运算负责连接同一水平线上的字符笔画15 像素的横向跨度刚好覆盖常见车牌字符间距如果图像分辨率更高这个数字可以按比例放大到 20~30。第二步的 3x3 开运算用于剔除路面颗粒和树叶造成的小面积边缘。第三步[5 25]是粗定位的关键它把还没完全连成一片的车牌轮廓整体扩张成块状让后续bwlabel能一次性框住整个车牌区域。调节参数时优先看中间结果。如果close_map中车牌字符边缘没有连成线说明se1横向尺寸太小如果clean_map中出现了长条状噪声说明se2尺寸太大。资源中的M.bmp和8.bmp是不同光照下的测试图片建议修改参数后同时在这些图上运行避免只针对单张图调出过拟合结果。3. 车牌区域定位从灰度化、边缘检测到连通域筛选的完整流程3.1 预处理灰度化、直方图均衡化与滤波的选择形态学处理作用于二值图但二值图质量取决于灰度预处理。如果直接对car.jpg做rgb2gray车牌区域对比度经常偏低尤其当蓝色车漆在灰度图中与路面灰度接近时。我一般先做直方图均衡化增强对比度再用中值滤波去噪最后才做边缘检测。% 直方图均衡化增强对比度 enhanced histeq(gray); % 3x3 中值滤波去除椒盐噪声 filtered medfilt2(enhanced, [3 3]); % 边缘检测Sobel 阈值 0.10 edge_map edge(filtered, sobel, 0.10);这里没有选择 Canny原因是 Canny 双阈值会生成大量细碎边缘后续形态学连接时容易把车头进气格栅的纹理一起并进来Sobel 对横向边缘响应更强车牌字符的横笔较多定位阶段用 Sobel 更干净。medfilt2的窗口大小很关键[3 3] 是保边去噪的临界点扩到 [5 5] 会把字符笔画内部的小拐角也磨平导致后续二值化时字符笔画变细。直方图均衡化会放大噪声尤其夜间图像的暗部噪声会变得非常明显因此medfilt2必须在histeq之后立即执行。如果处理后的灰度图上出现了明显条纹可以改为adapthisteq限制对比度增强率但该函数运行较慢在课程设计演示中不一定需要。3.2 边缘检测 形态学膨胀/闭运算的两次关键变换3.2.1 为什么先用 sobel 再用 imdilate 而不是直接找轮廓直接对灰度图找轮廓无法处理光照不均因为车牌蓝底的边缘梯度会被整体光照削弱。Sobel 得到的边缘图是离散的字符轮廓和车牌边框必须通过膨胀把它们连成面才能交给连通域分析。膨胀次数和结构元素尺寸共同决定连接程度膨胀越强漏检越少但误检越多。下面这段代码完成了从边缘图到候选区域的完整粗定位。% 第一次膨胀连接字符边缘 se_conn strel(rectangle, [3 20]); conn_map imdilate(edge_map, se_conn); % 闭运算填充字符内部空洞 se_fill strel(rectangle, [9 25]); filled_map imclose(conn_map, se_fill); % 清除小面积连通域 filled_map bwareaopen(filled_map, 500);bwareaopen不是形态学函数但它在形态学流程之后扮演面积滤波器可以把路面石子、树叶边缘产生的小连通域直接删掉。这里的 500 像素是相对 1600x1200 图像的经验值如果测试图中车牌占比很小需要降到 100 甚至更低。闭运算的[9 25]明显大于字符间距目的是把整个车牌区域打成实心白色块避免字符之间的间隔保留成空洞。3.3 连通域标记与车牌几何特性筛选实战形态学处理完成后filled_map中通常还有非车牌区域。接下来用bwlabel标记连通域再通过regionprops提取几何属性实现候选区域筛选。这是整个Runme.m中最核心的判断逻辑。% 标记连通域 [label_map, num_regions] bwlabel(filled_map); % 提取面积、外接框、矩形度相关属性 stats regionprops(label_map, {Area, BoundingBox, MajorAxisLength, MinorAxisLength}); plate_candidates []; for k 1:num_regions area stats(k).Area; bbox stats(k).BoundingBox; % [x, y, width, height] aspect_ratio bbox(3) / bbox(4); fill_ratio area / (bbox(3) * bbox(4)); % 车牌长宽比通常在 2.5~5.5 之间 if area 1000 area 100000 aspect_ratio 2.5 aspect_ratio 5.5 fill_ratio 0.4 plate_candidates(end1) k; end endregionprops返回的BoundingBox是最小外接矩形参数。标准车牌外形宽高比约 3.14实际照片因为拍摄角度比值在 2.5 到 5.5 之间浮动。矩形度fill_ratio用于排除 L 形或 Z 形连通域这类形状通常来自路肩线条或车灯反光。如果筛选结果为空优先检查aspect_ratio是否被透视变形压缩将下限放宽到 2.0 再试。注意如果edge阈值调低后出现大量横向纹理先不要急着增大结构元素优先检查中值滤波是否覆盖了噪声源。否则膨胀会把路面颗粒全部纳入候选区长宽比和矩形度都失真。3.4 候选区域验证长宽比与矩形度阈值设置场景Area 下限Aspect RatioFill Ratio建议结构元素近距离正面照30002.8 ~ 4.2 0.5[3 20]车头斜侧面照8002.0 ~ 5.0 0.35[5 25]夜间弱光5002.5 ~ 6.0 0.30[3 15]斜侧面拍照时车牌在图像中形成透视变形水平方向被压缩宽高比明显下降所以长宽比下限要放宽到 2.0矩形度也要降低因为外接框里会混入车牌侧面的阴影区域。夜间弱光条件下的边缘图稀疏膨胀结构元素加大后非车牌区域也容易满足长宽比条件此时用fill_ratio 0.3能有效拦截大部分误检。如果多个候选区重叠保留面积最大的一个作为最终车牌框这是最稳妥的规则。4. 车牌字符提取裁剪、二值化与字符切分的仿真细节4.1 从定位框到车牌子图imcrop 的边界处理定位得到的BoundingBox是浮点数imcrop可以直接接受但工程上我习惯先round再向外扩 3~5 像素避免边缘字符被截断。下面的代码展示了从候选区域索引中裁剪车牌子图的过程。for k plate_candidates bbox round(stats(k).BoundingBox); % 防止越界并外扩3像素 x max(1, bbox(1) - 3); y max(1, bbox(2) - 3); w min(size(gray, 2) - x, bbox(3) 6); h min(size(gray, 1) - y, bbox(4) 6); plate_img imcrop(gray, [x y w h]); imwrite(plate_img, sprintf(plate_%d.png, k)); end外扩 3 像素的原因是车牌白色边框在形态学定位过程中可能被膨胀操作压缩直接按原 bbox 裁剪会让字符贴边导致后续二值化时字符与边框粘连。如果裁剪后子图的长宽比仍然大于 5说明定位框包含了车灯或保险杠这个候选区应在这一步直接丢弃。裁剪时使用gray而不是原始 RGB 图可以减少后续二值化时颜色通道带来的干扰。4.2 车牌子图的二值化与去噪车牌子图相对干净但蓝底、铆钉和反光会造成灰度不均。此时使用全局二值化容易出错我一般用adaptthresh做自适应阈值并显式指定前景极性为深色。% 计算自适应阈值灵敏度0.4 T adaptthresh(plate_img, 0.4, ForegroundPolarity, dark); bw_plate imbinarize(plate_img, T); % 清除小面积噪声 bw_plate bwareaopen(bw_plate, 30); % 闭运算连接字符笔画断裂 bw_plate imclose(bw_plate, strel(rectangle, [2 3]));ForegroundPolarity设为dark是因为普通蓝底白字车牌中字符是深色背景是浅色。如果是新能源绿牌绿色在灰度图中的亮度通常仍高于黑色字符这个设置依然有效。但如果换成白底黑字的临时牌照就必须改成bright。二值化后检查一下bw_plate中字符是 1 还是 0如果反了用~bw_plate取反。不同二值化场景下的参数调整可以参考下表。二值化场景灵敏度参数前景极性后续闭运算结构元素蓝底白字0.4dark[2 3]白底黑字0.5bright[3 3]强反光/夜间0.3dark[2 5]adaptthresh的灵敏度参数越大阈值越高前景像素越少在反光严重的图片上把参数降到 0.3能减少背景白斑被误判为字符的情况。闭运算结构元素从[2 3]加大到[2 5]后可以同时连接笔画中的横向断裂但如果字符串字之间间隙不足 5 像素会把相邻字符粘在一起。这个尺度需要根据切分结果反复确认。4.3 字符投影切分与归一化尺寸字符切分最稳定的是垂直投影法对二值图按列求和字符列出现波峰字符间隙波谷接近零通过波谷切出字符左右边界。下面的代码是投影切分的原型实现。vertical_profile sum(bw_plate, 1); threshold max(vertical_profile) * 0.1; is_gap vertical_profile threshold; % 从非间隙到间隙再回到非间隙得到字符区域索引 char_start find(diff([0, is_gap]) -1); char_end find(diff([is_gap, 0]) 1); for i 1:min(length(char_start), length(char_end)) one_char bw_plate(:, char_start(i):char_end(i)); % 归一化到 40x20 norm_char imresize(one_char, [40 20], nearest); % 后续可以保存或送入分类器 enddiff([0, is_gap]) -1表示从间隙状态变为非间隙状态这正是字符左边界diff([is_gap, 0]) 1对应字符右边界。当铆钉遮挡字符时投影会出现伪波谷导致同一个字符被切成两段因此切分后需要检查每段宽度小于 15 像素的片段合并到相邻片段。imresize使用nearest是为了避免双线性插值给二值字符引入灰色过渡影响后续模板匹配或特征提取。4.4 输出接口为后续 OCR 识别做准备切分出来的字符既可以直接保存为图片也可以展开成特征向量。常见做法是把 40x20 的二值字符拉成 800 维向量送入 SVM 分类器。资源里的B.bmp和M.bmp是标准字符模板可以用它们对切分结果做逐像素比对统计匹配率。若匹配率较低优先检查二值化方向是否反了其次检查adaptthresh的灵敏度是否过高导致字符内部出现空洞。如果后续要接入 MATLAB 深度学习工具箱做字符识别可以把形态学定位结果当作训练数据的自动标注来源定位框裁剪出的车牌子图经过字符切分后按顺序写入文件夹再配合人工校验即可生成训练集。这样传统形态学方法和深度模型并不是二选一的关系前者可以为后者省下大量数据标注时间。5. 形态学参数调优的三种验证手段5.1 现场照片与标准图库的效果差异用car.jpg调通算法后不要只盯着这一张图。建议修改Runme.m增加多个测试图入口让2.bmp、4.bmp、B.bmp都跑一遍并输出每次imclose和imdilate的中间结果。对比中间图能快速定位是哪一步丢失了车牌边缘。如果定位框总偏向一侧检查长宽比筛选区间和矩形度阈值是否把车牌的一半切掉了。5.2 用形态学试剂定位失败原因实际仿真中两类失败最典型。第一类是白车白牌场景边缘图过稀闭运算无法形成实心候选区对策是把edge阈值从 0.10 降到 0.04并将膨胀结构元素从[3 20]改为[5 20]。第二类是膨胀过度导致车牌与进气格栅粘连此时即使长宽比正常矩形度也会明显下降。用fill_ratio 0.3作为弃用条件比反复调膨胀参数更有效因为连通域已经覆盖了非车牌区域继续收缩结构元素反而会漏掉真实车牌。5.3 运行效率与精度权衡imresize 加速验证形态学算子的耗时随结构元素尺寸增长大尺寸imclose在 1600x1200 图上可能单次需要数百毫秒。一个工程化技巧是先把原始图像缩到 640 宽度完成粗定位再将定位坐标映射回原图裁剪。缩放比scale 640 / size(img, 2)结构元素也要同步缩放原图用[3 20]时缩略图用[2 9]。映射回原图后再次进行细定位通常只需要一次imclose。资源中的多张测试图宽度不一在代码开头统一做缩放可以省下大量重复调参的时间。最后保留一组调好的params.mat在每次批量测试后自动生成候选框叠加图横向对比不同参数组合下的定位误差这种方式比反复人工观察单张图要快得多。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价