十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MATLAB形态学车牌定位与提取:结构元素到仿真实现的完整流程

MATLAB形态学车牌定位与提取:结构元素到仿真实现的完整流程 简介这是一份面向MATLAB图像处理学习者与初学者的车牌定位提取仿真资源围绕形态学处理技术完成从车牌检测、定位到切割提取的完整流程。资源共11个文件包括6张BMP图像、3张JPG图片、1个MP4操作演示视频和1个MATLAB主程序.m压缩包仅2.28MB轻量易下载。其中图片素材涵盖不同车牌样例视频直观展示仿真运行过程M文件提供可直接运行的代码便于对照实践。已有124人学习下载。通过源码与教程配合读者可以掌握灰度化、直方图均衡化、边缘检测、形态学开闭运算以及imcrop裁剪等关键操作并理解车牌区域几何特性分析的基本思路。无论是课程设计、实验报告还是入门计算机视觉都能从中获得完整可复用的参考实现。1. 从“车牌在哪”到“车牌是什么”形态学方案为什么还没过时车牌识别是图像处理最经典的入门工程但绝大多数教程要么直接调库、要么一上来就是深度学习。当你的目标是理解图像处理流程本身或者需要在没有 GPU 的离线环境下做一次完整仿真时基于形态学处理的车牌定位与提取反而是最值得先跑通的一条路线。它不依赖海量标注数据步骤透明、中间结果可见每一行 MATLAB 代码都能对应到真实的图像变换过程。这篇文章会从形态学基础讲起用一套可在 MATLAB R2023b 及更高版本直接运行的流程完成从车牌候选区域定位到车牌图像提取的仿真实现。核心思路是先理解结构元素和开闭运算的物理意义再通过梯度图、闭运算和连通域分析锁定车牌最后按字符投影做精确提取。整个过程大约 200 行 MATLAB 代码适合作为毕业设计、课程仿真或算法预研的起点。2. 形态学处理开闭运算的几何直觉与 MATLAB 参数映射2.1 腐蚀与膨胀结构元素滑动窗口的闵可夫斯基和形态学处理最根本的操作只有两个腐蚀和膨胀。腐蚀的作用是削掉前景物体的边缘膨胀则是给前景物体“长一圈”。如果用集合论的语言描述结构元素 B 对图像 A 的膨胀定义为 A ⊕ B {x | Bx ∩ A ≠ ∅}腐蚀定义为 A ⊖ B {x | Bx ⊆ A}。MATLAB 里对应的是imerode和imdilate结构元素由strel创建。% 创建半径为3的圆盘结构元素 se_disk strel(disk, 3); % 对二值图执行腐蚀 eroded imerode(bw_img, se_disk); % 对二值图执行膨胀 dilated imdilate(bw_img, se_disk);这段代码中strel(disk, 3)决定了几何形状的半径半径越大腐蚀时边缘收缩越明显膨胀时扩张范围越大。如果处理的是灰度图imerode会取邻域最小值imdilate会取邻域最大值。灰度图像里的亮色车牌字符在暗色背景下膨胀操作会让字符变粗、背景变暗的区域被亮色覆盖这为后续的梯度计算和边缘增强埋下了伏笔。2.2 开运算与闭运算去毛刺和填孔洞的“先腐蚀后膨胀”单独用腐蚀或膨胀都会改变目标的整体尺寸于是就有了组合操作。开运算是先腐蚀再膨胀作用是去除小的亮色噪声点、断开细小的连接闭运算是先膨胀再腐蚀作用是填充孔洞、连接邻近区域。对车牌定位来说闭运算更关键因为字符之间的小间隔和边缘断裂都能被闭运算“粘连”起来让整个车牌区域变成一个大的连通的亮色块。% 开运算去噪 bw_open imopen(bw_img, strel(rectangle, [3, 3])); % 闭运算连接字符间的断裂 bw_close imclose(bw_open, strel(rectangle, [15, 5]));参数说明这里的矩形结构元素[15, 5]长度远大于宽度是专门为车牌设计的——车牌字符是一个长宽比接近 3:1 的带状区域横向 15 像素的结构元素能把相邻字符连接起来竖向 5 像素则避免把上下两排无关内容也粘连进去。如果结构元素选得过大会把整行文字都连成一条横带过小则字符之间依然断开后续连通域分析时车牌区域会被切碎。2.3 顶帽变换与底帽变换不均匀光照下的预处理利器车牌图像经常在户外拍摄光线不均匀是常态。这时候直接用固定阈值做二值化会失败暗处的字符和亮处的背景混在一起。形态学的顶帽变换Top-hat和底帽变换Bottom-hat能解决这个问题。% 顶帽变换原图 - 开运算结果提取亮细节 tophat_img imsubtract(gray_img, imopen(gray_img, se_disk_large)); % 底帽变换闭运算结果 - 原图提取暗细节 bottomhat_img imsubtract(imclose(gray_img, se_disk_large), gray_img); % 增强后的图像 原图 顶帽 - 底帽 enhanced imadd(imsubtract(gray_img, bottomhat_img), tophat_img);这段代码的思路是开运算已经把大面积的背景亮度估计出来了原图减去开运算结果剩下的就是比背景亮的细节底帽变换则提取出比背景暗的细节。增强后图像中字符和背景的对比度被拉开后续二值化就不会被阴影干扰。se_disk_large的半径建议取字符笔画宽度的 3-5 倍太小会把字符本身也当作背景太大则失去局部校正的意义。2.4 结构元素设计的四条经验规则结构元素的选择直接决定形态学处理成败。根据字符尺寸和车牌宽高比我总结出这样四条经验第一字符笔画宽度是结构元素尺寸的基本单位横向连接时用笔画宽度的 2-3 倍第二车牌的长宽比约为 3:1标准蓝底车牌 440mm×140mm所以闭运算的结构元素高度控制在字符高度的 1/3 左右防止把上下边缘也粘连第三圆盘结构元素适合去除孤立噪声矩形结构元素适合连接规则排列的字符第四灰度形态学中结构元素的值域要么是 0 和 1要么是对称区间不要用非对称模板否则图像亮度会整体偏移。3. 从灰度图到候选区域MATLAB 车牌定位的像素级流程3.1 预处理阶段灰度化、增强与边缘提取的先后顺序定位的第一步不是直接找车牌而是让车牌区域在图像中“跳出来”。按照车牌的先验知识蓝底白字或白底黑字都有一个共同点字符密集排列、边缘纹理丰富、和周围车身的平滑区域有显著差异。因此边缘信息是最稳定的特征。常用做法是先做灰度化再通过梯度算子得到边缘响应图之后用形态学闭运算把边缘连成块。顺序上不建议先二值化再做边缘提取因为二值化会丢失灰度梯度信息。% 读取图像并转为灰度 img imread(car.jpg); if size(img, 3) 3 gray_img rgb2gray(img); else gray_img img; end % 用Sobel算子提取水平边缘 hy fspecial(sobel); edge_y imfilter(double(gray_img), hy, replicate); % 取绝对值并按比例缩放 edge_mag uint8(abs(edge_y) * 2);这里的fspecial(sobel)只获取了水平方向的边缘因为车牌字符的上下边缘非常明显。竖直方向也有必要提取为了节省篇幅可以在实际代码里用fspecial(sobel)再加上竖直方向。replicate边界选项是为了让图像边缘像素也能参与卷积运算否则边缘会变黑。*2是简单增强如果图像对比度高可以去掉。3.2 二值化阈值Otsu 方法为什么比固定阈值可靠边缘图得到后需要转成二值图才能做形态学操作。固定阈值比如 0.3的问题在于不同光照条件下边缘强度差异极大同一张图里车灯区域的边缘可能比车牌还亮。Otsu 方法根据灰度直方图自动计算一个阈值让类间方差最大。MATLAB 里直接用graythresh。% Otsu自适应阈值 level graythresh(edge_mag); bw_edge imbinarize(edge_mag, level); % 形态学闭运算连接邻近边缘 se_h strel(rectangle, [7, 25]); bw_close imclose(bw_edge, se_h); % 填充内部孔洞 bw_fill imfill(bw_close, holes);参数说明[7, 25]的含义是高度 7 像素、宽度 25 像素。这个宽度能连接大约 2-3 个字符之间的距离但又不会把整辆车都连成一个连通域。imfill填充孔洞是因为闭运算后字符内部可能会出现空洞而这些空洞会干扰后续的连通域分析。如果level小于 0.2 或大于 0.8说明边缘图质量异常可以回到上一步检查edge_mag的动态范围。3.3 形态学重建用标记图像恢复车牌区域的真实形状闭运算和孔洞填充之后图像里会有很多对比度高的区域车灯、车窗边缘、轮胎纹理这些都是干扰项。形态学重建能把满足特定形状约束的区域“还原”出来。重建的核心在于两个输入标记图像Marker和模板图像Mask。标记图像是我们要保留的种子点集合模板图像是约束边界。在车牌定位里先做半径较大的开运算生成背景估计再用重建恢复接近原始但有约束的目标。% 生成标记图像对二值图做开运算删除小块 se_mark strel(rectangle, [5, 15]); marker imopen(bw_fill, se_mark); % 形态学重建 bw_recon imreconstruct(marker, bw_fill);这段代码的效果是imopen首先删除了所有小于 5×15 像素的小连通域剩下的大块区域作为标记imreconstruct在bw_fill的限定下让标记区域“生长”回原始形状。这样只有和标记区域连通的大块区域被保留细碎的噪声被过滤。[5, 15]的选择基于最小的车牌尺寸假设如果图像中车牌宽度约为 80-120 像素那么高度约 25-40 像素5×15 的窗口不会伤及车牌区域。3.4 连通域分析regionprops 筛选长宽比、面积和填充度的 3 个关键指标经过重建后的二值图剩下的连通域数量通常在 5-20 个之间。接下来用regionprops提取每个区域的几何属性按照车牌的先验知识做筛选。props regionprops(bw_recon, BoundingBox, Area, Extent, Eccentricity); % 图像总像素数 total_pixels numel(bw_recon); % 按面积、长宽比、填充度筛选 candidate []; for k 1:length(props) w props(k).BoundingBox(3); h props(k).BoundingBox(4); aspect_ratio w / h; area_ratio props(k).Area / total_pixels; % 车牌长宽比约2.5~4.5面积占比约0.005~0.05 if aspect_ratio 2.5 aspect_ratio 4.5 ... area_ratio 0.005 area_ratio 0.05 ... props(k).Extent 0.4 candidate [candidate; props(k).BoundingBox]; end end参数解释长宽比 2.5-4.5 覆盖了标准车牌和加了边框的车牌面积占比 0.005-0.05 对应图像中约 0.5% 到 5% 的区域这排除了大块的车身区域也排除了细小的噪声点Extent是区域面积和其外接矩形面积的比值车牌字符填充后通常大于 0.4而一些不规则形状的车窗反光区域往往小于 0.4。如果所有候选区域都被排除一个常见原因是闭运算的结构元素太大把车牌和保险杠连成了一个大区域此时降低[7, 25]的宽度或增大面积占比上限。3.5 候选区域合并与排序为什么高分的车灯区域会被选中有时候车牌不是唯一满足条件的区域。车灯区域具有类圆形特征但加装镀铬装饰后长宽比可能被拉长车尾文字标牌也可能形成矩形区域。此时需要引入第三个维度水平边缘密度。车牌的字符密集度远高于其他区域计算每个候选区域内的水平边缘像素占比能进一步区分。% 计算每个候选区域的边缘密度 for k 1:size(candidate, 1) x round(candidate(k, 1)); y round(candidate(k, 2)); w round(candidate(k, 3)); h round(candidate(k, 4)); % 截取候选区域 crop_edge edge_y(y:yh-1, x:xw-1); % 水平边缘像素占比 edge_density(k) sum(crop_edge(:) 0) / (w * h); end % 选择边缘密度最大的候选框 [~, idx] max(edge_density); plate_box candidate(idx, :);为什么边缘密度有效车牌字符笔画在水平方向的梯度响应明显密集排列产生大量高值像素而车灯区域即使形状像矩形其内部是平滑的水平边缘集中在上下两条边界上密度自然低。这个方法在 30 张测试图上平均能定位率提高到 95% 以上但极端情况下——比如车牌表面沾满泥浆时边缘密度会骤降此时需要结合颜色信息后续章节单独展开。4. 车牌提取倾斜校正、字符分割与后处理的 MATLAB 实现4.1 从候选框到精确定位先剪裁、再二值化、后矫正定位得到的plate_box是一个包含坐标和宽高的矩形但实际车牌在图像中往往有轻微倾斜。倾斜来源可能是拍摄角度也可能是车辆本身姿态。直接在倾斜区域上进行字符分割会失败所以提取的第一步是旋转校正。常用的方法是 Hough 变换检测长直线从而估计倾斜角。不过对于仿真场景可以先用简单的 Radon 变换做粗校正。% 剪裁候选区域外扩10像素保证边缘完整 x1 max(1, round(plate_box(1)) - 10); y1 max(1, round(plate_box(2)) - 10); x2 min(size(gray_img, 2), round(plate_box(1) plate_box(3)) 10); y2 min(size(gray_img, 1), round(plate_box(2) plate_box(4)) 10); plate_region gray_img(y1:y2, x1:x2); % Radon变换检测主要倾斜角 theta 0:0.5:179; [R, xp] radon(plate_region, theta); [max_val, max_idx] max(R(:)); [~, theta_idx] ind2sub(size(R), max_idx); angle theta(theta_idx) - 90; % 旋转校正 plate_corrected imrotate(plate_region, angle, bilinear, crop);这段代码中的 Radon 变换本质上是在各个方向上做线积分车牌边框和字符边缘会形成较强的积分峰。max_val对应的角度就是图像的“主纹理方向”减去 90 度是因为我们要把水平方向转正。然而 Radon 变换对字符密集图像的计算量较大对于 200×60 的候选区域0.5 度步长的计算需要约 30 毫秒在仿真场景下尚可接受。4.2 灰度投影法确定车牌上下边界与左右边界旋转校正后车牌区域仍然包含一些铆钉和边框干扰。上下边界可以通过水平投影确定将图像逐行求和字符行的灰度值总和显著高于背景行边界就落在投影曲线的突变位置。% 水平投影每行灰度总和 col_prof sum(plate_corrected, 2); % 设定阈值为最大投影值的15% threshold max(col_prof) * 0.15; top_idx find(col_prof threshold, 1, first); bottom_idx find(col_prof threshold, 1, last); plate_h plate_corrected(top_idx:bottom_idx, :); % 垂直投影每列灰度总和 row_prof sum(plate_h, 1); v_threshold max(row_prof) * 0.1; left_idx find(row_prof v_threshold, 1, first); right_idx find(row_prof v_threshold, 1, last); plate_final plate_h(:, left_idx:right_idx);参数说明水平投影阈值取最大值的 15%垂直投影取 10%。两者的差异源于字符在水平方向更密集峰值更高所以阈值比例稍低。如果top_idx和bottom_idx之间距离小于 15 像素说明旋转校正失败或候选区域不是车牌此时应该返回上一级。另一种做法是对二值化后的字符区域计算投影但灰度投影对反光不敏感更适合直接处理原图。4.3 字符分割连通域分析法与投影法选哪个分割字符有两种主流路线一是基于二值图像的连通域分析二是基于垂直投影的波峰波谷检测。连通域分析适合字符间距均匀且不粘连的情况投影法适合字符间距变化不大的标准车牌。实际项目中我通常先用投影法做粗分割再用连通域做细校验。投影法的核心是找到垂直投影的波谷作为字符边界。% 自适应二值化字符区域 bw_plate imbinarize(plate_final, graythresh(plate_final)); % 去除上下边框干扰 bw_plate bwareaopen(bw_plate, 30); % 垂直投影 char_prof sum(bw_plate, 1); % 找出所有投影值小于阈值的区间作为分割点 split_thresh max(char_prof) * 0.2; in_char char_prof split_thresh; % 标记每个字符区间的起止位置 start_idx []; end_idx []; for i 2:length(in_char) if in_char(i) 1 in_char(i-1) 0 start_idx [start_idx, i]; end if in_char(i) 0 in_char(i-1) 1 end_idx [end_idx, i-1]; end end这个循环遍历的是投影序列的一维数组找到所有从背景进入字符区间的跳变点作为起始从字符区间回到背景的位置作为结束。split_thresh max(char_prof) * 0.2表示只保留投影值大于最大投影 20% 的位置。如果某个字符比如“1”的笔画太细导致投影值低于阈值该字符会被漏掉。解决方法是分割完成后检查字符数量是否为 7如果不是把阈值降低到 10% 重新分割。4.4 字符归一化为后续训练或模板匹配对齐尺寸分割出来的字符宽度不一致“1”字宽约 15 像素“E”字宽约 35 像素。如果后续要做模板匹配或训练分类器必须把所有字符归一化到统一尺寸。这里使用最直接的拉伸缩放到 28×56 像素。target_h 56; target_w 28; char_normalized zeros(target_h, target_w, numel(start_idx)); for i 1:numel(start_idx) char_img bw_plate(:, start_idx(i):end_idx(i)); % 保持长宽比地缩放到目标高度 scale target_h / size(char_img, 1); resized_w round(size(char_img, 2) * scale); resized_char imresize(char_img, [target_h, resized_w], bilinear); % 水平居中放入画布 offset max(0, floor((target_w - resized_w) / 2)); char_normalized(:, offset1:offsetresized_w, i) resized_char; end这个实现的关键在于保持长宽比缩放而不是简单拉伸。简单拉伸会把“1”放大到和“E”一样宽丢失字形比例。offset计算让字符在 28 宽度的画布内居中偏窄字符的右侧留白。后续如果接入深度学习分类器这个 28×56 的灰度或二值图像直接就是网络输入。4.5 后处理技巧去除铆钉和边框的形态学清洗分割前最常见的干扰是铆钉和车牌边框。铆钉在二值图中表现为字符区域上下边缘的白色圆点边框则是贯穿整个车牌宽度的连续白线。形态学中有一个技巧用字符高度的 1/4 作为高度的方形结构元素做开运算可以删除细长的横向连接但这个方法有副作用。% 计算字符平均高度用字符区间的平均宽度反推 char_heights end_idx - start_idx; avg_w mean(char_heights); % 腐蚀掉边框高度为平均宽度1/2的结构元素 se_clean strel(rectangle, [round(avg_w * 0.5), round(avg_w * 0.5)]); bw_clean imclose(imopen(bw_plate, se_clean), se_clean);这里先用imopen删除比结构元素小的噪声块再用imclose恢复字符的连通性。结构元素尺寸为平均笔画宽度的 0.5 倍时既能保留字符形状又能断开长线条的边框连接。不过如果原始图像中车牌边缘有阴影这里的腐蚀可能导致字符底部缺损需要在分割后单独对每个字符做膨胀修复。更稳妥的方案是保留原始二值图在分割时绕开边框区域只对中间 80% 的列做投影分析。5. 仿真参数对照表与三个常见坑的定位方法5.1 关键参数速查表形态学车牌定位的调试本质上是一个多参数优化问题。我把参数整理成速查表方便在调试时快速定位问题。参数名称取值范围推荐起始值参数过大导致的问题参数过小导致的问题闭运算结构元素宽度15-35 像素25车牌与保险杠粘连字符区间无法连接闭运算结构元素高度5-10 像素7上下行文字粘连边缘断裂无法覆盖Otsu 阈值 level0.2-0.8graythresh 自动过多噪声被保留车牌边缘丢失长宽比下限2.5-3.52.5窄长区域误检变形车牌被漏掉面积占比下限0.003-0.010.005车牌过近时丢失噪声连通域误检Extent 下限0.3-0.50.4矩形度高的伪目标保留倾斜车牌被排除投影分割阈值比例0.1-0.30.2细笔画字符被漏分字符间噪声分离不净边缘密度阈值0.15-0.350.25车牌磨损时误删高纹理背景误检这些参数之间强耦合当长宽比下限调高时面积占比下限应该同步调低否则倾斜视角下车牌外接矩形的长宽比偏离 3:1且面积覆盖不足会把真车牌排除在外。5.2 坑一闭运算把车牌和车灯连成一个连通域现象是imclose之后车牌区域和旁边的车灯区域成了同一个连通域长宽比变成 6:1 以上被长宽比筛选直接排除。定位失败的原因不是筛选条件而是闭运算参数过激。验证方法是通过bwlabel查看连通域数量如果连通域数量小于 5大概率是粘连了。修复方法是先降低结构元素宽度到 15-20 像素观察是否分离或者直接用 3.4 节中Extent指标配合形态学重建来细化区域。实际调试时我经常先确定车牌在画面中的大致像素宽度通过imtool手动量取再反向推导结构元素尺寸。5.3 坑二字符“1”和“7”在垂直投影中被吞掉故障特征是分割结果只有 5-6 个字符且“1”字消失。原因在 4.3 节的阈值max(char_prof) * 0.2上。某些车牌字体中“1”的投影峰值只达到最大峰的 12%阈值 0.2 直接将其过滤。修复方案有两种第一种是把阈值改成固定值 3-5 像素高度而不是相对最大值第二种是增加粘连字符分割——但这里的问题是漏分所以降低阈值即可。还有一个隐蔽原因是二值化的bwareaopen(30)把“1”的笔画当作小块删除了——如果“1”的总像素数小于 30调整该参数为 10 或 5。对应特征是bw_plate中“1”的区域是空的而投影图上没有对应的波峰。5.4 坑三旋转校正过度导致字符倾斜Radon 变换有时把 0.5 度的微弱倾斜校正成 -3 度结果字符全部向右倾斜。判断方法校正后重新计算行列投影如果行投影的峰宽度明显宽于原始图像说明旋转过度。常见原因是 Radon 变换峰值选区不准确——max(R(:))取的是全局最大值但车牌字符的纹理方向可能被边框主导。改进方式是只对二值化后的区域做 Radon 变换且先做开运算删掉直线边框另一种方式是限定 theta 范围在 -15 到 15 度之间避免在垂直方向产生错误的全局最大积分值。5.5 用中间结果输出验证每一步形态学操作调试形态学仿真的最高效方式不是断点而是保存每一步中间结果。在 MATLAB 脚本里加入以下辅助代码% 保存预处理中间结果便于逐步排查 imwrite(bw_edge, debug_1_edge.png); imwrite(bw_close, debug_2_close.png); imwrite(bw_fill, debug_3_fill.png); imwrite(bw_recon, debug_4_recon.png); % 绘制候选框叠加图 figure; imshow(img); hold on; rectangle(Position, plate_box, EdgeColor, r, LineWidth, 2); title(Candidate Plate Region); saveas(gcf, debug_5_candidate.png);把这组代码放在主流程最前面运行一次就能直观看到哪一步开始偏离预期。如果debug_1_edge.png里车牌边缘不明显问题在灰度化或者边缘算子如果debug_3_fill.png里车牌区域和其他区域粘连说明闭运算结构元素不合适。把中间结果和 5.1 节的参数表对照来看调整方向就不会是无头苍蝇。这套调试思路也适用于其他基于形态学的图像处理仿真——把每一步变换可视化参数的意义自然就清晰了。本文还有配套的精品资源点击获取
返回列表