简介:面向计算机视觉初学者与行人检测研究者,这份MATLAB工程实现了基于支持向量机与梯度直方图的行人识别完整流程,涵盖特征提取、分类器训练、多尺度滑动窗口检测以及重心滤除、重叠面积去重等后处理环节。压缩包内共含2429个文件,其中以样本图像为主,包括1482张JPG和933张PNG图片,另有10个M源码文件、mat模型、TXT说明与PPTX讲稿,便于对照代码与演示理解算法细节。目前已有2828人学习下载,资源包大小约35.35MB。资料中保留了多组测试图片和过程截图,直观展示了负例训练不足引发的误判以及去重优化后的效果。读者可借助fitcsvm训练脚本、自定义的HOG特征计算函数以及参数优化模块,快速复现检测结果,并进一步掌握交叉验证、网格搜索等调参思路。无论是课程设计、毕业设计,还是智能交通、视频监控中的行人检测实践,这份资料都能提供可直接运行的基线方案和完整的算法学习路径。
1. 从课程设计到边缘设备部署:SVM+HOG行人识别算法的matlab实现到底在解决什么问题
如果你搜到这篇,多半是正在做 matlab 图像处理大作业、毕设里的目标检测章节,或者被导师一句话“你把这个行人识别复现一下”推进了坑里。SVM+HOG行人识别算法的matlab实现,不是什么新潮东西,它是 2005 年 Dalal 和 Triggs 在 CVPR 上提出的经典方案,在没有深度学习之前,这几乎是行人检测的事实标准。它做的事情很简单:把图像分成小格子,统计每个格子的梯度方向直方图,得到一串特征向量,再用支持向量机判断这串特征像不像人。它的优势在于训练快、部署轻、CPU 上能实时跑,到今天仍在安防、交通、工业场景里被用作弱算力端的兜底方案——很多无人车项目在给高算力板卡调模型之前,用的就是这套逻辑打底。
这篇我会按“理论先立住、再动手能复现”的顺序,从 HOG 特征怎么算、SVM 为什么选线性核,到数据集准备、MATLAB 代码实现、滑窗与 NMS,最后重点讲我复现时踩过的几个坑。你有 MATLAB 基础、哪怕只是上课学过一点,跟着抄就能跑通。如果你是想快速搞定一个能演示的检测 demo,我会给出一个保守但可靠的路径;如果你是打算认真把这个方案做进项目甚至写论文,参数和坑的部分对你的帮助会更大。
2. HOG特征和线性SVM:先搞清楚“提取什么”和“怎么决策”
2.1 HOG特征到底在描述图像的什么
HOG(Histogram of Oriented Gradients,方向梯度直方图)的核心思想是:目标的局部外观可以通过梯度方向分布来描述,不需要知道像素的具体颜色和纹理。行人这种刚性弱、姿态多的目标,表面纹理变化大,但边缘轮廓的梯度方向分布是有统计规律的——站姿的人,躯干和腿的梯度方向主要是垂直的,而背景里的树木、墙面,梯度方向要杂乱得多。
具体的计算流程分为四步。第一步灰度化,把 RGB 转为单通道灰度图。第二步计算梯度,分别求水平方向 Gx 和垂直方向 Gy,这一步的卷积核习惯上用 [-1, 0, 1] 和它的转置,而不是 Sobel 核,因为 Dalal 当年的实验证明这个简单核效果反而更好。第三步是统计方向直方图,把图像划分成若干 cell(细胞单元,常见 8×8 像素),每个 cell 内部所有像素的梯度幅值按方向加权投票,方向区间一般是 9 个 bin,即 0~180 度分成 9 等份,注意是无符号方向,因为梯度的正负在边缘表达上是冗余的。第四步是块归一化,把相邻的 2×2 个 cell 组成一个 block,对 block 内的特征做 L2 归一化,消除光照和对比度变化的影响。
这套逻辑说起来简单,但直接影响后面 SVM 训练效果的关键点在于参数选型。cell 大小决定特征的分辨率,8×8 是行人检测的经典配置;block 重叠步长决定特征的连续性,常见是 1 个 cell 作为步长滑动,这样相邻 block 有重叠,特征更平滑。我见过不少人把 cell 设成 32×32 想提速,结果检测率急剧下降,就是因为空间分辨率不够,小肢体部分的梯度被平均掉了。还有一点容易被忽略:图像需要先缩放到统一尺寸,INRIA 行人数据集的标准裁剪尺寸是 64×128,这是 HOG 行人检测最常用的默认值。
2.2 线性SVM为什么够用:从 Hard Margin 到 Soft Margin
HOG 特征只是把图像变成了向量,真正做分类的是 SVM。SVM 的目标是找一个超平面,让正负样本到这个超平面的最小距离最大化。对于行人检测这个二分类问题,理论上特征维度很高、样本量动辄上万,用非线性核(比如 RBF 核)会导致训练极慢且容易过拟合;而线性 SVM 在高维特征下已经有很强的表达能力,因为 HOG 本身就是高维描述子,在 64×128 的图像上提取出来的特征维度是 3780 维,线性超平面在这个空间里已经能找到足够好的分界。
MATLAB 里实现线性 SVM 有两条路:老版本用svmtrain,R2015a 之后官方建议用fitcsvm。两者的核心参数是BoxConstraint(即 C 值),它控制“误分类惩罚”和“模型泛化”之间的平衡。C 值设得大,训练集上的错误率低,但容易过拟合,检测阶段对姿态变化大的行人会很敏感;C 值设得小,模型宽容度高,泛化好,但可能漏检。我一般习惯初始化 C=1,然后基于验证集的误报率做一次调参,先粗调一个量级,再精调。实际上,OpenCV 里默认给的行人检测 SVM C 值就是 0.01 左右,这个值偏低,但配合高维 HOG 特征反而在真实场景里表现更稳,值得参考。
2.3 算法整体流程与特征维度的估算
整体流程可以拆成训练阶段和检测阶段。训练阶段拿到的是成对的图片和标签:把行人样本和负样本(背景、非行人)缩放到统一尺寸,提取 HOG 特征,组成特征矩阵 X 和标签向量 y,交给 SVM 训练;训练完得到一个模型,然后进入检测阶段。检测阶段拿到的是任意大小的图像,用一个固定尺寸的滑窗遍历整张图,每个位置截取一块送入特征提取和 SVM 预测,得出这块区域是不是行人;最后把重叠的多个检测框用 NMS(非极大值抑制)合并。
这里有一个很关键的维度估算:64×128 的图像,每个 block 是 16×16,水平和垂直方向各有多少个 block?水平方向 block 数是 (64/8 - 1) = 7,垂直方向是 (128/8 - 1) = 15,总共 105 个 block;每个 block 有 2×2×9=36 维特征,105×36 = 3780 维。这就是“3780 维 HOG 特征”这个数的由来,在论文和工具里经常直接出现。搞清楚了它,你才能理解为什么最终训练时间可控、模型文件很小——本质上就是 3780 维空间里的线性分类器,权重向量也只有 3780 个浮点数加一个偏置。
3. 准备训练数据:公开数据集、自建样本与负样本挖掘的取舍
3.1 公开数据集怎么选:INRIA 与 Daimler 的差异
要复现行人识别,第一步是拿到数据。最经典的是 INRIA Person Dataset,这是 Dalal 当年论文里用的数据集,包含 614 张训练正样本图片(裁剪对齐的行人)和 1218 张负样本图片(无人物的街景),测试集是 288 张带标注的完整图片。它的特点是标注框比较紧,基本贴合人体轮廓,并且场景以街道、公园为主,和常见的安防场景接近。
另一个可用的是 Daimler 行人数据集,主要从车载摄像头采集,分辨率偏低,正样本是 18×36 或类似的小尺寸,更贴近远距离检测场景。如果你做的课题是车载场景,用这个数据集更合适;如果是通用安防或者课程演示,INRIA 会比较友好。实际上很多课程设计是不具备下载条件的,MATLAB 自带了一些样本图片,但数量远远不够训练一个像样的分类器,这里我会在下面仔细说自建与增强的路线。
我自己的经验是:不要一上来就用全量数据训练。先取 INRIA 的训练正样本和少量负样本训练一版,跑通整个流程,确认代码无误后再扩数据。因为首次跑会遇到很多特征提取和数据结构的问题,全量数据只会让调试更慢。调通之后,再逐渐增加负样本,你会看到误报数量有明显的下降——这个过程本质上就是负样本难例挖掘。
3.2 自建数据集的采集与标注操作路径
如果你没法访问外网数据集,或者课题要求针对特定场景(比如校园、工厂、仓库),自建数据集很正常。常见做法是自己拍或者从监控视频里抽帧,利用 MATLAB 的TrainingImageLabeler或 Image Labeler 这个交互式标注工具框出行人框。需要注意的是,标注框的纵横比尽量接近 1:2,因为最终训练样本要 resize 到 64×128,如果原始框长宽比差异太大,resize 会引入严重的形变,导致特征失真。
采集的时候有几个硬建议。正样本要覆盖不同光照、拍摄角度、穿着颜色;市中心主干道、黄昏逆光、雨伞撑开的情况,能拍尽量拍。负样本不要只选“完全没人”的空场景,要包含“有行人但有遮挡”“有人形物体(电线杆、树干、广告牌上的剪影)”这种难负样本,否则检测阶段会出现大量误报。这一条是后面避坑章节的核心素材,你如果只拿纯街道做负样本,训练出来的模型大概率会把电线杆认成人。
标注完导出时,用export到工作区或者写入 PASCAL VOC 格式的 XML。如果你用的是 Image Labeler,导出的 ground truth 是table类型,第 1 列是imageFilename,第 2 列是label对应的cell数组,每行是多个[x, y, width, height]的矩阵。这个结构后面做训练数据加载时非常关键——因为不同图片包含的目标数不同,所以 label 列不能是普通列向量,而必须是 cell 数组。
3.3 数据增强:只用 8 个技巧也够支撑一个能用的模型
数据增强是老生常谈,但 HOG 特征对手工增强的敏感度和深度学习不一样。HOG 对颜色不敏感,所以做颜色抖动意义不大;平移和缩放有实际帮助,因为滑窗检测时参考框和训练框不可能完全重合。我一般只做三种增强:水平翻转,这是行人检测最有效也最安全的增强方式,因为行人左右对称;轻微缩放,0.9 到 1.1 倍之间随机缩放后重采样;小幅平移,上下左右平移 1~2 个像素。
有一种增强我明确不建议做:垂直翻转。行人和倒立的人形在 HOG 特征空间里是完全不同的分布,垂直翻转会让模型学习到错误的梯度方向统计。写论文的时候你可能会想“为了扩充样本做 8 倍增强”,但实际效果可能适得其反。还有一点是增强后的样本不要全部丢进训练集,保留一部分原始样本在验证集里,看看增强是否真的改善了泛化——我在教同事做这个流程时发现,很多增强操作做完验证集指标反而不涨,原因往往是增强参数太激进,引入了不合理的形变。
4. 用MATLAB实现HOG特征提取、SVM训练与滑窗检测的完整流程
4.1 提取HOG特征:用 extractHOGFeatures 还是手动实现
MATLAB 自 2015a 起有了extractHOGFeatures函数,封装了底层实现,接收图像输入,返回特征向量和可视化对象。如果你用的版本有它,直接调用即可,比自己写卷积核再统计直方图要稳定得多,而且性能经过了优化。
% 读取图像并转为灰度 img = imread('pedestrian.jpg'); if size(img, 3) == 3 img = rgb2gray(img); end % 统一裁剪到 64x128,保持 HOG 参数一致性 img_resized = imresize(img, [128, 64]); % 提取 HOG 特征 [hogFeature, visualization] = extractHOGFeatures(img_resized, ... 'CellSize', [8 8], ... 'BlockSize', [2 2], ... 'NumBins', 9, ... 'UseSignedOrientation', false); % 查看特征维度 fprintf('特征维度: %d\n', length(hogFeature)); % 可视化:查看 HOG 描述子的形状是否像一个人形 figure; subplot(1,2,1); imshow(img_resized); title('原图'); subplot(1,2,2); plot(visualization); title('HOG 可视化');CellSize是 [8 8] 时特征最细粒度,BlockSize[2 2] 表示一个 block 含 2×2 个 cell,NumBins取 9 会得到 3780 维特征。UseSignedOrientation设为 false 表示用 0~180 度的无符号方向,这是 Dalal 实验得出的最优选择,设成 true 反而会降低识别率。可视化输出visualization是一个 vector field 对象,画出来可以看到人体的轮廓:头肩部有明显的梯度集中区域,腿部的梯度方向周期性变化——如果特征图看起来一团乱,多半是图片尺寸或者预处理出了问题。
这段代码作为特征提取的模板函数,在后续训练和检测中都会被反复调用。单独封装成函数是明智的,例如getHOGFeature(im),这样训练循环对每张图调用,检测时对每个滑窗调用。代码层面抽象到位了,后面调参才高效。
4.2 训练SVM模型:fitcsvm 与参数设置
特征提取封装好之后,进入训练环节。常见做法是把所有正负样本的 HOG 特征拼成一个大矩阵,每行一个样本,标签用 +1 和 -1。注意:SVM 的标签在 MATLAB 里必须是数值或 categorical 类型,用字符串的话fitcsvm会报错。
% 假设已经读取了一批正负样本,这里演示加载流程 posFiles = dir(fullfile('data', 'pos', '*.jpg')); negFiles = dir(fullfile('data', 'neg', '*.jpg')); numPos = length(posFiles); numNeg = length(negFiles); % 预分配特征矩阵:所有样本都将 resize 到 64x128 featureDim = 3780; X = zeros(numPos + numNeg, featureDim); y = [ones(numPos, 1); -ones(numNeg, 1)]; for i = 1 : numPos img = imread(fullfile('data', 'pos', posFiles(i).name)); if size(img, 3) == 3, img = rgb2gray(img); end img = imresize(img, [128, 64]); X(i, :) = extractHOGFeatures(img, 'CellSize', [8 8], ... 'BlockSize', [2 2], 'NumBins', 9); end for i = 1 : numNeg img = imread(fullfile('data', 'neg', negFiles(i).name)); if size(img, 3) == 3, img = rgb2gray(img); end img = imresize(img, [128, 64]); X(numPos + i, :) = extractHOGFeatures(img, 'CellSize', [8 8], ... 'BlockSize', [2 2], 'NumBins', 9); end % 训练 SVM 模型,使用线性核 SVMModel = fitcsvm(X, y, ... 'KernelFunction', 'linear', ... 'BoxConstraint', 0.01, ... 'Standardize', false, ... 'ClassNames', [1; -1]); % 保存模型供检测阶段加载 save('svmModel.mat', 'SVMModel');BoxConstraint如上文所说,这里建议从 0.01 起步接近 OpenCV 的默认值,不要用默认的 1。Standardize设为 false,原因很直接:HOG 特征本身已经是梯度统计值,各个维度的数值范围接近,归一化反而可能抹掉不同维度之间应有的权重差异。ClassNames明确指定正负类的标签顺序,后面调用predict时才能确定返回的分数代表哪一类。
训练完成后你会看到一个有意思的现象:支持向量的数量通常不多,几百到上千不等,模型文件就是一个结构体。这也意味着这个方案部署到任何设备上,本质上就是加载一组权重做一次矩阵乘法,这也是它至今在嵌入式行人检测方案里没有被淘汰的原因。如果你的训练集很小(比如只有 50 张正样本),训练几乎秒完成,但模型基本不可用——这不是代码的问题,是数据量的问题。
4.3 滑窗检测与 NMS:把模型放到大图上找行人
模型有了,检测阶段要在大图上滑动窗口。这一步最容易出现性能问题,因为窗口数很多。滑窗分两个尺度:固定窗口缩放图像(图像金字塔),或者固定图像缩放窗口(多尺度窗口),前者是更常见的做法。用imresize按比例缩小原图,对每个尺度用 64×128 的窗口逐行逐列扫描,每移一次提取 HOG 特征、送入predict得到分数。
function boxes = detectPedestrians(img, SVMModel, stepSize, scaleStep, threshold) % 输入:原图、SVM模型、滑窗步长、缩放步长、判定阈值 % 输出:检测框 [x, y, width, height; ...] if size(img, 3) == 3, img = rgb2gray(img); end boxes = []; scale = 1; % 图像金字塔循环 while size(img, 1) / scale >= 128 && size(img, 2) / scale >= 64 resizedImg = imresize(img, 1 / scale); [rows, cols] = size(resizedImg); % 滑窗 for y = 1 : stepSize : rows - 128 + 1 for x = 1 : stepSize : cols - 64 + 1 window = resizedImg(y : y + 127, x : x + 63); hog = extractHOGFeatures(window, 'CellSize', [8 8], ... 'BlockSize', [2 2], 'NumBins', 9); [label, score] = predict(SVMModel, hog); % 对线性 SVM,score 是到超平面的距离,这里用 Score 中正类分数 if label == 1 && score(2) >= threshold % 把检测框映射回原图坐标系 boxes = [boxes; [x*scale, y*scale, 64*scale, 128*scale]]; end end end scale = scale * scaleStep; % 例如 1.05 倍递增 end % NMS 合并重叠框 boxes = nonMaxSuppression(boxes, 0.4); endscore的解读是这里最大的坑。predict对每个样本返回一个两列的分数矩阵,每列对应一个类别。对线性 SVM,这个分数本质是w·x + b的值,值越大表示越靠近正类一侧。因此判定条件不能只看label,要结合正类的分数阈值设置,通常设成 0 或 -0.5 附近。设成 0 表示要求样本严格落在超平面正侧,设成 -0.5 会更宽松一些,能找回一些被遮挡的行人,但也会增加误报。这个值就是你的“灵敏度旋钮”。
nonMaxSuppression对应 MATLAB 里没有直接提供,常见做法是自己写几十行代码。NMS 的思路是:把置信度最高的框选出来,删掉所有和它 IoU 超过阈值的框,然后重复。IoU 阈值取 0.4~0.5 比较常规,设得太高(比如 0.8)会导致重叠框大量保留,最终输出多个框套在同一个行人身上;设得太低会导致紧密站在一起的两个人只保留一个框。NMS 写错是检测效果看起来“还能看”但实际漏检很高的重要原因,后面我会给一段可以直接用的参考实现。
4.4 用 trainCascadeObjectDetector 代替手写训练流程的场合
写完上面的流程后,新手可能会遇到一个问题:fitcsvm训练出来的是一个分类器,但 MATLAB 的 Computer Vision Toolbox 里还有一个更贴合“开箱即用”的接口——trainCascadeObjectDetector。它内部用的是 HOG + 级联分类器(基于 Viola-Jones 框架的变体),训练接口比手写 SVM 更简单,输出的 detector 对象直接支持detect方法。如果你只是为了交一个作业或者做一个快速 demo,用它是性价比最高的。
% 训练级联检测器,正样本是标注表,负样本是图片列表 trainCascadeObjectDetector('pedestrianDetector.xml', ... positiveInstances, ... fullfile('data', 'neg'), ... 'FalseAlarmRate', 0.2, ... 'NumCascadeStages', 5, ... 'FeatureType', 'HOG'); % 检测 detector = vision.CascadeObjectDetector('pedestrianDetector.xml'); bboxes = step(detector, img);但它和我们前面手写的 SVM 方案有一个本质区别:级联检测器是多级分类器的串联,每级负责快速排除大量非目标区域,最后一级才做精细判断,速度和精度取向与单线性 SVM 不一样。FalseAlarmRate控制每级允许的误报率,设得越小,级联数需要越多,训练越慢,但最终误报越少。NumCascadeStages设 5 到 8 是常见配置。如果你是在做毕设中“算法实现”类的课题,手写 SVM 流程能展示你对原理的理解,得分通常更高;如果你是给项目做原型,级联方案更快。这两个方向的代码我都改过,各有各的坑:手写 SVM 的坑是数据维度容易出错,级联的坑是trainCascadeObjectDetector要求正样本尺寸一致且长宽比固定,训练中报内存错误很常见。
5. 训练与检测中的6个高频坑:现象、原因与解决
5.1 检测框集中在人体边缘内侧,而不是完整包住人
现象:检测出的框总是比实际人略小一圈,集中在人形内部,四肢部分会“切掉”一块。原因:训练正样本的裁剪框不统一,有的紧贴人体,有的留了背景边,SVM 学到的边界不完全是人体轮廓。解决:检查正样本的裁剪,确保人体占框高度的 70% 以上,头顶留 5% 左右余量,脚底留 8% 左右,两侧尽量贴紧肩宽。INRIA 数据本身的标注框就是这种风格,如果你发现自己的框普遍偏小,把训练样本重新裁剪一遍,而不是调检测阈值。
5.2 全图检测时误报很多,但训练集上准确率 100%
现象:训练集上几乎零错误,放到整张街景图上框出来一堆不是人的目标。原因:这是过拟合后又在极端不平衡的负样本上测试的典型表现。如果负样本只包含空草地、纯墙面,模型没有见过“长得像人的树桩”“广告牌人形剪影”这类难负样本,自然会把它们当人。解决:做难例挖掘——把当前模型在负样本(或者验证集)上检测出来的所有误报框,裁剪出来加入负样本集,重新训练。这个操作重复两轮,误报率通常能下降一个数量级。这也是 Dalal 论文里明确提到的训练策略,属于经典操作,不是进阶技巧。实施时把阈值的灵敏度调高,让模型“多误报一些”,这样能采到更多难负样本。
5.3 滑窗步长设太大,小个子行人被跳过
现象:近距离大尺度的行人都能检测到,远处的小目标几乎全漏。原因:滑窗步长stepSize设得太大,比如 16 或 32 像素。滑窗步长大小直接决定了搜索密度,步长为 8 时窗口覆盖密度是步长为 16 的 4 倍(两个方向都是原来的一半)。远处行人在图像上只有 60×30 像素左右,按 64×128 窗口等比缩放后,对应的原图窗口位置是离散的,步长大了就会落在大目标之间的缝隙里。解决:把步长降到 4~8。代价是检测时间显著增加,所以要和图像金字塔的缩放步长配合调整。图像金字塔的缩放比例取 1.05 或者 1.1,越小越密但耗时越高;如果追求速度优先,取 1.2 并缩小滑窗步长到 4,这样能找到相对好的平衡点。
5.4 HOG特征提取时报维度不匹配
现象:报错 “Error using extractHOGFeatures ... Expected input number 1, I, to be 2-D”。原因:传入了彩色图像或者二值图像,函数要求灰度图或彩色图都可以,但二值图不被接受。还有另一种情况:图像尺寸小于选定的 cell 尺寸乘以 block 尺寸,比如用 8×8 的 cell、2×2 的 block,window 尺寸是 16×16,如果再乘一个尺度缩放值,窗口可能比 16×16 还小。解决:在提取特征前检查size(img),如果小于 32×32 跳过该窗口。我的习惯是在滑窗循环里加一个判断:if rows < 16 || cols < 16, continue; end。这也是图像金字塔循环最小尺寸限制的直接原因。
5.5 检测速度太慢:一张 720p 的图要跑好几秒
现象:检测一张 1280×720 的图需要 3 秒以上。原因有两个:滑窗步长太小,金字塔层数太多。1280×720 的图,步长 8 加上 1.05 的金字塔缩放,大约有 8000 个窗口,每个窗口提取特征再预测一次,累计时间自然高。解决思路有明确优先级:先确认是否真的需要多尺度——如果你的业务场景里行人高度基本在 100 像素以上,可以只检测原图尺度,窗口数立刻降到一两千个,耗时减半;如果必须多尺度,金字塔缩放比从 1.05 改成 1.1,层数减少三分之一,检测率损失不大;最后一道加速手段是把滑窗步长从 8 调成 12,这一步需要结合 NMS 阈值来平衡,因为稀疏窗口会导致同一个目标只被扫到一次,NMS 找不到可合并的框。
5.6 训练时内存溢出:特征矩阵太大
现象:使用全量 INRIA 训练集时,MATLAB 报内存不足。原因:正样本 1200 张、负样本 12000 张的规模下,特征矩阵是 13200×3780 的双精度矩阵,约 400MB 内存,加上特征提取过程的中间变量,很容易撑爆默认内存设置。解决:从行数上做文章,把负样本分批处理。第一次只用 2000 张负样本(随机采样),训练之后做难例挖掘,每次往负样本池里加入 500 个难例,保持负样本总量在 5000 以内。这种做法不只是解决内存问题——它对模型精度的贡献也大于一次性上全量数据。另一个技巧是把特征矩阵转成single类型,内存直接减半,fitcsvm对single是完全支持的。
6. 让模型真正“可用”:验证曲线、难例挖掘与部署前检查清单
调试到检测框基本能正确框住行人之后,距离“能交差”还有一段路。最重要的不是多看几张效果图,而是做一次定量的验证。找一批没有参与训练的行人图片(哪怕三五张),统计漏检率和误报率:把所有检测框和人工标注框做 IoU 计算,IoU 大于 0.5 的算正确检测,小于 0.5 的算定位偏差,被漏掉的目标算漏检。这个统计至少能告诉你模型是“偏激进”(误报多漏检少)还是“偏保守”(漏检多误报少),从而决定阈值往哪个方向调。进阶一点的做法是画 Precision-Recall 曲线,MATLAB 里用perfcurve函数传入标签和分数,就能得到曲线;曲线面积越大越好,而对行人检测来说,要重点关注低误报区间的查全率——毕竟安防场景里误报可以容忍,漏检才是真事故。
第二个值得做的操作是把模型用到一段连续视频上,观察误报的分布规律。我做的实际项目里出现过这样一个情况:静态图片验证表现良好的模型,在监控视频里对自行车后座的一袋杂物稳定误报。原因很简单,静态验证集的负样本里这种形似人形的分体目标太少。把这段视频里出现误报的帧全部抓出来,裁剪误报区域加入负样本集,做一轮再训练,这类稳定误报基本能被清掉。这在工程上叫“场景特化”,是一个比调参更能提升实际可用性的手段。
最后一个建议是部署前在低算力设备上做一次真实耗时测试。用tic/toc包住检测主循环,分别记录不同分辨率、不同金字塔层数下的耗时和帧率,然后画一张表贴在项目报告里。这个数据比任何效果图都更有说服力。我习惯把滑窗步长和金字塔缩放比的几组组合全部跑一遍,选一组满足“单帧 150ms 以内”的配置作为最终参数——因为实际部署时 CPU 占用率不可能一直是 100%,至少要留 30% 的余量。
这套方案做下来,最大的教训就是不要迷信花哨的调参,真正决定效果的是数据质量和滑窗策略。希望帮到你,祝你的行人检测一次跑通。
本文还有配套的精品资源,点击获取