尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

MATLAB交通标志识别实战:从图像预处理到SVM分类的完整流程

MATLAB交通标志识别实战:从图像预处理到SVM分类的完整流程 简介本资源是一套基于MATLAB实现的交通标志识别完整项目面向智能交通系统初学者、图像识别入门者及高校课程设计学生解决交通标志自动分类与识别这一典型计算机视觉应用问题。压缩包共25个文件含9张PNG与9张JPG格式的真实交通标志图像涵盖警示类、指示类、禁止类等常见类别3个核心M文件Traffic_Iden.m实现GUI主逻辑TrainBP.m与BP_Prince.m分别负责BP神经网络训练与预测3个MAT数据文件Trained_BP.mat保存已训练模型参数Data.mat与Name.mat存储预处理图像矩阵及对应标签以及1个FIG图形界面文件整体大小仅1.72MB轻量易部署。已有639人学习下载。读者可直接运行GUI交互识别新图像深入理解从图像预处理、特征提取到BP神经网络建模、训练、保存与调用的全流程同时获得可复用的交通标志数据集结构与模块化代码组织范式。1. 初识工程这个 MATLAB 交通标志识别项目到底在做什么交通标志识别英文叫 Traffic Sign Recognition缩写 TSR。它本质上是计算机视觉领域里“目标检测分类”的一个典型落地场景专门解决“摄像头拍到的画面里有没有交通标志牌、标志牌在哪、它是什么含义”这三个问题。用 MATLAB 来做这个项目在国内高校的课程设计、毕业设计、电子设计竞赛里非常常见因为它不需要像深度学习框架那样搭复杂环境MATLAB 自带的 Image Processing Toolbox 和 Computer Vision Toolbox 就能覆盖大部分流程对新手极其友好。这个项目的核心价值在于它把图像处理中的经典操作——灰度化、边缘检测、形态学处理、特征提取、分类器训练——全部串在了一条完整的流水线上。做完这个项目你不仅学会了“怎么识别交通标志”更关键的是理解了“一套图像识别系统是怎么从零开始搭起来的”。这套思路可以迁移到人脸检测、车牌识别、手写数字识别等一堆类似任务上。适合什么人来参考第一类是正在做课程设计/毕业设计的本科生需要一套能跑通、能讲清楚原理的完整方案第二类是刚入门计算机视觉的开发者想用 MATLAB 快速验证一个识别 idea 是否可行第三类是准备参加数学建模或电子设计竞赛的学生需要一个现成的、可改可扩展的 baseline。我个人的建议是别把这个项目当成“调包”而是当成“解剖麻雀”。你哪怕只是把其中一条路径比如从二值化到连通域标记再到模板匹配完整理解透收获都远大于跑通十几个现成 demo。2. 整体方案设计为什么选“传统图像处理机器学习”而不是直接上深度学习2.1 技术选型背后的实际考量看到“交通标志识别”很多人的第一反应是“用 CNN 啊YOLO 啊”。这话没错但要看你所处的场景。在 MATLAB 的课程设计或毕设环境里直接用深度学习有四个现实问题一是很多同学的电脑没有 NVIDIA 显卡用 CPU 训练一个像样的检测模型非常痛苦二是深度学习项目容易陷入“调参地狱”一旦效果不好很难在答辩时讲清楚到底哪里出了问题三是 MATLAB 的 Deep Learning Toolbox 虽然能用但对新手来说数据标注、网络结构设计、训练过程调试的链路明显比传统方法繁琐四是传统方法的每个环节都能可视化每一步的中间结果都能展示这在答辩和写报告时是巨大的加分项。所以对于“从零开始做一个能跑通、能演示、能讲明白”的交通标志识别项目合理的方案是图像预处理 颜色空间分割 形态学处理 连通域定位 HOG 特征 SVM 分类器。这条路线每一步都透明可控效果也完全够用尤其是在德国交通标志数据集GTSRB或自建的小规模数据集上识别准确率做到 90% 以上不困难。2.2 方案的整体流程图解这个项目我建议按下面的模块来组织代码图像采集模块读入图片或视频帧统一尺寸和格式预处理模块灰度化、直方图均衡化、高斯滤波去噪标志定位模块基于颜色红色/蓝色/黄色分割候选区域再用形态学闭运算连通分散区域最后用连通域分析框出候选目标特征提取模块对候选区域提取 HOG 特征或者直接缩放到固定尺寸后提取颜色直方图 形状特征分类识别模块训练一个 multiclass SVM用 fitcecoc输出标志类别结果可视化模块在原始图像上画出检测框并在框上标注识别出的标志名称。有人会问为什么不直接用模板匹配模板匹配的问题在于它对尺度、旋转、光照变化极其敏感同一块“禁止停车”标志拍摄距离远了近了的像素差异就足以让匹配分数崩掉。而 HOG SVM 对局部形变和光照变化有很强的鲁棒性这也是为什么在深度学习普及之前HOG SVM 是人脸检测和行人检测的主流方案。3. 核心环节实现从图像预处理到分类输出的完整流程3.1 图像采集与预处理模块先说图像采集。如果你用的是 GTSRB 这类公开数据集图片尺寸不统一而且很多是小图。我建议先把所有训练图片统一缩放到 32×32 像素或者 48×48 像素原因后面讲 HOG 参数时会解释。如果是自建数据集用手机或摄像头拍的时候尽量保持标志在画面中间、占画面比例不小于 20%减少后期裁剪的麻烦。预处理阶段的代码我直接贴出来% 读取图像 img imread(stop_sign.jpg); % 如果是RGB图像转为灰度 if size(img, 3) 3 grayImg rgb2gray(img); else grayImg img; end % 直方图均衡化增强对比度 enhancedImg histeq(grayImg); % 高斯滤波去除噪声sigma取1.5 filteredImg imgaussfilt(enhancedImg, 1.5);这里有一个细节值得注意高斯滤波的 sigma 参数不要贪大。sigma 1.5 在多数场景下已经足够过大的 sigma 会把标志边缘也模糊掉导致后续边缘检测或 HOG 特征质量明显下降。我见过有同学把 sigma 设成 5结果标志边缘糊成一团SVM 的准确率掉了十几个百分点。为什么先均衡化再滤波而不是反过来因为直方图均衡化会夸张噪声如果先均衡化再滤波可以顺带把放大的噪声压下去。反过来操作噪声已经被均衡化放大了滤波也救不回来。这个顺序是靠实际效果踩坑踩出来的。3.2 基于颜色空间的标志定位交通标志有一个非常强的先验颜色。中国的交通标志禁令标志是白底红圈警告标志是黄底黑边三角形指示标志是蓝底白图案。利用颜色先验做分割可以把候选区域从全图快速缩小到几个小区域大大减轻后续分类的压力。在 MATLAB 中颜色分割通常不在 RGB 空间做而选择 HSV 空间。原因是 RGB 的三个通道相关性太高对光照变化极其敏感。HSV 把色调Hue、饱和度Saturation、明度Value分开其中 H 通道对光照相对稳定做颜色分割最合适。% 转换为HSV颜色空间 hsvImg rgb2hsv(img); % 提取H、S、V三个通道 hChannel hsvImg(:, :, 1); sChannel hsvImg(:, :, 2); vChannel hsvImg(:, :, 3); % 红色分割红色在H通道有两个区间0附近和0.9-1附近 % 因为HSV的H通道是环形0度附近是红色 redMask (hChannel 0.03 | hChannel 0.95) sChannel 0.3 vChannel 0.2; % 蓝色分割蓝色大约在H0.55-0.70 blueMask (hChannel 0.55 hChannel 0.70) sChannel 0.3 vChannel 0.2; % 黄色分割黄色大约在H0.10-0.20 yellowMask (hChannel 0.10 hChannel 0.20) sChannel 0.3 vChannel 0.2;这里面的阈值怎么定我告诉你一个笨但有效的方法把图片导入 MATLAB用 imtool 函数打开鼠标悬停在标志区域上直接读 H、S、V 三个值统计多张图片取一个共性的区间。我自己的经验值是红色 H 在 0.95 到 1.0 和 0 到 0.03 两段蓝色 H 在 0.55 到 0.70黄色 H 在 0.10 到 0.20S 和 V 都卡在 0.2 或 0.3 以上即可。如果你的图是手机拍的偏色严重可以适当放宽 S 和 V 的下限。颜色分割之后得到一个二值掩码。但这个掩码会有很多离散的小噪声点需要用形态学操作清理一下% 中值滤波去小块噪声 cleanMask medfilt2(redMask, [5 5]); % 形态学闭运算先膨胀后腐蚀把断开的部分连起来 se strel(disk, 5); closedMask imclose(cleanMask, se); % 填充孔洞 filledMask imfill(closedMask, holes); % 连通域分析 cc bwconncomp(filledMask, 8); % 筛选面积合适的区域 for i 1:cc.NumObjects area numel(cc.PixelIdxList{i}); if area 500 area size(img, 1) * size(img, 2) * 0.5 % 这个区域是候选目标 end end闭运算的结构元素半径取多少取决于你的图像尺寸和标志在画面中的大小。如果标志离镜头远、像素面积小半径取 3 就够如果标志占画面比例大半径可以取 7 甚至 10。一个经验是结构元素半径不要超过标志短边长度的十分之一否则两个相近的部件会被融合成一个不规则团块导致后续裁剪区域不准确。3.3 候选区域裁剪与尺寸归一化做完连通域分析后用 regionprops 提取每个候选区域的 BoundingBox然后在原图上把对应位置裁剪出来stats regionprops(filledMask, BoundingBox, Area); % 按面积从大到小排序一般面积最大的就是我们要的 [~, idx] sort([stats.Area], descend); for i 1:min(3, length(idx)) bbox stats(idx(i)).BoundingBox; % 在原图上裁剪候选区域 candidate imcrop(img, bbox); % 缩放到统一尺寸32x32 candidate imresize(candidate, [32, 32]); end为什么要统一缩放到 32×32因为 HOG 特征提取需要固定窗口大小而且 SVM 分类器的输入维度必须是固定的。32×32 是我试下来性能与计算量平衡较好的选择。如果缩放到 64×64HOG 特征的维度会翻好几倍训练时间变长但准确率提升有限。GTSRB 上很多论文用 48×48也足够。裁剪时的关键点是BoundingBox 是浮点数imcrop 会自动取整但要注意边界溢出。如果 bbox 超出图像边界需要在裁剪前用 min/max 把坐标限制在图像范围内% 边界限制 x1 max(1, floor(bbox(1))); y1 max(1, floor(bbox(2))); x2 min(size(img, 2), floor(bbox(1) bbox(3))); y2 min(size(img, 1), floor(bbox(2) bbox(4))); candidate img(y1:y2, x1:x2, :);这个小坑不处理程序会直接报错而且是在你意想不到的地方报“索引超出矩阵维度”排查起来非常浪费时间。3.4 HOG 特征提取为什么它能“看懂”形状HOGHistogram of Oriented Gradients方向梯度直方图是 2005 年由 Dalal 和 Triggs 提出的特征描述子最初用于行人检测后来被广泛用于各种物体识别。它的核心思想是一幅图像中物体的局部形状可以通过梯度方向的分布来描述。你把图像划分成小的 cell例如 8×8 像素在每个 cell 里统计梯度方向直方图然后把相邻的 cell 组成 block例如 2×2 个 cell在 block 内做对比度归一化最终得到一串特征向量。为什么要用梯度方向而不是原始像素值因为梯度方向对光照变化和颜色变化不敏感。比如同一个“禁止驶入”标志白天和傍晚拍出来的红圈亮度差异很大但红圈边缘的梯度方向基本不变。这就像你看一个物体的轮廓无论它被光照成亮的还是暗的轮廓的形状没有变。在 MATLAB 中用 extractHOGFeatures 函数非常方便% 在32x32的归一化图像上提取HOG特征 [hogFeature, visualization] extractHOGFeatures(candidate, ... CellSize, [8 8], BlockSize, [2 2], NumBins, 9); % 特征维度 floor(32/8) * floor(32/8) * 2 * 2 * 9 4*4*4*9 576这里解释一下维度计算32×32 的图像CellSize 为 8×8水平方向有 4 个 cell垂直方向有 4 个 cell共 16 个 cell。BlockSize 为 2×2即 4 个 cell 组成一个 block水平方向有 3 个 block垂直方向有 3 个 block共 9 个 block。每个 block 计算 4 个 cell 的直方图每个直方图有 9 个 bin0-180 度分成 9 个区间所以每个 block 的维度是 4×936。最终特征维度是 9×36324。但我上面代码里写的是 576这是因为我用的是默认的 BlockOverlap 设置默认 overlap 是 0.5所以 block 数量增加到了 4×41616×36576。具体维度可以在 MATLAB 里用 size(hogFeature) 查看。HOG 参数有三个调节重点。第一是 CellSize越小越能捕捉细节但维度越大计算越慢第二是 NumBins太少了区分度不够太多了容易过拟合第三是 BlockOverlapoverlap 越大特征对局部形变的鲁棒性越强但维度也越大。我实测下来CellSize 为 8×8、NumBins 为 9、BlockOverlap 为 0.5 是最平衡的组合。3.5 分类器训练SVM 多分类的工程实现特征提取完之后接下来就是训练分类器。在 MATLAB 中推荐使用 fitcecoc它底层实现是“一对一”One-vs-One的 SVM 多分类策略。也就是说如果有 5 类标志就训练 C(5,2)10 个二分类器每个二分类器负责区分其中两类最终通过投票决定样本的类别。为什么要用 OvO 而不是 OvROne-vs-Rest在类别数不多比如 10 类以内时OvO 的每个二分类器只用到两类样本训练速度快而且类别不平衡的影响小。OvR 每次要用全部样本而且负样本太多会严重偏向负类。训练代码% 假设 trainFeatures 是 N×M 的特征矩阵 % trainLabels 是 N×1 的类别标签可以是 categorical svmModel fitcecoc(trainFeatures, trainLabels, ... Learners, svm, ... Coding, onevsone, ... KFold, 5); % 查看交叉验证准确率 cvAccuracy 1 - kfoldLoss(svmModel, LossFun, ClassifError); fprintf(交叉验证准确率: %.2f%%\n, cvAccuracy * 100);这里有几个训练细节要注意第一SVM 对特征的尺度敏感。如果特征值范围差异大必须先做归一化。HOG 特征的值一般都在 0~1 之间但也建议做 z-score 归一化% z-score 归一化 mu mean(trainFeatures); sigma std(trainFeatures); sigma(sigma 0) 1; % 防止除零 trainFeatures (trainFeatures - mu) ./ sigma;第二核函数的选择。默认是线性核对于 576 维的 HOG 特征线性核通常已经够用。RBF 核高斯核在特征维度不高、样本量大的时候可能更好但训练时间显著增加而且需要调 C 和 gamma 两个参数。对于课程设计来说线性核 大概率准确率不会让你失望。第三样本均衡性。如果某一类标志的样本数远多于其他类SVM 的超平面会被多数类主导。解决办法是数据增强把少数类图片做水平翻转、小角度旋转、亮度变换把样本量对齐。4. 项目实战构建完整的训练与识别流程4.1 数据集准备与标签管理这个项目的成败一半取决于数据集。如果你用公开数据集推荐两个GTSRB德国交通标志数据集和 TT100K腾讯清华交通标志数据集。GTSRB 有 43 类、5 万多张图片是标准的分类数据集TT100K 是中国特色场景包含中国的标志类型更适合做本土化项目但标注相对复杂。如果自建数据集我强烈建议目录结构按类别组织data/ train/ stop/ # 禁止停车 speed40/ # 限速40 noentry/ # 禁止驶入 ... test/ stop/ ...然后写脚本批量读取% 读取所有训练图片 trainFolder data/train; categories dir(trainFolder); categories categories([categories.isdir] ~startsWith({categories.name}, .)); trainFeatures []; trainLabels []; for i 1:length(categories) categoryName categories(i).name; imageFiles dir(fullfile(trainFolder, categoryName, *.png)); if isempty(imageFiles) imageFiles dir(fullfile(trainFolder, categoryName, *.jpg)); end for j 1:length(imageFiles) img imread(fullfile(trainFolder, categoryName, imageFiles(j).name)); candidate imresize(img, [32, 32]); hog extractHOGFeatures(candidate, CellSize, [8 8]); trainFeatures [trainFeatures; hog]; trainLabels [trainLabels; string(categoryName)]; end end注意全量读入一次性拼接特征矩阵在样本量超过 1 万时可能内存吃紧。我建议先预分配矩阵或者用 tall 数组。更简单的做法是分批提取特征最后再拼接或者直接用 datastoreimds imageDatastore(trainFolder, IncludeSubfolders, true, LabelSource, foldernames);用 imageDatastore 的好处是它不会一次性把所有图片读入内存适合大规模数据。提取特征时可以用 tall 数组或循环遍历避免内存溢出。4.2 训练流程与超参数选择完整的训练脚本结构如下%% 1. 加载和准备数据 imds imageDatastore(data/train, IncludeSubfolders, true, LabelSource, foldernames); imds.ReadFcn (filename) imresize(imread(filename), [32, 32]); %% 2. 划分训练集和验证集 [imdsTrain, imdsVal] splitEachLabel(imds, 0.8, randomized); %% 3. 提取HOG特征 trainFeatures []; trainLabels []; for i 1:numel(imdsTrain.Files) img readimage(imdsTrain, i); trainFeatures [trainFeatures; extractHOGFeatures(img, CellSize, [8 8])]; end trainLabels imdsTrain.Labels; %% 4. 特征归一化 [normalizedFeatures, mu, sigma] zscore(trainFeatures); sigma(sigma 0) 1; %% 5. 训练SVM t templateSVM(KernelFunction, linear, BoxConstraint, 1); svmModel fitcecoc(normalizedFeatures, trainLabels, Learners, t); %% 6. 验证 valFeatures []; valLabels []; for i 1:numel(imdsVal.Files) img readimage(imdsVal, i); valFeatures [valFeatures; extractHOGFeatures(img, CellSize, [8 8])]; end valLabels imdsVal.Labels; valFeatures (valFeatures - mu) ./ sigma; predLabels predict(svmModel, valFeatures); accuracy sum(predLabels valLabels) / numel(valLabels); fprintf(验证集准确率: %.2f%%\n, accuracy * 100);这里出现了一个很重要的坑归一化参数 mu 和 sigma 必须用训练集的统计量来计算不能混在一起计算。否则会有信息泄露验证集准确率虚高。很多同学在这个问题上栽过跟头答辩时一问准确率怎么来的支支吾吾说不清。BoxConstraintC 参数是 SVM 的正则化参数控制误分类的惩罚力度。C 越大对训练集拟合越严格但容易过拟合C 越小泛化能力越强但可能欠拟合。默认值是 1对多数场景都合适。如果你发现训练集准确率高但验证集低大概率是 C 过大尝试降到 0.1反过来都低尝试升到 10。4.3 单张图片识别与可视化识别单张图片的完整流程function result recognizeTrafficSign(imgPath, svmModel, mu, sigma) img imread(imgPath); % 颜色分割定位 hsvImg rgb2hsv(img); hChannel hsvImg(:, :, 1); sChannel hsvImg(:, :, 2); vChannel hsvImg(:, :, 3); redMask (hChannel 0.03 | hChannel 0.95) sChannel 0.3 vChannel 0.2; blueMask (hChannel 0.55 hChannel 0.70) sChannel 0.3 vChannel 0.2; yellowMask (hChannel 0.10 hChannel 0.20) sChannel 0.3 vChannel 0.2; combinedMask redMask | blueMask | yellowMask; combinedMask imclose(combinedMask, strel(disk, 5)); combinedMask imfill(combinedMask, holes); % 连通域分析 cc bwconncomp(combinedMask, 8); stats regionprops(cc, BoundingBox, Area); % 找出面积最大的候选区域 if isempty(stats) result 未检测到交通标志; return; end [~, idx] max([stats.Area]); bbox stats(idx).BoundingBox; % 裁剪候选区域 x1 max(1, floor(bbox(1))); y1 max(1, floor(bbox(2))); x2 min(size(img, 2), floor(bbox(1) bbox(3))); y2 min(size(img, 1), floor(bbox(2) bbox(4))); candidate img(y1:y2, x1:x2, :); candidate imresize(candidate, [32, 32]); % 提取特征并分类 hogFeature extractHOGFeatures(candidate, CellSize, [8 8]); hogFeature (hogFeature - mu) ./ sigma; label predict(svmModel, hogFeature); % 可视化 figure; imshow(img); rectangle(Position, bbox, EdgeColor, g, LineWidth, 3); title(char(label)); result char(label); end这段代码里我用了组合掩码红、蓝、黄三种颜色因为一个场景里可能同时有不同类型的标志取面积最大的一个作为主目标。如果你想识别多个标志就不要只取面积最大的而是遍历所有面积超过阈值的连通域分别做分类。4.4 视频流的实时识别扩展如果想把项目扩展到视频识别核心逻辑不变只是加一个循环videoFile traffic.mp4; v VideoReader(videoFile); outputVideo VideoWriter(output.avi); open(outputVideo); while hasFrame(v) frame readFrame(v); % 调用识别函数 result recognizeTrafficSign(frame, svmModel, mu, sigma); % 在帧上画框和文字 % ... writeVideo(outputVideo, frame); end close(outputVideo);视频识别的难点不是算法而是速度。一帧 1080p 图像从颜色分割到连通域分析再到 HOG 特征提取和 SVM 预测MATLAB 跑起来大约需要 0.5 到 1 秒远达不到实时要求。解决办法有两个一是先把图像缩小到 640×480 再做检测检测速度能提升 3~4 倍二是锁定上一帧检测到的位置在下一帧只在该位置附近的小邻域内搜索跟踪思想大幅减少候选区域数量。这两个技巧对于演示项目已经足够。5. 常见问题排查与性能优化方向5.1 问题速查表我在带学生做这个项目的过程中发现问题和原因高度集中。整理成速查表供参考问题现象可能原因解决方案颜色分割后标志区域大量空洞HSV 阈值设置过严或图像过曝检查 hChannel 的实际值适当扩大 H 范围降低 S/V 阈值检测框太大把背景也框进去形态学闭运算结构元素半径太大或图像中有大面积无关系色块缩小结构元素半径增加面积阈值或用宽高比筛选标志通常是圆形或接近正方形训练集准确率高验证集准确率低过拟合C 值过大降低 BoxConstraint 到 0.1或增加样本量验证集准确率高但实际照片识别差训练集和测试集分布不一致比如训练集是截图测试集是实拍用真实拍摄图像做数据增强增加光照/角度变化红色标志检测不到红色在 HSV 中 H 值接近 0 和 1 的环形交界处阈值写反用 (h 0.03程序报错“矩阵索引超出范围”裁剪时 bbox 超出图像边界用 min/max 限制坐标范围特征矩阵维度不一致图像 resize 后尺寸不一致或提取 HOG 参数不一致打印 size(hogFeature) 检查统一使用相同的 CellSize5.2 现场识别效果差数据增强来救如果你发现模型在测试集上效果不错但拿到真实场景就翻车最常见的根因是训练数据太“干净”了。GTSRB 这类数据集里的图片虽然真实但标志基本都是正对镜头的。而实际应用中摄像头拍到的标志可能是歪的、部分遮挡的、逆光的、有反光的。数据增强就是人为制造“脏”数据。MATLAB 里可以用 imrotate、imnoise、brightness 变换等操作实现% 对每个训练样本生成5个增强版本 augmentedFeatures []; augmentedLabels []; for i 1:size(trainFeatures, 1) img reshape(originalImgs(i, :), [32, 32]); % 原始样本 augmentedFeatures [augmentedFeatures; extractHOGFeatures(img, CellSize, [8 8])]; augmentedLabels [augmentedLabels; originalLabels(i)]; % 小角度旋转 for angle [-10, 10, -20, 20] rotImg imrotate(img, angle, bilinear, crop); augmentedFeatures [augmentedFeatures; extractHOGFeatures(rotImg, CellSize, [8 8])]; augmentedLabels [augmentedLabels; originalLabels(i)]; end % 亮度变化 brightImg imadjust(img, [0.2 0.8], [0 1]); augmentedFeatures [augmentedFeatures; extractHOGFeatures(brightImg, CellSize, [8 8])]; augmentedLabels [augmentedLabels; originalLabels(i)]; end从我的实践经验来看光是把训练集扩充 3~5 倍真实场景的识别率就能从 50% 提升到 80% 以上。这是性价比最高的优化手段比调 SVM 参数管用得多。5.3 从“能用”到“好用”性能优化的三个方向第一个方向是特征融合。HOG 捕捉的是形状信息但交通标志的颜色信息极其重要。“禁止停车”和“禁止驶入”形状相似但颜色圆圈的内部图案不同单靠 HOG 容易混淆。可以把颜色直方图特征拼接到 HOG 特征后面构成一个混合特征向量% 提取HSV颜色直方图 hsvCandidate rgb2hsv(candidate); hHist imhist(hsvCandidate(:, :, 1), 16); % 色调直方图16个bin sHist imhist(hsvCandidate(:, :, 2), 8); % 饱和度直方图8个bin colorFeature [hHist; sHist]; % 拼接HOG和颜色特征 combinedFeature [hogFeature, colorFeature];特征拼接后维度增大训练时间会变长但对颜色的区分能力提升明显。第二个方向是两阶段分类。先用粗分类区分标志的大类禁令/警告/指示再用细分类区分具体标志。粗分类可以只用颜色信息红/黄/蓝准确率极高而且几乎是零成本。这样可以大幅减少细分类中每类的样本数降低训练难度。第三个方向是深度学习迁移。如果你到后期确实想进一步提升准确率可以用 MATLAB 的 GoogLeNet 或 ResNet 做迁移学习。加载预训练模型替换最后一层全连接层输出类别数改为你的标志类别数然后只微调最后几层net googlenet; lgraph layerGraph(net); newFc fullyConnectedLayer(numClasses, Name, new_fc); lgraph replaceLayer(lgraph, loss3-classifier, newFc); lgraph replaceLayer(lgraph, prob, softmaxLayer(Name, softmax)); % 然后设置训练选项并训练迁移学习的优势是特征提取能力极强即使你只有几千张训练图也能达到 95% 以上的准确率。代价是模型更大、推理更慢、显存需求更高在纯 CPU 环境下训练会比较痛苦。5.4 项目答辩与文档写作的小建议如果你是在做课程设计或毕设有几点整理项目的经验值得参考。第一每一步中间结果都截图保存灰度图、均衡化图、颜色分割掩码、连通域框选结果、HOG 可视化这些图放在报告里比任何文字都直观。第二准确率指标要分清训练集、验证集、测试集三个口径不要混着说。第三代码注释要写“为什么”而不是“是什么”。比如上面那行sigma(sigma 0) 1注释写成“防止特征值全零时除零报错”比你写“计算标准差”有用得多。6. 几个用过之后才知道的细节经验最后分享几条零散但实在的经验。第一提取 HOG 特征时extractHOGFeatures默认返回的特征维度比你根据 CellSize 计算的可能多。原因前面提过默认 BlockOverlap 是 0.5block 密度变大。所以不要手工硬编码特征维度用size(hogFeature, 2)去动态获取。第二训练 SVM 时如果类别数特别多比如 GTSRB 的 43 类fitcecoc训练的模型文件可能非常大甚至超过 100MB。如果项目部署有体积限制可以考虑把特征维度降低比如增大 CellSize 到 16×16或者改用更紧凑的分类器比如决策树或 kNN。kNN 在 32×32 HOG 特征上也不慢模型还特别小。第三颜色分割时sChannel 0.3这个阈值在实际场景中经常需要动态调整。阴天拍摄的图片饱和度普遍偏低0.3 可能把目标全部滤掉。一个技巧是先对图像的饱和度做直方图统计根据峰值位置决定阈值实现一定程度的光照自适应。第四标志识别完成后输出类别名最好加上图形示意。在 MATLAB 里可以用text函数在图像上绘制文字也可以用insertObjectAnnotation函数把识别结果和置信度一起标出来。这个交互效果在答辩演示时的观感提升非常明显。第五如果遇到 MATLAB 安装在虚拟机上运行卡顿的情况优先检查是否开启了硬件加速。在 MATLAB 首选项里把 Graphics 渲染方式从 OpenGL 改成 hardware或者反过来有时候能解决莫名其妙的卡顿和显示问题。这个项目做到最后你会发现它真正的难点不是某个算法而是把一堆零散的模块拼成一个稳定的系统。这种“工程整合能力”恰恰是书本上不教、工作中最看重的东西。希望这份分享能让你少走一些弯路也欢迎在实际复现过程中遇到问题时再来交流。本文还有配套的精品资源点击获取
返回列表