基于Matlab的人体异常行为检测:背景建模+HOG+SVM实现跌倒预警
2026/9/17 3:43:43 网站建设 项目流程

简介:这套基于Matlab的人体异常行为检测源码,面向智能监控与智慧养老场景,利用计算机视觉识别快跑、慢跑、跌倒等异常动作,适合具备一定编程基础的研究者学习主动监控系统的搭建。资源包共756个文件,大小8.82MB,其中以741张jpg样本图像为主,搭配6个m源码文件、3个avi演示视频、fig界面文件以及doc/txt说明文档,便于从数据、代码到界面演示完整对照学习。目前已有349人学习下载;通过该源码可掌握基于Matlab的行为识别思路、GUI交互界面设计方法,以及从图像序列到动作判别的实现流程,还可在实际监控画面中进行扩展验证。

1. 从“被动回放”到“主动报警”,这套 Matlab 代码在做什么

你手上这份基于 Matlab 的人体异常行为检测源代码,解决的不是“监控里有没有人”,而是“监控里的人怎么了”。它会把画面中的行走、慢跑、快跑、跌倒区分开,一旦出现跌倒这类异常姿态,就触发提示。这正好对应空巢老人、医院走廊、养老院等场景:传统监控只能事后回放,而主动预警需要在人摔倒后几秒内就通知到值班端。我拿到这个项目时,第一件事不是看算法,而是打开 Main_Test.fig,因为这个图形界面决定了代码是从视频文件读帧,还是从摄像头实时取流。对正在做课程设计、毕业设计,或者想给现有监控系统加一层智能判断的工程师来说,这份源码的价值在于:它用 Matlab 传统视觉工具箱完成了从特征提取到分类报警的完整闭环,没有依赖深度学习框架,也更容易改造成自己的逻辑。

2. 动作特征怎么来:帧差、背景建模与 HOG 的取舍

2.1 为什么固定机位下先做背景减除,而不是直接上目标检测

样本视频里“1步行.avi”“2慢跑.avi”“3快跑.avi”都是固定机位拍摄,背景基本不变。这种场景下,最划算的做法是背景减除:先让模型学习若干帧纯净背景,再用当前帧减去背景,留下变化区域。现在很多人习惯上来就调 YOLO,但那是另一套工程:需要标注数据、训练权重、处理目标框抖动。传统视觉在固定相机下反而更稳定,而且每一步都能可视化,方便你向评委解释“跌倒到底是怎么判断出来的”。即使你之后要转向深度学习 matlab 的方案,先理解背景建模和姿态特征,也有助于设计更有效的输入特征。

2.2 用 vision.ForegroundDetector 建立背景模型并做形态学清洗

Matlab 的 Computer Vision Toolbox 里,vision.ForegroundDetector是基于高斯混合模型的前景检测器。我的习惯是先读入视频,给检测器 20 帧左右训练背景,再用形态学开闭运算去掉噪声点。下面是核心代码:

% 读取慢跑视频样本 videoReader = VideoReader('2慢跑.avi'); % 高斯混合背景模型 foregroundDetector = vision.ForegroundDetector(... 'NumTrainingFrames', 20, ... 'InitialVariance', 30*30, ... 'MinimumBackgroundRatio', 0.7, ... 'AdaptLearningRate', true); while hasFrame(videoReader) frame = im2gray(readFrame(videoReader)); foreground = foregroundDetector(frame); % 开运算去除孤立噪点,闭运算填补人体区域空洞 foreground = imopen(foreground, strel('disk', 3)); foreground = imclose(foreground, strel('disk', 5)); imshow(foreground); drawnow; end

逻辑说明:im2gray将 RGB 转为灰度,减少计算量;strel('disk',3)定义半径为 3 的圆形结构元素,开运算先腐蚀后膨胀,能清除小噪点,闭运算先膨胀后腐蚀,能连接同一人体区域断裂的碎片。参数需要根据视频分辨率调整:分辨率越高,结构元素半径可以适当加大,否则人体边缘会被削掉。NumTrainingFrames如果是 5,背景模型还没学完就进入检测,很容易把整体画面都当成前景。

2.3 用 regionprops 提取宽高比、重心高度和 HOG 描述子

背景减除得到的是二值图,接下来要把它变成可判别的数值特征。最常用的就是连通区域分析:找到面积最大的目标,提取它的边界框、质心、主轴长度等几何量。

% 对二值前景图做连通域分析 stats = regionprops(foreground, 'Area', 'BoundingBox', 'Centroid'); if ~isempty(stats) [~, idx] = max([stats.Area]); box = stats(idx).BoundingBox; % [x, y, width, height] centroid = stats(idx).Centroid; % 人体重心位置 aspectRatio = box(3) / max(box(4), 1); % 宽高比,防止除零 heightDrop = centroid(2); % y 越大表示中心越靠近画面底部 disp(['宽高比: ', num2str(aspectRatio)]); end

说明:BoundingBox返回[x, y, w, h],其中w/h在站立时通常小于 1,人在跌倒后横躺,w/h会迅速变大。Centroidy坐标方向是从图像顶部向下增长,所以 y 值越大,重心越低。除几何特征外,我一般还会把人体裁剪区域做 HOG 特征提取,用来区分慢跑和快跑时四肢摆动姿态的差异:

