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

资讯详情

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

MATLAB车牌识别实战:HSV定位与形态学处理全流程

MATLAB车牌识别实战:HSV定位与形态学处理全流程 简介一份基于MATLAB实现车牌识别LPR系统的完整资源包面向计算机视觉初学者、MATLAB开发者及交通监控、停车场管理等场景的算法研究者。内容覆盖图像预处理、车牌定位、特征提取、字符分割与识别全流程重点演示了edge边缘检测、regionprops连通域分析、svmtrain与神经网络模型训练等关键方法并配有实际车牌图像、演示视频及可直接运行的.m脚本便于对照学习与二次开发。资源共22个文件以8个MATLAB脚本为核心辅以10张车牌样本图片、2个演示视频及1个png说明图整体压缩包约42.71MB结构清晰适合从零搭建车牌识别系统时参考。当前已有373人学习下载资源内还提供了一组car plate相关文件可帮助读者快速验证预处理、定位到字符识别的完整链路并针对不同光照、倾斜等场景进行参数调优。1. 车牌识别 matlab 方案从图像采集到字符输出的完整链路车牌识别在 MATLAB 里做难点从来不是调用哪个函数而是把「定位、分割、识别」这条链路按顺序打通每一步的参数都和实际图像强相关。同样是蓝底白字的民用车牌光照、倾斜、遮挡、分辨率一变固定阈值就会失效所以成熟的 MATLAB 车牌识别工程本质上是「图像预处理 形态学分析 字符切割 模板/分类器匹配」的组合而不是某个单一算法通吃。这套方案的好处是调试直观每一步都能用imshow实时看中间结果坏处是参数多、需要有一套自己的调参方法论。本文面向能用 MATLAB 读写图片、知道图像滤波基本概念的人目标是给你一套在小批量本地图片上能跑出可用准确率的流程。2. 车牌定位与预处理颜色特征与形态学操作的配合2.1 为什么不能只靠灰度图做定位车牌识别 matlab 入门教程里最常见的错误是把 RGB 图直接转灰度、二值化、找轮廓。这么做在单一背景下可能成立但换到实拍场景车身的白色、路面标线、路牌文字都会成为干扰。真实工程里优先用颜色特征圈定候选区域因为中国民用车牌有固定的蓝底白字HSV 空间中蓝色色调集中在 H 分量的 100° 到 124° 附近用这个特征做掩膜比灰度阈值稳定得多。另一个原因在于「车牌区域」的几何特征它宽高比大约为 3.14:1440mm×140mm面积在整张图中占比有限。颜色特征能快速把候选区缩小到几个矩形再用几何过滤剔除误检。这个思路对后续字符分割有直接帮助——定位不准后面所有步骤都是空中楼阁。2.2 RGB 到 HSV 的转换与蓝色掩膜提取% 读取图像 img imread(car_plate.jpg); % RGB 转 HSV hsvImg rgb2hsv(img); % 提取蓝色通道掩膜H 在 [0.55, 0.68]S 不低于 0.35 mask (hsvImg(:, :, 1) 0.55) (hsvImg(:, :, 1) 0.68) (hsvImg(:, :, 2) 0.35); % 形态学闭运算连接断裂的车牌区域 se strel(rectangle, [15, 15]); maskClosed imclose(mask, se); % 连通域分析获取候选区域属性 cc bwconncomp(maskClosed); stats regionprops(cc, BoundingBox, Area, FilledArea);这段代码里rgb2hsv把 RGB 图像转换到 HSV 色彩空间H 表示色相、S 表示饱和度、V 表示明度。hsvImg(:, :, 1)取出色相通道做区间判断设置 H 为 0.55 到 0.68 是因为在 MATLAB 中 HSV 各分量均归一化到 [0, 1]蓝色对应的实际角度 198° 除以 360° 约等于 0.55。饱和度下限 0.35 用来排除偏灰的蓝色区域。imclose做闭运算先用 15×15 的矩形结构元素膨胀再腐蚀目的是把车牌字符与蓝色底板之间的空隙填上——因为字符是白色的二值化后它会把蓝色底板断开。结构元素尺寸由车牌在图像中的像素宽度决定图片分辨率为 1920×1080 时15 到 20 的尺寸比较适合如果分辨率低需要相应缩小。bwconncomp找出所有连通区域regionprops返回每个区域的包围盒和面积信息。这里用FilledArea而不是Area是因为一个连通区域内可能有小空洞填充后的面积更真实反映物理尺寸。2.3 几何特征过滤与倾斜校正% 筛选候选区域宽高比约 2.5~4.0面积足够大 candidates []; for k 1:length(stats) bb stats(k).BoundingBox; ratio bb(3) / bb(4); % 宽 / 高 area stats(k).FilledArea; if ratio 2.2 ratio 4.2 area 500 candidates [candidates; bb]; end end宽高比范围设 2.2 到 4.2 是留出余量的做法标准车牌是 3.14但实拍中镜头畸变会让它偏移到 2.5 或 3.8。面积下限 500 像素是一个经验值前提是车牌在画面中不是远小点。如果实际图片中车牌很小这个下限需要降到 100 左右。倾斜校正常用radon变换检测车牌长轴的角度或者用最小外接矩形minAreaRect的思路——在 MATLAB 里对应的函数是regionprops的Orientation属性。得到角度后用imrotate反向旋转再裁剪出正的车牌区域。% 假设 idx 是选定的候选区域索引 angle stats(idx).Orientation; % 单位度 rotated imrotate(img, -angle, bilinear, crop); % 裁剪车牌区域 plateBB stats(idx).BoundingBox; plate imcrop(rotated, [plateBB(1), plateBB(2), plateBB(3)*1.1, plateBB(4)*1.6]);注意裁剪时宽度乘 1.1、高度乘 1.6这是把车牌上下边缘的铆钉、边框余量一起裁进来。后面字符分割时这些边框是要去掉的。提示imrotate的crop选项会让旋转后的图像尺寸与原图一致缺点是四个角会被裁剪掉一部分。如果车牌正好在图像边缘建议改用loose并手动去除多余黑边。3. 字符分割的确定性算法投影法与连通域分析组合3.1 车牌区域二值化的阈值选择% 转为灰度图 grayPlate rgb2gray(plate); % 中值滤波去噪3×3 窗口保留边缘的同时去除椒盐噪声 filtered medfilt2(grayPlate, [3, 3]); % 自适应阈值二值化局部邻域 31×31 bwPlate imbinarize(filtered, adaptive, Sensitivity, 0.45);固定阈值 Otsugraythresh在均匀光照下没问题但车牌表面经常有反光或阴影全局阈值会把半个字符吃掉。自适应阈值按局部邻域计算阈值对光照渐变有更好的容忍度。Sensitivity参数控制阈值偏置0.45 的含义是让更多像素被判为前景白色因为字符笔画本身较细提高敏感度能减少笔画断裂。如果二值化结果是「黑底白字」需要取反让字符为白色、背景为黑色因为后面连通域分析时习惯性认为面积小的连通区域是字符% 统计前后景占比若白色占大多数则取反 if mean(bwPlate(:)) 0.5 bwPlate ~bwPlate; end3.2 垂直投影分割法计算逐列白像素数% 垂直投影 proj sum(bwPlate, 1); % 每一列的白像素总数 % 找到投影值为 0 的列作为字符间隔 gapCols find(proj 0);垂直投影的基本原理是字符列的白像素数多字符间隙的列白像素数为 0。相邻的 0 值列之间构成一个字符区间。实际处理中会有噪声导致投影值在间隙处不为 0此时需要设一个阈值而不是直接用 0。% 低通滤波平滑投影曲线 projSmooth conv(double(proj), ones(1, 5)/5, same); % 阈值设为最大投影值的 2% thresh max(projSmooth) * 0.02; gapCols find(projSmooth thresh); % 将连续 gap 列合并为一个分割点conv做滑动平均窗口为 5 个像素把离散噪声抹平。阈值设 2% 是因为有些字符边缘会有毛刺导致间隙列仍有一两个白像素。3.3 连通域分析做二次分割解决汉字断裂问题垂直投影法最怕的情况有两种。第一汉字结构复杂比如「京」「苏」这类字中间可能完全断开成为左右部分投影表面上是多个字符第二字符粘连投影值在所有列都不为 0投影法直接失效。综合做法是先跑连通域用字符的高宽比过滤出候选字符块再结合投影法修正。字符的典型高宽比约在 0.5 到 1.2 之间汉字偏方数字和字母偏窄按照上面的高度就是plateBB(4)*1.6的裁剪后图像字符高度约占车牌高的 70% 到 80%。% 连通域分析 ccChar bwconncomp(bwPlate); statsChar regionprops(ccChar, BoundingBox, Area, Centroid); % 按 x 坐标排序 boxes reshape([statsChar.BoundingBox], 4, []); boxes sortrows(boxes, 1); % 过滤宽高比在 0.3~1.5面积占比适中 charBoxes []; for k 1:size(boxes, 1) w boxes(k, 3); h boxes(k, 4); ratio w / h; area w * h; if ratio 0.25 ratio 1.6 area size(bwPlate, 1) * size(bwPlate, 2) * 0.005 charBoxes [charBoxes; boxes(k, :)]; end end过滤阈值中面积下限取整图的 0.5%这是对「小噪声连通域不算字符」的经验界定。比率范围扩大是为了容纳数字「1」和汉字「川」两种极端宽度的笔画特征。提示连通域分割后标准 7 位车牌会得到 7 个字符块但省会简称「粤」「京」等常会被拆成 2 到 3 块。此时要结合投影法把相邻且纵向重叠的块合并成一个字符。4. 字符识别模板匹配与分类器的工程取舍4.1 字符归一化去除车牌边框干扰% 去边框上下左右各裁剪掉 3% 的边距 [h, w] size(bwPlate); marginX round(w * 0.03); marginY round(h * 0.08); bwClean bwPlate(marginY1:end-marginY, marginX1:end-marginX);车牌边框在二值化后会和字符粘连需要先裁边再去连通域。竖直方向裁 8% 是因为车牌上下边框更粗水平方向只裁 3%。标准化时统一缩放到 40×20 像素同时保持笔画粗细一致。imresize默认双线性插值但二值图用最近邻更合适不然会出现灰色边缘charNorm imresize(charImg, [40, 20], nearest);4.2 模板匹配的数学逻辑与距离度量% 加载模板库50 张31 省份简称 24 字母 10 数字统一 40×20 templateSet load(charTemplates.mat); % 结构体含 chars 和 labels bestMatch ; minDist Inf; for t 1:length(templateSet.labels) tpl templateSet.chars(:, :, t); % 度量 1异或距离汉明距离 xorDist sum(xor(charNorm(:), tpl(:))); % 度量 2归一化互相关 corrVal corr2(double(charNorm), double(tpl)); % 组合评分距离越小越好相关越大越好 score xorDist - 50 * corrVal; if score minDist minDist score; bestMatch templateSet.labels(t); end end异或距离统计的是两个二值图像对应像素不同的数量取值 0 到 80040×20。互相关corr2对轻微形变更鲁棒但也会对笔画粗细差异过敏。把两个指标组合成权重评分经验上异或距离为主、互相关为微调。模板库的来源有两类一是自己用上面的流程对 50 张已标注车牌做分割后构建二是用程序生成标准字体再叠加噪声做仿真模板。前者准确率高但工作量在标注后者精度稍低但可以大量生成。4.3 基于统计特征的识别避免大网络、小样本陷阱如果样本量太少训练 CNN 会过拟合一个替代方案是提取方向梯度直方图HOG特征 SVM。MATLAB 里可以用extractHOGFeatures提取特征但一个值得警惕的点是该特征对笔画粗细和噪声较敏感需要在提取前对字符做骨架化bwmorph的thin选项来减弱笔画粗细差异。% 骨架化后再提取 HOG charSkel bwmorph(charNorm, thin, Inf); features extractHOGFeatures(charSkel, CellSize, [8, 8]);字符识别中 0零与 O字母、1一与 I字母是高频误判对。模板匹配阶段容易把「O」判成「0」因为它们的二值图几乎一致。常见处理方式是后验修正如果识别结果是字母 O 且车牌位置是第四位数字位强制改为 0。4.4 训练集构建与交叉验证的实操框架% 构建特征矩阵 featureMatrix zeros(numSamples, numFeatures); labels strings(numSamples, 1); for i 1:numSamples img imread(sprintf(train_chars/%03d.png, i)); % ... 预处理、归一化、骨架化 ... featureMatrix(i, :) extractHOGFeatures(imgSkel, CellSize, [8, 8]); labels(i) readLabelFile(i); % 从文件名读取标签 end % 训练多分类 SVM一对一策略 mdl fitcecoc(featureMatrix, labels, Learners, svm); % 交叉验证 cvmdl crossval(mdl, KFold, 5); loss kfoldLoss(cvmdl); fprintf(5-fold 交叉验证准确率: %.2f%%\n, (1 - loss) * 100);样本量少于 500 时用crossval做 K 折验证看泛化误差不要只看训练集准确率。fitcecoc内部自动做一对一的多分类每个二分类器都是线性 SVM训练速度快适合字符识别这种类别数 65 个左右的中等规模问题。5. 定位失败、字符粘连、识别混淆的排查路径5.1 定位环节的 4 个必查可视化点% 可视化检查清单 figure; subplot(2, 2, 1); imshow(mask); title(蓝色掩膜); subplot(2, 2, 2); imshow(maskClosed); title(闭运算后); subplot(2, 2, 3); imshow(plate); title(裁剪出的车牌); subplot(2, 2, 4); imshow(bwPlate); title(二值化后的字符);每个子图对应一个可调参数掩膜出现大块蓝色噪声时收紧 H 区间或 S 下限闭运算后车牌区域仍然是碎块时增大结构元素裁剪出的车牌倾斜明显时检查Orientation是否因为连通域不完整而产生偏差二值化后字符断笔画时降低Sensitivity。5.2 字符粘连的启发式拆分粘连的主要成因是二值化阈值偏高把间隙也判成了白像素。如果字符间粘连较窄可以在投影平滑曲线中找局部最小值代替绝对 0 值% 找到投影曲线的全部局部极小值 [~, locs] findpeaks(-projSmooth, MinPeakProminence, max(projSmooth)*0.1); % locs 中相邻距离大于 5 像素的才是有效分隔点 splitPoints locs(diff([0; locs(:)]) 5);findpeaks需要负号传入才能搜索波谷。MinPeakProminence设 10% 的意义是一个真正的字符间隙其投影下降幅度应显著大于普通噪声波动。如果粘连宽度超过 8 个像素启发式拆分不可靠。此时不要硬拆考虑重新拍图或换角度。工程上没有任何字符分割算法能在宽粘连下稳定工作这属于输入质量问题不是算法问题。5.3 模板匹配误判的后处理策略利用车牌本身的结构约束第 1 位是省份简称汉字第 2 位是发牌机关字母第 3 到 7 位可能混合数字和字母但第 5 到 7 位在大多数城市是数字。把先验规则编码进后处理recognized 京A12345; % 假设识别结果 % 规则 1第 5~7 位必须数字化 for pos 5:7 if ~isstrprop(recognized(pos), digit) % 将字母映射到最相似的数字 recognized(pos) letterToDigit(recognized(pos)); end endletterToDigit的映射表需要手动建立例如 B→8、S→5、O→0、Z→2。这个规则的有效性取决于目标城市是否在末位使用字母上海、广州的号段规则不同要根据实际数据调整。6. 识别准确率验证的闭环方法6.1 用混淆矩阵定位高误判字符类别% 假设有真实标签和识别结果 actual load(actualLabels.mat); % 字符串数组与预测一一对应 predicted load(predictedLabels.mat); % 求混淆矩阵 classes unique(actual); [C, order] confusionmat(actual, predicted, Order, classes); % 显示每个字符的准确率 accPerClass diag(C) ./ sum(C, 2); for i 1:length(order) fprintf(%s: %.2f%%\n, order(i), accPerClass(i) * 100); end % 找到最低的三个 [~, idx] sort(accPerClass); fprintf(最容易混淆的字符: %s, %s, %s\n, order(idx(1)), order(idx(2)), order(idx(3)));confusionmat的返回矩阵第 i 行表示真实类别为第 i 类的样本分别被预测成了哪些类。对角线占比过低就说明该字符特征提取不够有区分度。常见结果里「0」和「O」互混、「B」和「8」互混如果这些类别恰好占比高优先补充模板或样本。6.2 光照变化的批量压力测试% 对同一张车牌图做多组亮度变换验证识别鲁棒性 baseImg imread(test_plates/001.jpg); brightnessFactors [0.4, 0.7, 1.0, 1.3, 1.6]; for i 1:length(brightnessFactors) adjusted imadjust(baseImg, [], [], brightnessFactors(i)); result plateRecognitionPipeline(adjusted); % 你自己的主函数 fprintf(亮度系数 %.1f - %s\n, brightnessFactors(i), result); endimadjust的第四个参数大于 1 时让图像整体变亮小于 1 变暗。这个测试能快速判断你的预处理对光照有多敏感。如果 0.4 和 1.6 全挂、1.0 全对说明自适应二值化的参数还需要放宽。6.3 减少回归错误的 Git 化实验习惯把每次调整写成一个独立脚本输出目录按「日期 参数摘要」命名experiments/ 20260612_hsv_055_068_s15/ 20260613_adaptive_sens_045/每个实验目录里保存四样东西运行脚本、复制出来的中间图像、准确率统计文件、参数改动说明。下次调参时直接对比两目录的准确率比在同一个脚本里反复改注释要可靠得多。最后一笔车牌识别的精度天花板不在识别算法而在定位和二值化——这两步决定了交给分类器的输入质量。用imshow把每个中间步骤可视化出来永远比直接看最终准确率更能定位问题。本文还有配套的精品资源点击获取
返回列表