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

资讯详情

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

基于Matlab的人体异常行为检测:背景建模与SVM分类实战

基于Matlab的人体异常行为检测:背景建模与SVM分类实战 简介一套基于Matlab的人体异常行为检测系统源代码聚焦主动监控场景如农村空巢老人看护中对跌倒、快跑、慢跑等行为的自动识别与预警相比传统被动监控只能事后回放它可在检测到异常时及时给出提示实现从“查看”到“预警”的转变。项目包含完整Matlab源码、带交互界面的GUI文件fig、3段行为测试视频avi、大量标注样本图jpg/png并附有程序导入说明doc/md/txt方便按文档步骤复现与二次开发。压缩包共756个文件容量8.82MB以741张图像样本为主体另有6个m脚本、3个avi视频和fig界面文件目录结构清晰便于定位主程序与测试数据。资源内大量的jpg图片可作为行为识别的训练或测试样本用于理解不同姿态与动作的特征差异。已有349人学习下载对想研究运动特征、视觉监控或GUI封装流程的读者来说这套代码从视频读取、目标分析到界面反馈构成了完整工程稍加改造即可用于跌倒预警等原型演示。1. 人体异常行为检测系统用 Matlab 搭比 Python 更快的验证路径养老监护摄像头拍到老人跌倒在床边系统在两秒内上报这是人体异常行为检测最典型的应用场景。Matlab 在这个方向上的优势不在算法本身而是 Computer Vision Toolbox 把背景建模、光流估计、特征提取和 SVM 训练封装成了可组合的函数几百行脚本就能从一段视频跑到行为判定结果对验证算法假设和阅读源码都足够高效。这套方案适合想做跌倒检测算法验证的工程师、需要出实验数据的在读研究生也适合医疗监护产品预研阶段做快速原型。用 Matlab 搭这个系统核心是五个环节的衔接前景提取、特征设计、判定逻辑、分类器训练、结果评估下面按这条链逐层展开。2. 数据准备与人体前景提取从视频帧到干净的二值掩码人体异常行为检测的第一层不是行为识别而是把人从背景里分离出来。摄像头静止的场景下背景建模是最常见的选择它比 Mask R-CNN 这类深度学习分割轻一个量级不需要标注数据Matlab 里的 vision.ForegroundDetector 一个对象就能完成混合高斯背景建模这也是这类系统源码里出现频率最高的预处理模块。2.1 视频读取与背景建模的三种方案取舍先想清楚用什么方式从视频帧得到前景掩码。帧差法是最简单的做法相邻两帧做差再取阈值对光照变化不敏感但运动缓慢或静止时前景会整块丢失。跌倒后的躺地静止恰恰是检测系统最需要保留的状态所以帧差法在跌倒检测里只能做辅助不能做主背景模型。混合高斯背景建模是监控场景的主流方案。Matlab 用 vision.ForegroundDetector 封装了标准实现对多模态背景树叶晃动、屏幕闪烁有建模能力前 N 帧作为训练期之后逐帧输出前景掩码。它的问题是训练期内画面里有人走动会污染背景模型采集数据时尽量让镜头空场 3 到 5 秒。深度学习语义分割精度最高但依赖预训练权重和 GPUMatlab 里可以用 deepLabv3plusResnet18 这类现成网络不过在普通办公电脑上处理 1080p 视频很难达到实时。它适合离线评估不适合做实时检测原型。背景建模方案抗光照变化运算耗时静止目标保留适用阶段帧差法较强最低差静止即消失辅助判定、运动检测预筛选混合高斯中等低CPU 实时好持续输出完整前景实时检测主背景模型深度学习分割强高需 GPU最好离线评估、精度对照实验混合高斯方案的主体循环代码不长下面是可以直接套用的骨架接口在 R2020b 到 R2023b 之间没有变化% 创建 VideoReader 读取测试视频 vid VideoReader(fall_case01.mp4); % 混合高斯前景检测器3 个高斯分量训练 50 帧初始方差 0.05 fg vision.ForegroundDetector( ... NumGaussians, 3, ... NumTrainingFrames, 50, ... InitialVariance, 0.05, ... MinimumBackgroundRatio, 0.7); frameIdx 0; while hasFrame(vid) frame im2double(vid.readFrame()); frameIdx frameIdx 1; if frameIdx 50 % 前 50 帧只做背景建模不输出检测结果 step(fg, frame); continue; end fgMask step(fg, frame); % 输出二值前景掩码 % 后续形态学清洗与人体框提取在 2.2 展开 end这里每个参数都值得按场景调一下。NumGaussians 控制背景模型的复杂程度室内固定机位取 3 就够室外有树影晃动取到 5 更好但计算量会跟着涨。NumTrainingFrames 决定背景收敛速度取 30 到 50 之间比较常见帧数太少背景不稳定帧数太大启动等待太长。InitialVariance 表示像素值方差初值0.05 适合 0 到 1 归一化后的图像如果代码里读进来的是 uint80 到 255就得改到 400 左右这个量级。MinimumBackgroundRatio 是像素被判定为背景的概率下限灯光缓慢变化的环境可以降到 0.6让背景更新更快。注意ForegroundDetector 的 step 接口每次调用都会用当前帧更新背景模型训练期不能跳过输入帧要一帧不落。这类系统源码里最常见的误用是只喂了前 3 帧就认为模型造好了实际上一开始输出的掩码全是伪前景后面所有特征全被污染。2.2 前景掩码的形态学清洗与人体候选框提取背景建模直接输出的掩码噪声很大。室内场景有灯光反射、窗帘摆动室外有树影这些区域面积小但会进入后续的特征计算必须先做形态学清洗再接连通域分析这一步决定了宽高比特征是否可靠。% 形态学闭运算先填补人体内部的空洞 se strel(disk, 5); mask imclose(fgMask, se); % 开运算去掉孤立噪点保留人体主体 mask imopen(mask, se); % 面积小于 200 像素的连通域直接删除 mask bwareaopen(mask, 200); % 提取连通域属性用于筛选人体候选框 stats regionprops(mask, BoundingBox, Area, Centroid); boxes reshape([stats.BoundingBox], 4, []); areas [stats.Area]; centroids reshape([stats.Centroid], 2, []); % 面积筛选去掉过小噪点和占满画面的异常前景 validIdx areas 500 areas 0.5 * size(mask, 1) * size(mask, 2); boxes boxes(validIdx, :); centroids centroids(validIdx, :);闭运算用的 strel(disk, 5) 意思是半径 5 像素的圆形结构元素先膨胀再腐蚀人体边缘中断的部分会被连起来。开运算反过来先腐蚀再膨胀直径小于 10 像素的噪点被消掉。bwareaopen 更直接小于阈值的连通域整块删除200 这个经验值对 640×480 分辨率合适高清视频要按分辨率等比放大到 800 到 1000 左右。regionprops 拿到的是图像的标准几何属性。BoundingBox 是 [x, y, width, height] 形式的向量Centroid 是质心坐标 [x, y]后面算宽高比、质心速度全靠这两个字段。筛选阶段的 validIdx 里500 的下限过滤小噪点0.5 倍画面上限过滤背景建模失误导致的整块前景。如果一帧里出现多个候选框就取面积最大的作为目标人体。写法是按 areas 降序排序后取第一个框这在单人跌倒检测场景下成立多人场景需要接跟踪器给每个框分配 ID。把这一步和 ForegroundDetector 组合起来看它其实是整个 Matlab 图像处理栈里性价比最高的链路不需要标注数据、不需要训练、CPU 就能跑实时这也是为什么这类源码系统里出现混合高斯而不是神经网络属于合理设计。3. 异常行为特征提取宽高比、质心速度与光流方向前景框拿到后判定跌倒不是一个布尔量要用一组特征表达人从站立变为躺卧的动态过程。第 2 章得到的是单帧几何属性本章分别从包围盒时空变化、光流方向两个维度构造特征向量滑窗聚合后交给判定层。3.1 人体包围盒的时空特征定义跌倒过程在视频上的表现非常直观人体的宽高比从高瘦突变为矮胖质心高度快速下降随后进入一段低质心静止状态。这三个变化是跌倒检测里最稳定的三个信号阈值法和机器学习分类器的输入都从这里来。按上一章的候选框数据往下接% 按面积降序排序取最大连通域作为目标人体的框 [~, sortIdx] sort(areas, descend); box boxes(sortIdx(1), :); centroid centroids(sortIdx(1), :); % 宽高比height / width直立时大于 1跌倒时小于 1 height box(4); width box(3); aspectRatio height / max(width, eps); % 质心高度归一化用质心 y 坐标除以画面高度 imgH size(frame, 1); centroidNormY centroid(2) / imgH; % 有效面积比人体像素占整个前景框的比例 fillRatio areas(sortIdx(1)) / (height * width eps);width 按 max(width, eps) 处理是为了防止检测框退化成一个像素时出现除零。centroidNormY 用画面高度归一化的作用是消除机位高度和分辨率的影响让同一条规则在不同摄像头下可迁移。fillRatio 帮的是区分跌倒躺地和弯腰捡东西后者宽高比也会下降但人体框里肢体占的面积比例远达不到躺平时的水平。特征计算方式正常行走跌倒瞬间倒地后宽高比框高 / 框宽2.0 ~ 4.01.0 ~ 1.5 急降0.4 ~ 0.8质心归一化高度质心 y / 画面高度0.3 ~ 0.6快速增大稳定在 0.7 以上有效面积比框内前景像素占比0.3 ~ 0.6略增0.6 ~ 0.9这三个量里单帧抖动最大的是宽高比前景掩码边缘一收缩框就跳变。单帧直接用会导致后续判定频繁误触所以在进入判定前必须做滑窗平滑。3.2 光流特征区分跌倒与下蹲的关键信号宽高比和质心高度能捕获大部分跌倒但在快速下蹲捡东西这类场景会产生相似的变化模式。下蹲是主动控制下的慢速动作跌倒是被动失稳下的快速下落本质差异在垂直方向的速度曲线。光流可以逐像素估计运动场给出连贯的垂直运动方向信息。Matlab 的光流估计在 Computer Vision Toolbox 里有三个选择LK、Horn-Schunck 和 Farneback。跌倒检测场景用 Lucas-Kanade 稠密光流就够了计算量比 Horn-Schunck 低用 opticalFlowLK 构造% 创建 LK 光流估计器每帧灰度图作为输入 opticFlow opticalFlowLK(NoiseThreshold, 0.009); % 每帧转灰度后估计光流场 gray im2gray(frame); flow estimateFlow(opticFlow, gray); % 竖直方向光流分量统计跌倒时 V 分量显著增大 vComp flow.V; % 只统计前景区域内的竖直光流规避背景运动干扰 fgIdx find(mask(:) 0); if ~isempty(fgIdx) meanV mean(vComp(fgIdx)); % 竖直光流中方向为下的像素占比 downwardRatio sum(vComp(fgIdx) 1) / numel(fgIdx); endestimateFlow 是带状态的连续帧调用时内部会用上一帧做参考循环里不能重建 opticFlow 对象。NoiseThreshold 是像素梯度幅值下限低于这个值的像素不参与求解默认 0.009 对 0 到 1 范围的灰度图是合理的。downwardRatio 抓的是画面内有多少比例的像素在向下运动。正常行走时四肢与躯干的运动方向分散downwardRatio 大致在 0.3 到 0.5 之间跌倒时整个人体整体向下运动这个值会冲到 0.7 以上。它对缓慢下蹲鲁棒因为下蹲时躯干基本静止运动集中在下肢局部像素占比远低于跌倒。注意如果背景有窗帘摆动meanV 会被背景运动主导必须用 mask 限制统计区域。前景掩码质量直接决定光流特征是否可用这也是把形态学清洗放在第 2 章而不是本章的原因。3.3 特征平滑与滑窗聚合单帧特征直接用会出两个问题单帧误检前景空洞、检测框跳动带来尖峰判定层一遇尖峰就触发跌倒过程本身需要在一个时间窗口内观察单帧的宽高比突变可能只是框抖动。常见的做法是开一个固定长度的滑窗对每个特征做滑窗中值滤波再聚合。% 特征矩阵按时间顺序压成 featMat行对应帧列对应特征 featMat [aspectRatioSeries, centroidNormYSeries, ... meanVSeries, downwardRatioSeries]; winLen 15; % 0.5 秒 30fps smoothed movmedian(featMat, winLen, 1); % 滑窗内宽高比下降幅度窗口首帧与末帧之差 aspectDrop smoothed(end, 1) - smoothed(1, 1); % 质心垂直速度滑窗内质心位移换算成每秒变化量 frameRate 30; centroidSpeed (smoothed(end, 2) - smoothed(1, 2)) * frameRate / winLen;movmedian 用中位数而不是均值理由是特征尖峰多为离群点中位数能直接抹掉而不被拉偏。winLen 取 15 帧表示 0.5 秒跌倒动作本身持续约 0.5 到 1 秒窗口太短抓不全动作太长则引入动作开始前的大量正常帧判定延迟也跟着变长。先看平滑后的宽高比有没有跨过从大于 1.5 到小于 1.0的翻转再看质心速度是否超过阈值两个条件都满足才进入下一层的判定逻辑。特征工程到这一步已经足够喂给 SVM下一章把阈值判定和分类器两条路线都写出来。4. 跌倒判定阈值规则与 SVM 分类器两种落地方式特征已经构造好判定层有两条路人工设规则阈值或者用 SVM 从标注数据里学出决策面。阈值法结构透明便于在源码里逐条对照 if 条件SVM 能捕捉特征之间的非线性组合虚警更低但需要标注数据。可用的系统源码里往往是先有阈值逻辑再在它基础上训练分类器做二次确认。4.1 阈值判定宽高比突变、质心骤降与短时静止三条件单帧判定逻辑可以写成独立函数跨帧的确认计数器用 persistent 保存function [eventFlag, confirmCount] judgeByThreshold(featVec, confirmCount) % featVec(1): 滑窗后宽高比 % featVec(2): 质心垂直速度归一化/秒 % featVec(3): 滑窗后质心高度 % featVec(4): 滑窗后有效面积比 condA featVec(1) 1.0; % 宽高比翻转到 1 以下 condB featVec(2) 0.25; % 质心下落速度超阈值 condC featVec(3) 0.6 featVec(4) 0.55; % 低质心 高占比形态 % 三条件连续满足的帧数计数 if condA condB condC confirmCount confirmCount 1; else confirmCount 0; end eventFlag confirmCount 10; % 连续 10 帧满足才确认 if eventFlag confirmCount 0; % 触发后复位避免连续触发 end end三个条件缺一不可。只看宽高比弯腰捡东西会触发只看质心速度跳起落地又会触发condC 的 fillRatio 条件把仍有站立形态但框内比例异常的情形排除。连续确认 10 帧并不是简单延迟报警而是要求帧与帧之间连续满足中间任何一帧不满足就清零这种机制对单帧误报的抵抗力比低通滤波强得多。阈值取值必须对照自己的数据重标定。condA 的 1.0 对应宽高比翻转点人体在画面里偏小时用 1.2 更稳condB 的 0.25 是质心垂直速度阈值帧率越低该值要越小因为每帧间隔对应真实时间更长。标定方法是把一条已知跌倒视频的特征序列画出来看三条曲线在跌倒时刻的数值落在哪里再用 70% 分位数做初值。4.2 用 fitcecoc 训练一个 SVM 分类器替代人工阈值阈值法的问题在条件之间的耦合速度阈值和宽高比阈值语义不同、单位不同很难统一调优。SVM 让统计学习替人决定特征之间的组合关系把四个特征放进一个向量输出跌倒得分。素材组织成监督学习样本每一帧特征向量 x [aspectRatio, centroidNormY, fillRatio, downwardRatio, meanV]标签来自人工标注1 表示跌倒过程帧0 表示正常行为帧。% 读回标注好的特征数据featMatAll 是 Nx5 特征矩阵labelsAll 是 Nx1 标签 load(fall_train_feats.mat); % 用 fitcecoc 训练二类 SVM指定 RBF 核 t templateSVM(KernelFunction, rbf, ... BoxConstraint, 1, ... KernelScale, auto); mdl fitcecoc(featMatAll, labelsAll, Learners, t, ... ClassNames, [0, 1]); % 对测试特征预测score 第二列是跌倒类得分 [predLabel, score] predict(mdl, featTest);fitcecoc 名字里的 ecoc 表示纠错输出码二分类时等价于普通二类 SVM。写成 fitcecoc 是为了后续扩展正常/跌倒/徘徊多类时可以直接复用同一套代码不用改训练入口。templateSVM 里 KernelFunction 用 rbf 是处理特征尺度不一致的常见做法BoxConstraint 控制误分类惩罚默认 1KernelScale 设为 auto 让 Matlab 用启发式自动选核宽度避免手调。训练数据里正负样本比例很容易失衡跌倒帧在整个视频里占比极小。常见做法是只取跌倒发生后 1 秒内的帧作正样本负样本从正常行走的帧里均匀抽取控制在正样本的 2 到 3 倍。负样本过多会让决策面偏向多数类跌倒漏检过少则虚警率上升。手工标注帧号时先确认和实际帧率对齐差 15 帧就会把大量正常帧标成正样本。判定方案调试成本数据需求误报表现适用阶段三条件阈值低直接改常数不需要高需反复标定快速原型、冷启动SVM 二分类中调核与采样需要标注帧低组合特征更稳数据积累后的正式版本阈值 SVM高两套参数需要标注帧最低双重确认上线前的最终形态阈值法和 SVM 并不互斥。可以在阈值法命中后取出以该时刻为中心前后各 15 帧的特征序列输入 SVM 做二次确认只有两者都通过才输出事件。粗判加精判的结构是这类系统里最常见的组合方式也是把误报率压下来的主要手段。5. 跌倒检测降低虚警率的调参技巧与实时视频流验证方法链路走通之后注意力放在两个最容易出问题的环节怎么量化系统的误报、怎么在实时视频流上验证而不是只在剪辑片段上验证。这两个技巧决定系统能否从 demo 走向可用。5.1 用混淆矩阵核对误报来自哪一类动作模型训练完别急着看准确率先输出混淆矩阵。Matlab 用 confusionchart 一行代码可视化横轴是预测类竖轴是真实类% labelsTest 是测试集真实标签predLabel 是模型预测结果 conf confusionchart(labelsTest, predLabel); conf.Title 跌倒检测混淆矩阵; conf.RowSummary row-normalized; % 按行归一化看每类召回率 conf.ColumnSummary column-normalized;重点看两类错误假阳性是非跌倒被判为跌倒假阴性是跌倒没被判出来。跌倒检测场景里假阳性的代价是护理人员被反复骚扰最终关掉系统假阴性直接让系统失去存在意义调参优先级是假阴性尽可能低、假阳性控制在可接受范围。如果误报集中在某个动作比如蹲下捡东西回去检查 3.2 的 downwardRatio 区分度如果误报随机分布更可能是前景掩码不稳导致特征抖动去 2.2 增强形态学处理或提高面积阈值。5.2 两个关键参数滑窗长度和确认帧数调参从最容易见效的两个量开始。滑窗长度 winLen 和确认帧数 confirmCount 本质上在控制观察多久再下结论参数变化对系统行为的影响如下参数调小调大选择依据winLen滑窗检出快、帧级误报多稳定、检出延迟高慢动作视频取 20~30 帧跌倒动作剧烈取 10~15 帧confirmCount反应灵敏、易连续触发漏报风险上升画面人体占比大时可加大BoxConstraint决策面平滑、虚警低过拟合、边界毛刺先固定 1效果不满意往 0.1 与 10 两头试探KernelScale决策边界卷曲决策边界过于平滑有标注数据时先用 auto再手动缩放到 0.5~2 倍调参顺序固定成先调滑窗长度让误报数量显著下降再调 confirmCount 清除残余的短促误报最后才动 SVM 的两个参数。前两者是时域滤波效果后两者是特征空间里的决策面混在一起调很难定位问题来源。5.3 在实时视频流上验证逐帧输出状态而不是剪辑后判定很多代码在测试阶段对整段视频做整体判定指标数值很好看但上线后立刻暴露问题实时判定必须逐帧向前推进没有回头标注的余地。验证阶段要把检测主循环写成一个独立函数输入单帧和跨帧状态输出当前判定function [eventFlag, state] detectFallOnFrame(frame, fg, opticFlow, state) % fg: ForegroundDetector 对象opticFlow: 光流估计器 % state: 结构体保存前景掩码、平滑特征和确认计数器 fgMask step(fg, frame); se strel(disk, 3); mask bwareaopen(imclose(fgMask, se), 200); stats regionprops(mask, BoundingBox, Centroid, Area); % ...特征计算与阈值判定逻辑... eventFlag false; % 满足条件时置 true end函数化之后每次触发事件时把发生时刻前后各 10 秒的视频段和特征曲线一起存盘。复查时看特征曲线相当于看到判定依据的原始轨迹比看视频本身更容易定位是哪一层误判如果是确认阶段的曲线反复穿越阈值说明滑窗太短如果是宽高比根本没翻转说明前景提取已经出错改分类器参数没有意义。一次完整的验证循环至少包含三段场景连续跌倒一次验证召回、正常走路与坐下做误报压力测试、弯腰捡东西做针对性误报测试。按 5.1 的方式记录误报回 5.2 调参反复两三轮误报率就能收敛。这个验证循环可以固化成脚本后续每次修改特征或参数都自动回放三组场景对比误报帧号的变化比肉眼扫视频高效得多。本文还有配套的精品资源点击获取
返回列表