% 裁剪出人体区域 patch = imcrop(frame, box); patch = imresize(patch, [128 64]); % 统一尺寸 hogFeature = extractHOGFeatures(patch, 'CellSize', [8 8]);

CellSize决定了特征粒度。8×8 能保留较多边缘方向信息,代价是特征维度更高,训练慢一些;如果追求速度,可以改成 16×16。下表是这三类特征在项目里的定位对比:

特征计算耗时对跌倒敏感度对慢跑/快跑区分度是否能看门狗解释性
宽高比极低
重心高度变化率极低
HOG 描述子中等

3. 跌倒和快跑是怎么被判出来的:阈值规则与 SVM 分类器

3.1 跌倒的几何特征突变和阈值条件

跌倒不是瞬时动作,它伴随着“先快速下坠、随后身体横躺、最后短暂静止”三个阶段。理解这个链条后,阈值就不只是宽高比大于多少的问题,还要考虑运动速度。我一般会维护一个长度为 5 的滑动窗口,保存每一帧的aspectRatio和重心的 y 坐标:

ratioHistory = [ratioHistory, aspectRatio]; centroidHistory = [centroidHistory, centroid(2)]; if length(ratioHistory) > 5 ratioHistory(1) = []; centroidHistory(1) = []; end % 重心下降速度:最近 5 帧 y 坐标的平均差 fallSpeed = mean(diff(centroidHistory)); if aspectRatio > 1.6 && fallSpeed > 0.35 disp('检测到疑似跌倒,触发报警'); end

逻辑说明:aspectRatio > 1.6表示人体宽度已经明显大于高度,接近水平姿态;fallSpeed > 0.35排除掉原地躺下这种缓慢过程。注意,fallSpeed的方向符号要和你自己定义的坐标轴一致:图像坐标下 y 增大表示向下摔,所以diff为正数。1.60.35是从样本视频中反复测出来的经验值,如果摄像头安装得高,画面里人体更小,宽高比阈值可以适当降到 1.3;如果摄像头贴近地面,阈值要调高到 2.0 左右。

3.2 用 fitcecoc 做慢跑、快跑、正常行走的四分类识别

只靠宽高比区分慢跑和快跑很困难,因为两者都是竖直姿态,差异主要在四肢摆动频率和位移速度。项目里更适合的做法是:对每一段连续视频提取一个固定长度的特征向量,然后用 SVM 做分类。Matlab 里多分类 SVM 可以直接用fitcecoc,它会自动拆分出一对一模型:

% 假设已经提取好所有样本视频的特征矩阵 % features: N x M,每行是一个视频的统计特征 % labels: N x 1,每个视频对应的动作标签 features = [meanRatio, meanHog, meanSpeed, heightDropRate]; labels = {'walk'; 'jog'; 'run'; 'fall'}; svmTemplate = templateSVM(... 'KernelFunction', 'rbf', ... 'KernelScale', 'auto', ... 'BoxConstraint', 1); mdl = fitcecoc(features, labels, 'Learners', svmTemplate);

参数说明:KernelFunction换成rbf是因为特征之间不是线性可分;KernelScale设为auto时,Matlab 会依据训练样本分布自动估计尺度,但我发现当样本量很小时,这个自动值容易过拟合,更稳妥的做法是先用fitcsvm对每一对动作做交叉验证,找到合适的BoxConstraintBoxConstraint越大,边界越硬,训练集准确率高但泛化差;这里1是中等强度,适合样本量在几十到几百的情况。特征meanHog如果直接把 HOG 向量做平均,维度会太高,更合理的做法是只取 HOG 能量均值,或者用 PCA 降到 20 维。

3.3 从 Main_Test.fig 的控件看懂数据流向

图形界面是理解整个源码入口的关键。用guide Main_Test.fig打开后,你会看到一个显示图像区的坐标轴、几个按钮,以及一个文本框用于输出识别结果。典型的数据流是:点击“开始检测”按钮触发回调,回调内部创建VideoReader,读取视频帧后调用 2.2 和 2.3 的特征提取函数,再把特征值喂给 3.1 的阈值判断或 3.2 训练好的 SVM 模型,最后把结果标签显示在文本框里。

控件类型名称示例作用
Axesaxes1显示视频当前帧或检测结果框
Push ButtonBtnStart开始读视频
Push ButtonBtnStop停止循环并释放对象
Edit TextTxtResult显示当前动作标签

源代码包里的 204.jpg、203.jpg、669.jpg 等图片,实际上是 GUI 界面测试时的静态帧,用于在拿不到视频时验证检测函数是否可以正常输出。你可以在回调里读取这些图片,逐帧测试特征代码,再切换回视频流。

4. 源码导入与联调:让步行、慢跑、快跑三个 avi 样本跑通

4.1 工作目录与路径设置

拿到源码包后,先把整个文件夹放到纯英文路径下,比如D:\behavior_detection,然后启动 Matlab,添加工作路径。这里最容易踩的坑是,直接双击 avi 文件系统会自动打开一个不相关的媒体播放器,而不是读入 VideoReader。

