尧图精选

MATLAB车牌识别实战:从图像定位到字符分割的完整实现

🕒 发布时间:2026/9/14 3:00:30 📁 来源:尧图网络
简介这份MATLAB车牌识别资源包含完整的GUI界面程序适合高校学生用于课程设计、大作业和毕业设计也适合MATLAB初学者进阶学习。包内共96个文件包括4个m源码文件、1个fig界面文件、1个doc导入说明、1个data配置文件及大量bmp/jpg测试车牌图像整体仅1.28MB轻量易运行目前已有52人学习下载。该资源覆盖车牌识别中的图像预处理、车牌定位、字符分割、字符识别等关键环节GUI界面可交互操作便于逐步理解算法流程同时附带多张真实车牌图片和多类分割样本方便对照调试。程序结构清晰可在现有基础上扩展识别算法也提供答疑支持对于需快速搭建车牌识别系统原型或完成课程项目的学习者是一份实用且可二次开发的参考实例。1. 车牌识别为什么说它是 MATLAB 课程设计里性价比最高的项目停车场闸机从抬杆到放行通常只要两秒这两秒里发生的图像抓拍、车牌定位、字符切分和号码输出本质上就是一套完整的 MATLAB 图像处理流程。我给不少本科课设和毕业设计做过代码复查结论很一致车牌识别这个方向比单纯做边缘检测、滤波器设计更能体现图像处理全链路能力又不像人脸识别那样需要大量标注数据做训练。而衡量这类项目是否合格光跑通一张图远远不够得看定位是否稳、分割是否准、GUI 是否把中间过程暴露出来让使用者能定位问题。拿到带 GUI 界面的 MATLAB 车牌识别程序时我的习惯是先不看识别结果直接打开主程序跟踪一幅图像从按下按钮到输出车牌号码之间经过了哪几个函数。整个工程里 qiege.m、getword.m、GUI.m 三份核心文件加上 datas 模板库和一批实拍样例图已经把这条链路完整铺开了。接下来几章就按定位、分割、识别、调优四个环节逐层拆。2. 车牌定位灰度化、边缘检测与形态学筛选2.1 图像读取与灰度化先解决格式陷阱车牌定位的第一步不是检测而是让后续算法拿到一张干净的灰度图。程序里读入“车牌图片/鲁AHC250.jpg”这类 JPG 文件后第一件事通常是灰度化。这里有个新手常踩的坑imread 对 JPG 返回的是 m×n×3 的 RGB 数组对某些 BMP 格式可能返回索引图像而 PNG 还有可能是带透明通道的 m×n×4 数据。如果读进来直接交给边缘检测edge 函数对多维输入大概率会报“索引超出数组维度”这是因为 edge 只认二维灰度矩阵。常见做法是做一个格式兜底先判断维度再决定要不要转灰度[filename, pathname] uigetfile({*.jpg;*.png;*.bmp, 图像文件}, 选择车牌图片); if isequal(filename, 0) return; end raw imread(fullfile(pathname, filename)); if ndims(raw) 3 gray rgb2gray(raw); else gray raw; end axes(handles.axes_img); imshow(gray);这段代码解决两个问题一是用 uigetfile 把文件选择和后续处理解耦二是用 ndims 判断绕开 RGB 图直接进边缘检测的坑。int8 也好逻辑矩阵也好只要保证进入处理链的是二维矩阵后续操作就稳定。实际运行时你会发现uigetfile 返回的 filename 只是文件名路径必须靠 fullfile(pathname, filename) 拼完整这在 Windows 和 Linux 的 MATLAB 环境下都能正确解析。2.2 边缘检测和二值化阈值怎么选车牌在画面里属于高梯度区域字符与底色交界处灰度变化剧烈。定位的第一步是先把强边缘点全找出来缩小搜索范围。MATLAB 的 edge 函数支持 sobel、prewitt、roberts、canny、log 等算子。这个项目用 sobel 是合理的sobel 对水平和垂直边缘响应均衡计算量小实时交互时不会卡顿canny 虽然边缘更细更连续但双阈值和滞后连接会产生大量细节边缘后续形态学处理时需要反复调整膨胀结构元素才能把边缘连成完整色块增加调试成本。边缘检测后马上做二值化目的是把连续灰度变成明确的 0/1 信号edges edge(gray, sobel, 0.12); bw imbinarize(edges);这里 0.12 是 sobel 算子的梯度阈值含义是梯度幅值超过全局最大梯度 12% 的点才保留为边缘。室内停车场 0.12 到 0.2 都可用强逆光场景要下调到 0.05 左右否则车牌边缘会被光照吞掉。imbinarize 不做阈值参数时使用全局 Otsu 算法对 edges 这种已经二值化的逻辑矩阵是安全的。2.3 形态学闭开运算锁定车牌区域二值化之后字符边缘是断开的需要用形态学操作把字符带连成一个完整块。核心是先闭运算再开运算这个顺序不能反。闭运算先填补字符间的缝隙把“辽A09030”连成一条高对比度的横向色带开运算再用更大的窗口把道路标线、绿化带这些面积更大但形状不规则的区域排除掉。程序处理到这一步输出来的就是工程里那张“车牌区域定位图.jpg”。操作结构元素作用常见参数范围imclosestrel(rectangle, [17, 65])填平字符间空隙形成整体块高度 15~25宽度 55~90imopenstrel(rectangle, [30, 80])滤除比车牌更大的背景连通块尺寸约为待检车牌 1.5 倍bwareafilt面积区间筛选按像素数保留候选车牌区域面积下限 500上限 30000se1 strel(rectangle, [17, 65]); after_close imclose(bw, se1); se2 strel(rectangle, [30, 80]); after_open imopen(after_close, se2); stats regionprops(after_open, BoundingBox, Area); [~, idx] max([stats.Area]); box stats(idx).BoundingBox; plate imcrop(after_close, box);参数说明结构元素尺寸按 960×540 测试图调过如果 GUI 里读入的是 1920×1080 大图这些数值必须按比例放大否则结构元素过小会把整个车牌边缘腐蚀掉。regionprops 返回的 BoundingBox 是 [x, y, w, h] 四元组Area 是连通域像素总数。单候选时直接 max 取最大面积多候选时必须加长宽比判断车牌长宽比通常在 2.5 到 5 之间路牌和车身上的印刷字往往不满足这个条件。3. 字符分割getword、qiege 与垂直投影的配合3.1 分割是把识别准确率拉低的最大变量到了字符层面影响最终识别率的反而不是识别算法而是分割。七个字符里切错一个后面再强的模板匹配也会整体错位。这套工程里 getword.m 和 qiege.m 一个负责按列投影切出候选区一个负责把候选区裁剪到最小包围矩形。两者配合才能从“车牌区域二值化图.bmp”里稳定取出单个字符图。为什么不用纯连通域因为车牌汉字笔画复杂“京”字可能被拆成三四个碎片而数字和字母的封闭空间又可能在二值化后粘连。垂直投影法按列统计白色像素利用字符间的空白列先粗切再在粗切结果里做连通域修正对汉字和大写字母的适应性更好。3.2 垂直投影粗切的实现细节function [word, result] getword(d_img) [h, w] size(d_img); col_proj sum(d_img, 1); col_proj(col_proj 0) 1; se strel(ones, 1, 3); col_proj imclose(col_proj, se); [L, num] bwlabel(col_proj, 8); % 按连通区段裁剪原图得到候选字符条 if num 5 result 0; word []; return; end ... end逻辑说明sum(d_img, 1) 计算每一列的白色像素总数得到一个 1×w 向量。col_proj 0 把投影向量变成 0/1 标记1 表示该列存在字符笔画。imclose 用 1×3 水平结构元素把因轻微倾斜而断裂的字符笔画重新在投影上连成连续区段。bwlabel 以 8 连通方式给这些区段编号num 就是候选字符带的个数后续按编号把原图切出来。参数说明8 连通比 4 连通对倾斜和笔画断裂更宽容代价是有可能把相邻字符粘连成一个区段。如果 num 小于 5说明这一块区域大概率不是车牌直接返回失败标记让上一层重新选择候选框。3.3 qiege 裁剪空白边界的循环逻辑getword 切出来的字符带还带着上下左右的白边直接送进模板匹配会严重干扰相关系数计算。qiege.m 的思路很直接循环删除全为 0 的行和列直到四边都碰到白色像素。function w qiege(d) [Sy, Sx] size(d); w double(d); while sum(w(1, :)) 0 Sy 1 w(1, :) []; Sy Sy - 1; end while sum(w(Sy, :)) 0 Sy 1 w(Sy, :) []; Sy Sy - 1; end while sum(w(:, 1)) 0 Sx 1 w(:, 1) []; Sx Sx - 1; end while sum(w(:, Sx)) 0 Sx 1 w(:, Sx) []; Sx Sx - 1; end w uint8(w); end逻辑说明四个 while 循环分别处理上、下、左、右四条边。sum(w(1, :)) 0 表示第一行全部为黑色这时字符还没出现删除这一行同时 Sy 减一防止越界。之所以用 while 而不用 if是因为空白行可能连续出现多行需要循环删到第一个非空行为止。double 转换是为了避免 uint8 在求和计算时溢出最后转回 uint8 是为和模板存储格式对齐。参数说明这个函数的输入必须是二值化后的车牌字符带灰度图直接送进来会因为在边缘处存在大量接近 0 的暗像素导致循环无法终止或裁剪过度。3.4 分隔符圆点的过滤策略车牌第二位和第三位之间有个圆点分隔符二值化后通常是一个独立的小连通域。不过滤它分割结果会多出一个字符模板匹配时整体右移一位识别结果直接错三位。常见做法是加面积阈值字符连通域面积通常在 1000 到 4000 像素圆点只有 300 左右bw_filtered bwareafilt(bw_plate, [500, 8000]);这条命令把小于 500 像素的连通域直接丢弃顺带清掉摄像头灰尘造成的孤立白点。需要注意500 这个下限和分辨率绑定距离更远的抓拍图、更低分辨率的摄像头这个下限要往下调否则会把真正的字符小碎片也过滤掉。4. 字符识别与 GUI 集成模板匹配、回调函数与界面联动4.1 为什么选模板匹配而不是深度学习字符区域分割完成后识别阶段有两条路线模板匹配和训练分类器。这个工程选模板匹配原因是车牌字符集规模小。省份汉字约 31 个字母 24 个不含 I 和 O数字 10 个加上新能源车牌 8 位组合模板总量也就一百多张。用 corr2 做相关系数匹配字符分辨率 40×20 时单字符识别耗时在毫秒级人机交互不卡顿课设答辩演示完全够用。SVM 和深度学习路线的抗噪声能力确实更强但需要几百张标注字符图做训练还要额外装 Deep Learning Toolbox。对主要目的是跑通流程、理解图像处理思路的场景模板匹配是最低门槛又足够完整的选择。4.2 GUI 界面控件与回调映射GUI.fig 搭出的界面通常包含原图显示区、中间结果预览区、识别结果文本区以及“打开图片”“车牌定位”“字符分割”“开始识别”“退出系统”五个按钮。GUIDE 生成的 GUI.m 中每个按钮都有独立的回调函数控件Tag 属性回调函数数据流打开图片按钮btn_openbtn_open_Callback图像文件 → axes_img车牌定位按钮btn_locatebtn_locate_Callback灰度图 → 边缘图 → 定位框字符分割按钮btn_segmentbtn_segment_Callback二值车牌 → qiege → 字符图组开始识别按钮btn_recognizebtn_recognize_Callback字符图 → 模板匹配 → 文本结果退出按钮btn_exitbtn_exit_Callback清理句柄并关闭回调函数里最容易被忽略的是句柄传递。GUIDE 会把 handles 结构体自动传给每个回调你在 btn_locate_Callback 里使用 axes(handles.axes_img) 时必须保证 axes_img 的 Tag 在 FIG 文件中确实存在否则回调直接报错找不到字段。工程里“如何导入程序.doc”提到的导入步骤实质就是把整个工程文件夹加入 MATLAB 路径然后运行“run GUI.m”这一步的底层原理就是让你的函数能找到 GUI.fig 和 datas 模板库。4.3 识别回调函数的数据流转识别回调要做的事是把分割出的字符批量和 datas 模板库比对。模板库的命名规则是汉字用汉字名字母数字用 ASCII 名遍历后取相关系数最大的文件名作为识别结果function result match_char(char_img, template_dir) char_img imresize(char_img, [40, 20]); char_img imbinarize(char_img); files dir(fullfile(template_dir, *.bmp)); max_corr -1; result ; for i 1:length(files) tpl imread(fullfile(template_dir, files(i).name)); tpl imresize(tpl, [40, 20]); tpl imbinarize(tpl); r corr2(double(char_img), double(tpl)); if r max_corr max_corr r; [~, name] fileparts(files(i).name); result name; end end end参数说明imresize 到 [40, 20] 是为了统一字符宽高比为 1:2模板库里的模板也按这个尺寸制作两边尺寸不一致时 corr2 会报错。corr2 返回皮尔逊相关系数范围 0 到 1越接近 1 表示越相似。max_corr 初始值设为 -1保证第一轮对比必能替换fileparts 提取文件名去扩展名后直接作为识别输出。4.4 模板匹配的精度边界与倾斜矫正模板匹配对形变非常敏感。原图倾斜超过 5 度、字符笔画粘连、二值化后出现毛刺相关系数会从 0.9 掉到 0.7 以下。样例图里辽B5705D.jpg、京QYJ357.jpg 都是近水平抓拍直接识别没问题但如果测试集里有手机随手拍的角度要先做水平矫正angle find_skew_angle(plate_bw); plate_bw imrotate(plate_bw, -angle, bilinear, crop);find_skew_angle 可以基于最小外接矩形与水平线的夹角计算也可以用 Radon 变换找投影最长的角度。无论哪种imrotate 的 crop 参数都会保证输出图像和输入尺寸一致但边缘会被截掉一圈所以旋转后最好再调用一次 qiege 重新裁剪避免字符靠近图像边界影响识别。5. 识别不出来时怎么查样例图排查顺序与三种扩展方向拿到工程先别急着改算法。用自带的“车牌图片/京QM2Z68.jpg”跑一遍完整链路确认 GUI 能把七个字符逐个显示。再换“鲁AHC250.jpg”“辽AJW767.jpg”这些不同省份的图重点看汉字部分是否被误识别。如果字符从第三个开始连续错位问题一定出在字符分割把分割中间结果显示在预览区对比 qiege 的输出是不是多选了连通域优先检查分隔符圆点和小噪声点的面积阈值是否匹配当前图像分辨率。如果只有最后一位偶尔出错那才是模板匹配层面的问题看一眼二值化后的字符图是否有毛刺或断笔。调优方向按优先级排三条。第一把全局 Otsu 二值化换成自适应局部阈值MATLAB 里对应 imbinarize(I, adaptive)对夜间车灯照射、树影遮挡造成的灰度不均匀有明显改善。第二在车牌定位结果上加最小外接矩形长宽比约束把路牌和公交车身上的印刷大字误识别为车牌的候选框直接丢弃。第三在模板匹配输出后加结果校验车牌第一位必须是省份汉字第二位必须是字母第三位必须是字母或数字不满足就取次优匹配结果再验一次。这三条都只需要在原有 GUI 函数里加分支不动整体架构。自制模板时尤其注意一点datas 目录里的模板来自样例图的归一化结果。换了高分辨率相机或用不同角度抓拍后旧模板和新的实际字符图在笔画粗细、边缘锐度上都会不一致相关系数整体下降。重新采集模板时字符图要统一经过 imresize 到 [40, 20] 再二值化和识别端保持完全一致的处理链路。最后验证识别率用同一光照条件下的 20 张以上车牌图批量跑统计整串号码完全正确的比例单张图能跑通不代表系统能交付。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →