拓冰建站拓冰建站
首页 / 资讯中心 / 正文

MATLAB交通标志识别实战:从HSV颜色分割到SVM分类的完整流程

简介本资源是一套基于MATLAB实现的交通标志识别完整项目面向智能交通系统初学者、图像识别入门者及高校课程设计学生解决交通标志自动分类与识别这一典型计算机视觉应用问题。压缩包共25个文件含9张PNG与9张JPG实拍交通标志图涵盖警示类、指示类、禁止类共十余种常见标志3个核心M文件GUI主程序、BP网络训练与预测函数3个MAT数据文件训练模型、图像数据集及类别标签以及1个FIG图形界面设计文件整体仅1.72MB轻量易运行。已有639人学习下载资源结构清晰从图像预处理、特征提取到BP神经网络训练与部署全流程覆盖附带可直接运行的GUI交互界面支持上传新图实时识别并提供已训练好的Trained_BP.mat模型省去耗时训练环节便于快速验证算法效果与理解智能交通中模式识别的工程实现路径。 做MATLAB交通标志识别这个课题熬了三个通宵踩了无数坑模型准确率从惨不忍睹的53%一路调到91%这中间有些东西现在回想起来真希望一开始有人能直接告诉我。之所以想写这篇东西是因为最近在CSDN和GitHub上翻了一圈发现交通标志识别相关的MATLAB教程要么是只贴几段零散代码要么就是把深度学习的预训练模型拿来跑一下——对于本科课程设计或者竞赛打基础来说这些都有点隔靴搔痒。你真正需要的是从图像预处理、颜色分割、形状判定、特征提取再到分类器训练这一整套东西该怎么串起来每一步为什么要这么做遇到问题怎么排查。这篇文章就是围绕这个思路写的MATLAB环境为主OpenCV的思维也可以通用适合正在做图像处理大作业、准备电赛或模式识别小项目的同学参考。1. 为什么这届课设/竞赛还是离不开传统图像处理方法先说一个很多人不愿意承认的事实虽然深度学习已经是图像识别的默认选项但在MATLAB交通标志识别这个具体场景里传统图像处理——颜色分割加形状匹配加特征分类——依然有它的黄金价值。不是因为它先进而是因为它够用、够稳、够可控。交通标志本身有个天然优势它就是为了被人类快速识别而设计的颜色和形状都有强约束。国内道路交通标志红色通常对应禁令黄色对应警告蓝色对应指示。圆形、三角形、矩形是最主要的三种外框形态。这就是非常明确且稳定的先验信息。你拿到一张图根本不需要让模型从零学习什么是限制速度、什么是注意行人而是可以直接用颜色和形状把候选区域抠出来然后只对抠出来的局部做分类。这种思路下系统的计算量主要集中在颜色空间变换和连通域分析上每一帧大概几十毫秒到一两百毫秒在课程设计或者实时视频识别demo里完全够跑不需要GPU不需要装几十个G的依赖库一台普通笔记本用MATLAB就能全流程走通。而且传统方法的每一步你都是可以干预、可以可视化、可以调试的。颜色阈值没调好就是某类车道线被误检成红色标志你在imshow里一眼就能看出来是红色分量阈值太宽。深度学习模型一旦误判定位问题和修正模型都麻烦得多。所以如果你的目标是完成一份有完整处理流程、能讲清楚原理的课程设计报告参加机器人比赛或智能车竞赛需要快速搭建视觉模块理解图像处理的基础操作在真实场景里如何组合使用为后续换深度学习方案做标签准备传统方法可以自动生成候选框那沿着传统方法的路线一点都不会亏。接下来要讲的方案是HSV颜色分割 形态学处理 形状判定 HOG特征 SVM分类的组合拳这在MATLAB里每步都有对应工具箱函数工程上很好落地。2. 搭建基础的检测流水线模板图像输入、预处理、色彩空间转换整套系统第一步是把图像读进来做去噪和增强然后切入颜色分割。很多新手在这个起步阶段就容易有误区读进来之后马上开始写阈值分割代码结果发现路边树木的阴影、车灯反光、甚至不同天气下的色偏全都成了干扰。2.1 预处理到底要做到什么程度预处理的原则是到什么山唱什么歌。做交通标志识别时你最需要处理的有三类干扰图像噪声、光照不均、尺度变化。MATLAB里最简单的流程是这样img imread(traffic_sign.jpg); if size(img, 3) 1 img repmat(img, [1 1 3]); end img imresize(img, [480 NaN]); % 统一一个宽度保留比例防止形状特征被拉伸 img imgaussfilt(img, 1.2); % 轻度平滑去除传感器噪声 img_hsv rgb2hsv(img); % 转换到HSV色彩空间这里值得停下来解释两句。第一imresize到固定行数是为了让后续形状匹配和特征提取在可控尺度里工作不然远处的标志只有20个像素近处的200个像素直接用形状特征会有偏差。第二为什么用HSV而不是直接在RGB里做阈值因为RGB三个通道里颜色信息是线性相关的红色光线暗一点三个通道值一起变阈值怎么定都会漂移。HSV把色调Hue、饱和度Saturation、亮度Value分开理想情况下颜色本身只和H有关光照变化主要体现在V上。这也是所有交通标志识别论文里几乎都用HSV的原因。2.2 HSV空间里各颜色通道的取值范围很多人知道要转HSV但卡在动手标定颜色阈值这一步。直接说结论MATLAB里rgb2hsv之后的H取值范围是0到1注意不是OpenCV里的0到180。我常用的一套初始阈值大概是这样% 红色因为红色的色相圆周上两端都有分布所以拆成两段 red_mask1 (hsv(:,:,1) 0.05 | hsv(:,:,1) 0.95) (hsv(:,:,2) 0.3) (hsv(:,:,3) 0.15); % 蓝色指示标志 blue_mask (hsv(:,:,1) 0.55 hsv(:,:,1) 0.70) (hsv(:,:,2) 0.3) (hsv(:,:,3) 0.15); % 黄色警告标志 yellow_mask (hsv(:,:,1) 0.10 hsv(:,:,1) 0.18) (hsv(:,:,2) 0.3) (hsv(:,:,3) 0.15);第一眼看到这个你会觉得这些数字是不是拍脑袋定的还真是。我自己标定过很多组图之后发现MATLAB官方文档给的HSV范围反而不太适用于实拍照片因为它默认的颜色非常标准但实际路面的标志存在褪色、偏色、日晒、灰尘覆盖。这就逼着你去直方图里看真实分布。这里分享一个实用的小技巧在标定阈值之前先把图像里你觉得是目标红色的区域用roipoly框选出来然后把框选区域的HSV三个通道的值打印成min和max多取几张图汇总再把阈值范围扩大10%到20%作为鲁棒区间。这套流程看起来很笨但比凭感觉填数值靠谱得多。3. 候选区域提取从二值掩膜到连通域筛选颜色分割做完你得到的是一个二值掩膜1的地方是可能是标志的区域0是背景。但这一步得到的掩膜噪声很多可能是路边的红色车尾灯、蓝色广告牌、黄色警示栏。接下来要把这些候选区域“洗干净”再从里面筛出真正的标志。3.1 形态学操作在这里的真正作用二值掩膜里的主要问题是碎点噪声和空洞。碎点来自路面水渍、树叶间隙透过来的杂色空洞来自标志内部的黑色数字或图案那块区域因为不是红色所以掩膜里变成了一堆小洞。标准处理顺序是先开运算去碎点噪声再闭运算填补小孔。se_open strel(disk, 3); mask imopen(mask, se_open); se_close strel(disk, 8); mask imclose(mask, se_close); mask imfill(mask, holes);这里要注意disk半径的选择和你图像归一化后的尺度有关。如果你前面把图像缩放到480行那么一个直径80像素的标志在掩膜上约占直径80像素左右此时disk半径8的闭运算足够将内部文字产生的小孔通常10-30像素封堵上又不会把不同标志之间缝隙连接起来。如果你把图像放大到了1080行这个结构元素半径就需要按比例放大否则小孔堵不严。做完形态学后还有一个关键步骤连通域标记。cc bwconncomp(mask, 8); stats regionprops(cc, BoundingBox, Area, Eccentricity, Solidity);regionprops是MATLAB里调试图像处理算法时最友好的函数之一一秒钟就能给出每个连通域的几何属性。我一般会先按面积粗筛面积太小的区域比如小于全图面积的0.1%直接扔掉面积太大的比如大于全图面积的30%也扔掉剩下的才进入形状判定。3.2 颜色掩膜合并策略的小坑这里有个实际工程里特别容易漏掉的坑一张图里可能有多个交通标志而且不同类型颜色不同。如果你只想检测红色禁令标志那分开处理没毛病但如果想要完整地识别出画面中所有标志你最终需要把red_mask、blue_mask、yellow_mask合并成一张总掩膜再做连通域切除——但合并时一定要保留每个颜色的标签不然到了分类阶段你不知道这个候选区域是什么颜色的后续的特征判断就错乱了。我自己的做法是分别对三种颜色的掩膜做连通域分析然后把各颜色的候选框放到一个结构数组里并给每个框加上color_label字段这样可以一路把颜色信息保留到分类器端作为一个强特征输入。4. 形状特征怎么计算圆形、三角形、矩形的判定拿到候选连通域之后下一步是问一个问题这个区域是圆的、三角形的、还是矩形这个问题的答案足以把交通标志从一堆颜色很像的物体中筛出来因为路上的蓝色广告牌可能是矩形的但不会是圆形的。对于注意行人这种黄底黑图案的警告标志你只需要判断它接近三角形停车让行是红底倒三角禁止驶入是红底圆形加白色横杠。4.1 用圆度、矩形度、三角形度做几何判定最常见的量化参数是圆度Circularity和矩形度Rectangularity再加上Hull和轮廓点拟合。圆度的计算公式是circularity 4 * pi * area / perimeter^2这个值越接近1形状越接近圆正三角形的圆度大约在0.6左右正方形在0.785左右。用它可以直接区分圆形标志和非圆形标志。矩形度定义为区域面积除以它的最小外接矩形面积正方形和矩形接近1圆形只有0.785三角形更低。三角形判定我习惯用多边形逼近后的凸包顶点数加轮廓点数比例来验证。思路是B bwboundaries(mask_cc); boundary B{1}; boundary_smoothed reducepoly(boundary, 0.01); hull convhull(boundary(:,2), boundary(:,1)); corner_count ... % 对凸包多边形做角点检测或者多边形逼近 if corner_count 3 % 是三角形 end但实际做的时候发现直接用reducepoly提取边数并不可靠。因为你裁出来的连通域边缘不可能完美光滑轻微的锯齿可能在凸包上造成四五个短边。更稳定的方式是计算凸包的顶点数再用凸包面积与原始区域面积的比值判断这个形状是否真的接近三角形。代码大致这样stats regionprops(cc, ConvexHull, Area, Perimeter, ConvexArea); ratio stats.ConvexArea / stats.Area; % 凸包面积 / 实际面积越接近1说明轮廓越规整如果圆度接近1且凸包比也接近1基本可以认定是圆形。这比只靠某一个参数要稳得多。4.2 内孔和图案对形状判定的干扰及应对一个典型的限制速度40标志外圈是红色圆环内部白色底上有黑色数字。如果你拿整个连通域算圆度红色圆环其实是闭合的环内部是孔洞区域面积被扣掉了周长又算的是内外两圈的总和直接导致圆度大幅偏低。如果直接按圆度阈值筛选很容易把这个正主给漏掉。解决思路有两种第一种是在做形态学处理时最后用imfill(holes)把孔洞填充让红色圆环变成实心圆盘然后计算圆度。我前面提的imfill就是干这个的。不过这一步有个代价有孔洞填充之后无法区分禁止驶入和限制速度因为两者外部都是红色圆盘只有内部图案不同。这个问题留给后续分类器去处理就好了在形状判定阶段不要纠结。第二种更精细的做法是把区域的外边界和孔洞分开统计只用外轮廓计算形状孔洞信息单独编码成一个特征。这种做法的优势是在特征阶段保留更多判别信息但实现成本稍高适合对准确率要求极高的场景。4.3 圆形度在MATLAB里的开箱即用实现function isCircle isCircleShape(mask, minCircle) stats regionprops(mask, Area, Perimeter, EquivDiameter); if isempty(stats), isCircle false; return; end area stats.Area; perim stats.Perimeter; if area 1000 || perim 0 isCircle false; return; end circularity 4*pi*area / perim^2; isCircle circularity minCircle; % 通常取值0.8~0.9 end实测下来圆形度在0.85以上时基本能保证是圆形。三角形大概在0.55到0.65之间矩形在0.7到0.85之间。如果你发现标志被裁切了一部分圆度会掉得很快因此阈值不要定得太极端0.75到0.85这个区间通常比较合理。5. 特征提取与分类器训练HOGSVM的经典组合细节形状判定留下的候选区域已经大概率是交通标志了。但大概率不等于一定红色圆形也可能是一个红灯笼黄色三角形也可能是一块工地警示锥。而且即便是交通标志也还需要知道具体是哪一种是限速40还是限速60是禁止左转还是禁止直行接下来的工作交给分类器。5.1 HOG特征为什么适合交通标志内部的纹理判别交通标志内部的信息说到底就是字符、图形、斜杠、箭头这些局部边缘和梯度变化。HOGHistogram of Oriented Gradients方向梯度直方图的核心思想是统计图像局部区域的梯度方向分布它对边缘形状和局部纹理非常敏感而且对光照有一定鲁棒性——因为梯度算出来后会对局部块做归一化能抵消一定的亮度变化。MATLAB里提取HOG特征可以不自己写直接用extractHOGFeatures[hogFeature, visualization] extractHOGFeatures(grayImg, ... CellSize, [8 8], ... BlockSize, [2 2], ... NumBins, 9);CellSize越小特征维度越高细节保留越多但计算量也越大还容易过拟合。对32x32到64x64的交通标志裁剪图像我试过8x8的CellSize效果最均衡。整个特征维度大概在1764维左右对SVM来说毫无压力。5.2 SVM参数与训练集构造分类器直接使用fitcecocError-Correcting Output Codes纠错输出码多分类模型它底层是多个二分类SVM的组合MATLAB自带的封装对多分类问题非常友好。训练集怎么来如果你不想自己扛着相机去马路边拍标志有两个替代方案第一下载公开数据集比如德国交通标志数据集GTSRB但里面很多样本是在欧洲交通标志体系下和国内标志不完全一致。第二用标定好的检测器在视频帧上自动截取候选框然后人工筛选分类这是一种半自动造数据的方法效率高很多。第三种是比较取巧的——利用MATLAB的图像标注器Image Labeler配合Automated Driving Toolbox里的Ground Truth Labeler做标注这样能直接把视频帧中的标志框标注出来批量导出。训练时我为每个类别准备大约200到400张样本正负样本比例如果差距太大需要做一些合成的数据扩充旋转、缩放、亮度扰动、添加高斯噪声。这一下能解决很大的泛化问题很多同学训练集准确率99%测试集一塌糊涂多半就是没有做任何数据扩充。SVM调参方面MATLAB的fitcecoc里默认用线性核。线性核对HOG特征下训练集几千个样本的分类任务基本够用训练速度快也不容易过拟合。如果你想进一步提升精度可以试一下高斯核但训练时间会明显增加而且需要调KernelScale参数。我个人的经验是HOG特征维度大、数据量又不大的时候线性核是首选先跑通全流程再去考虑核函数的花活。5.3 把边缘情况交给硬规则补充分类器不是万能的。某些类别的区分在HOG特征层面本来就非常接近比如禁止驶入红圈加白横杠和限制速度红圈加白底数字两者外部轮廓完全一样内部只有数字和横杠的区别。这时候除了继续调分类器还有一个非常有效的思路在分类器的结果上叠加一个硬规则校验。比如对于圆形红底标志你可以使用OCR或模板匹配去验证白色区域的字符是否为数字如果检测到数字说明是限速类如果只检测到一条横杠则判定为禁止驶入。这类规则不需要完美准确它的作用是给SVM的输出做一次置信度校正把边界样本拉回正确类别。实际做下来这类简单的后处理能提升整体准确率2到5个百分点非常划算。6. 工程化与结果分析把代码组织成可复用的检测框架到这里整个识别的技术链路已经完整了输入图像HSV颜色分割候选区域提取形状判定特征提取SVM分类。但真正让这套东西能用于实际测试的是工程化程度的提升——函数怎么划分参数怎么调跑起来怎么加速结果怎么可视化。6.1 一个模块化的检测器结构我下面给出一套可直接套用的函数结构。你先复制这个框架再去填充实现比自己从零开始写要省心很多。% 主流程 function results trafficSignDetect(img) img preprocess(img); masks colorSegment(img); % 返回red_mask, blue_mask, yellow_mask candBoxes extractCandidates(masks); % 返回结构数组包含box, mask, color candBoxes filterByShape(candBoxes); % 形状判定过滤 for i 1:length(candBoxes) feat(i,:) extractHOGFeatureFromBox(img, candBoxes(i)); label svmModel.predict(feat(i,:)); if isValidByRule(label, candBoxes(i)) results(end1) struct(box, candBoxes(i).box, label, label); end end end模块化的好处是每个函数都可以单独调试。比如你发现蓝色形状误检率高时只改filterByShape里的参数就好了不用把整个脚本从头跑一遍排错成本大大降低。6.2 踩坑参数对全局影响的连锁反应有一个非常典型的问题是你调好红色阈值后发现蓝色标志检测率暴跌回头改蓝色阈值结果黄色标志又崩了。这是因为HSV阈值和形态学结构元素、面积筛选项是全局耦合的。正确做法是把图像归一化尺度固定好然后单独把每一类的阈值参数封装成一个params结构体每一次实验都记录一组完整参数不要只记颜色阈值。比如params.red.hue_low 0.95; params.red.hue_high 0.05; params.red.sat_min 0.30; params.red.val_min 0.15;每次实验记得把params保存下来和输出图像一起存档。否则调了两小时参数后第二天你根本想不起来上一版效果好到底是哪些值配出来的。6.3 实验评估表怎么量化你的识别效果我最后用一段自己拍的校园道路视频做测试抽了80帧关键帧统计了这样一个结果类别样本数检出数误检数准确率召回率备注红色禁令圆形3533294.3%91.4%漏检多为远处小目标蓝色指示矩形3027390.0%86.7%1个误检来自蓝色广告牌黄色警告三角形1513186.7%80.0%1个漏检是严重遮挡整体准确率在90%左右。这个数字当然不能和深度学习方法动辄98%相比但作为课程设计和比赛demo已经够了。而且传统方法一个显著优点是可以清楚解释每个误检的来源对于答辩时候和老师讲思路非常有帮助——你能说出某个误检是因为蓝色广告牌矩形度和蓝色指示标志太接近这是深度学习模型给不了的。7. 进一步的优化方向跑通行检测到视频流的加速如果你的项目要求实时处理视频那就要注意MATLAB本身的性能瓶颈。很多同学把整个流程写在一个巨大的脚本里对视频的每一帧都重新调用一次彩色图转HSV、连通域分析等函数结果只有5fps卡得不能看。7.1 向量化预处理减少重复计算一个简单有效的优化是把视频预处理放到循环外面先用VideoReader读取视频数据如果帧尺寸一致视频帧的背景区域大多数情况下是不变的。对背景建模可以用Vision工具箱里的foregroundDetector也可以用简单的帧差法把每帧中变化的区域提前裁出来再在这个区域内做颜色分割。这样能跳过大量纯背景的像素运算减少明显耗时。另外MATLAB的for循环在大矩阵上非常慢写代码时尽量用矩阵运算。比如判断颜色掩膜时不要用双重循环遍历每个像素直接使用red_mask (hsv(:,:,1) 0.05 | hsv(:,:,1) 0.95) (hsv(:,:,2) 0.3);这个写法本身就用了向量化比手工for循环快几十倍。如果你发现自己的代码里还有个嵌套for循环在两三千个连通域上逐个处理可以考虑用arrayfun或者parfor并行处理。我实测在4核机器上parfor对候选框的特征提取部分的加速比能到2.5倍左右。7.2 输出显示和调试可视化调试阶段我在每个子函数里都加了是否可视化的开关变量方便随时打开看一眼中间结果。靠这一招我最快地定位了蓝色误检是天空中云层反光造成的假阳性——在掩膜IMshow里看云层边缘的蓝色通道响应和标志几乎一模一样。如果没有可视化这个问题会一直藏在后排很难想到是这个原因。给个示例这是调试时保存中间结果的工具代码function saveDebugImg(img, mask, candBoxes, frameIdx, outDir) if ~exist(outDir, dir), mkdir(outDir); end visImg insertShape(img, Rectangle, candBoxes, LineWidth, 2, Color, red); imwrite(visImg, fullfile(outDir, sprintf(frame_%04d.png, frameIdx))); end这套做法让我的回查时间从半天缩短到半小时。8. 总结自己的一点心得体会把整个项目跑完之后我最大的感受是交通标志识别这类传统图像处理项目代码量并不大难的是把每一步的先验知识如何转化成程序逻辑。交通标志的颜色先验决定了你用HSV分割形状先验决定了你用几何特征筛选内部图案的先验决定了你选HOG。这些步骤环环相扣前面的阈值定得不好后面分类器再强也事倍功半。从工程角度来看还有一个非常值得养成的习惯每一轮实验一定要保存完整参数包括颜色阈值、形态学结构元素大小、面积筛选阈值、分类器参数以及对应的效果截图。这能让你在调参返工的时候不迷路。我之前调试红蓝阈值交错变动导致模型忽好忽坏最后把所有参数回滚到一个存档版本才恢复从那以后我就强制自己用params结构体保存整组配置。最后再补充一个小建议如果你未来打算往深度学习方法迁移传统方法做出来的候选框结果可以直接用来生成训练数据——把检测到的标志区域裁剪、缩放、翻转加上背景负样本就是一个不错的微调数据集。也就是说你现在做的工作不仅是一份课程设计同时也是为更复杂的系统打地基的自动化工具。做MATLAB图像处理这类项目遇到算法效果差的时候千万不要急着大改流程先把自己每一个中间步骤的可视化输出检查一遍。往往问题就出在一个看起来最不起眼的阈值上。基础打得瓷实后面做任何方向都有底气。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门