cd('D:\behavior_detection'); addpath(genpath(pwd)); % 检查是否能看到视频样本 videoReader = VideoReader('1步行.avi'); disp(['视频帧数: ', num2str(videoReader.NumFrames)]);

addpath(genpath(pwd))的作用是把当前目录和所有子目录加入搜索路径,因为源码里可能把函数子函数放在不同子文件夹,少了这步就会报“Undefined function”。如果显示NumFrames为 0,不是程序问题,而是当前 Matlab 版本缺少对应解码器,换用 R2023b 及以上版本通常能解决。

4.2 三个样本如何对应不同的检测阈值

源码包里三个视频分别代表正常、异常临界、异常三种状态。实际调参时,我会先跑“1步行.avi”,记录它在正常状态下的宽高比均值,再跑“2慢跑.avi”和“3快跑.avi”,看特征差异。

视频动作预期宽高比重心下降速度适合的检测策略
1步行.avi正常行走0.4~0.70.05~0.15仅记录基线
2慢跑.avi轻度异常0.6~0.90.2~0.3阈值 + SVM 并查
3快跑.avi异常0.7~1.20.3~0.5SVM 主判,阈值辅助

注意,这里快跑的宽高比比慢跑大,是因为跑动过程身体前倾,目标框宽度增加。若你的监控画面是俯视视角,宽高比逻辑会完全相反,必须把跌倒判断改成“人体区域高度突然减小且长宽比趋近 1”。也就是说,这份源代码自带的参数不是通用常量,而是从这三个角度拍摄的 avi 样本标定出来的。

4.3 高频报错:VideoReader 编码、工具箱未装、fig 回调解案

我会把联调中的高频问题整理成检查顺序,避免浪费时间。

  • Undefined function 'vision.ForegroundDetector':说明没安装 Computer Vision Toolbox。使用ver('vision')检查版本信息,没有的话需要重新运行 matlab 安装程序,勾选该工具箱。
  • Cannot create VideoReader:视频文件用了非法编码,比如微信传给 Windows 的 avi 实际是 H.265。先用格式工厂转成 MJPEG 编码的 avi,或者用ffmpeg -i input.avi -c:v mjpeg output.avi转码。
  • Error while evaluating uicontrol Callback:通常是 fig 里引用的某个函数文件不在路径下。使用which 函数名检查路径,如果返回空白,就把源码文件夹重新执行一遍addpath(genpath(pwd))
  • GUI 中文乱码:这是 matlab 安装时系统区域与代码注释编码不一致造成的。建议用 R2023b 之后的版本,并且在“预设项”里把语言改为 System Locale。

5. 把视频文件输入改成实时摄像头:主动监控落地的最后一步

5.1 用 webcam 或 ipcam 替换 VideoReader

源码里主循环基于VideoReader.readFrame(),切换成摄像头取流后,检测逻辑不用改,变的是输入源。本地 USB 摄像头用webcam,网络摄像头用ipcam。下面这段代码可以直接替换原项目中VideoReader的初始化部分:

% 本地 USB 摄像头 camera = webcam('USB Camera'); % 如果是海康等网络摄像机,用 ipcam % camera = ipcam('http://192.168.1.64:8080/video', 'admin', '123456'); preview(camera); for k = 1:300 frame = snapshot(camera); frameGray = im2gray(frame); % 下面的 foreground 提取逻辑沿用第 2 章代码 foreground = foregroundDetector(frameGray); foreground = imopen(foreground, strel('disk', 3)); foreground = imclose(foreground, strel('disk', 5)); % 是否报警 if shouldAlert(frameGray, foreground) imwrite(frame, ['alert_' datestr(now, 'yyyymmdd_HHMMSS') '.jpg']); insertText(frame, [10 10], 'FALL DETECTED', 'FontSize', 18); end imshow(frame); end

这段代码实现了主动监控最核心的闭环:摄像头捕获帧,背景模型提取人体区域,阈值或 SVM 判断动作,一旦发现跌倒就把报警帧写成alert_时间戳.jpg。注意,foregroundDetector在实时场景下必须提前训练至少 20 帧,所以我在进入循环前先采集了 30 帧纯背景来做模型初始化。datestr(now,...)生成的文件名能保证重复报警不会覆盖旧记录,这个细节在项目答辩里很加分。

5.2 报警帧的本地保存与提示

保存报警帧不是随便存一下,要控制频率:同一事件连续 30 帧都在报警,不应该存 30 张重复图片。我一般加一个冷却时间变量:一旦触发报警,记录当前时间戳,接下来 5 秒内不再接受新的报警图片保存请求,但检测逻辑继续运行。同时把报警信息通过sendmail或写日志的方式留给值班端查询。这样改造完成后,这套基于 Matlab 的异常行为检测系统就不只是能读三个 avi 文件的演示源码,而是可以接入到真实监控场景的主动预警终端;如果你接下来打算把模型迁移到边缘设备,也可以把这里的 HOG 特征导出到 ONNX,再接嵌入式摄像头。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询