简介:这是一份面向毕业设计的MATLAB人体动作识别资料包,基于公开的KTH数据集,实现步行、跑步、跳跃、挥手、坐下和站立等六类动作的识别流程。内容覆盖视频帧读取、灰度化与尺寸标准化等预处理,HOG与光流特征提取,SVM分类器训练与准确率评估,并带有可交互界面。包内共94个文件,以62张动作帧JPG图像和19个M源代码文件为主,另有MAT数据文件保存真值标签与训练特征,FIG/PNG用于展示实验图表,AVI为KTH样本视频,HTML为代码说明,整体约19.49MB。目前已有161人学习下载,适合需要快速搭建动作识别实验、掌握视频处理与机器学习结合方法的计算机视觉方向学生参考。
1. 用MATLAB和KTH数据集做人体动作识别,到底在研究什么
人体动作识别(Human Action Recognition, HAR)这几年在计算机视觉里被反复提起,但很多初学者拿到KTH这类经典数据集后,第一反应往往是:这不就是把视频帧丢进神经网络里让它自己学吗?实际动手做一次,你会发现最耗时间的根本不是搭建网络,而是如何把一段段视频转成可计算的时空特征、如何在MATLAB里管理不同长度的帧序列、以及如何让分类器在"动作类内差异大、类间差异小"的KTH数据上拿到能写进论文的结果。
KTH数据集包含6类动作(走路、慢跑、跑步、拳击、挥手、拍手),由25个人在4种场景下拍摄,总计599段视频。它最大的特点是每个动作都有明显的周期性,对传统手工特征(如时空兴趣点、光流直方图)和深度学习模型都适用。本文沿着"数据预处理 -> 特征提取 -> 分类器设计 -> 实验评估"这条工程主线来展开,使用的工具是MATLAB,不依赖外部深度学习框架。对于做毕业设计的人来说,这套流程的好处是每个环节都可以单独可视化、调参、写进论文,且对硬件几乎没有要求。
下面先从最关键的数据准备工作说起,因为KTH的视频文件并不是拿来就能直接用的。
2. KTH视频数据的载入、抽帧与基础预处理
2.1 理解KTH数据集的目录结构和视频编码特征
KTH数据集解压后通常按person01_boxing_d4_uncomp.avi、person02_walking_d1_uncomp.avi这样的规则命名。名称中的personXX代表受试者编号,boxing/walking等是动作类别,d1~d4是场景序号,uncomp表明这是未压缩的AVI格式,方便直接用VideoReader读取。
需要注意,MATLAB 的VideoReader对不同编码格式的支持是有限的。KTH官方提供的是未压缩AVI,在Windows和Linux上的MATLAB里基本都能直接读。如果遇到读不出来的情况,最常见原因是系统中缺少对应的解码器,解决办法是用FFmpeg重新转码为MPEG-4或Motion JPEG格式,而不是去修改代码逻辑。
% 读取单个KTH视频文件并输出基本信息 v = VideoReader('person01_boxing_d1_uncomp.avi'); fprintf('视频帧数: %d\n', v.NumFrames); fprintf('帧宽: %d, 帧高: %d\n', v.Width, v.Height); fprintf('帧率: %.2f\n', v.FrameRate);这段代码的核心作用是验证MATLAB是否能正常解码该视频文件。如果NumFrames返回 -1 或读取时报错,说明当前环境不支持这种编码格式。在写批量处理脚本之前,先跑通这一句,否则后面所有环节都会卡住。
2.2 抽帧策略:均匀采样与光流计算的取舍
KTH数据集的分辨率是160x120,帧率25fps,每段视频长度约4秒。做动作识别时,极少有人把全部帧都送进模型,原因有两个:一是相邻帧之间信息冗余度极高,二是传统特征(比如光流)计算复杂度与帧数成正比。常用的做法是均匀抽帧,每段视频抽取固定的帧数,比如16帧或32帧,这样可以保证输入到分类器的特征维度是一致的。
% 均匀抽帧:从视频中抽取 N 帧,返回灰度图像序列 function frames = extractUniformFrames(videoPath, N) v = VideoReader(videoPath); totalFrames = v.NumFrames; if totalFrames < N % 帧数不足时,用重复最后一帧的方式补齐 idxs = [1:totalFrames, ones(1, N-totalFrames)*totalFrames]; else idxs = round(linspace(1, totalFrames, N)); end frames = zeros(v.Height, v.Width, N, 'uint8'); for i = 1:N frame = read(v, idxs(i)); frames(:,:,i) = rgb2gray(frame); end end这里的linspace(1, totalFrames, N)是均匀抽帧的关键参数。如果N取16,则从视频的首帧到末帧等间隔地取16个位置,这样既保证覆盖了整个动作周期,又避免了连续的重复信息。read(v, idxs(i))是随机读取,在循环中并不会重头播放视频,所以即使抽帧位置不连续也不会漏帧。
需要注意的是,KTH数据的原始帧尺寸较小,在做光流或HOG特征提取前,通常先双线性插值缩放到固定尺寸,比如128x128。这能降低边界干扰,同时让后续的特征维度更稳定。
2.3 前景提取与背景减除的必要性
KTH数据集的场景大多是静态的,背景基本不变,前景就是运动中的人体。直接用原始灰度图做特征提取,会把墙、门、地面这些不携带动作信息的像素也算进去,导致分类器学到的是"这个人在哪"而不是"这个人在做什么"。所以工程上会先做背景建模,把运动区域裁剪出来再做后续处理。
MATLAB中做前景提取最直接的是帧差法或高斯混合模型(GMM)。帧差法适合起步,计算量小,但容易在人体运动慢时产生空洞。GMM对慢运动更鲁棒,不过在初试阶段,帧差法加形态学闭运算已经能拿到不错的效果。
% 基于帧差法的前景掩码提取 function mask = getForegroundMask(frames, threshold) refFrame = double(frames(:,:,1)); % 以第一帧为参考背景 current = double(frames(:,:,end)); diff = abs(current - refFrame); mask = diff > threshold; % 形态学闭运算填补人体内部的空洞 se = strel('disk', 3); mask = imclose(mask, se); mask = imfill(mask, 'holes'); endthreshold是这里最需要调的参数,通常在15到30之间(灰度值范围0~255)。取值过小,背景的微小抖动会被判为前景,产生大量噪声点;取值过大,人体四肢的浅色区域会被漏掉,导致掩码不完整。你可以把每段视频的掩码用imshow(mask)逐一显示,根据目测效果确定阈值。
提示:KTH数据集中存在部分视频的人物与背景对比度很低(比如白衣人在白墙前),此时单一阈值会失效,建议改为自适应阈值(
graythresh)或直接在原始灰度图上提取方向梯度特征,不做前景分离。
3. 特征工程:从HOG到时空运动描述子
3.1 为什么毕业设计不直接用视频帧作为特征
很多人在毕业设计里尝试直接把下采样后的灰度帧拼接成向量,用SVM分类,结果准确率卡在70%以下。原因在于原始像素对光照、视角、衣着颜色过于敏感,而KTH数据集里同一动作在不同人身上、不同场景中,底层视觉差异远超动作本身的差异。特征工程的目的就是把这些无关变量剔掉,只保留"运动模式"。
在MATLAB中,计算机视觉工具箱提供了extractHOGFeatures函数,可直接从灰度图像中提取HOG描述子。HOG统计的是局部区域内梯度方向的分布,对光照变化不敏感,对人体的轮廓和四肢姿态却描述得很好。KTH中的动作(拳击、挥手、拍手)主要区别在上半身的姿态变化,走路、慢跑、跑步则区别在腿部运动频率和幅度,这些信息恰好是HOG能捕捉的。
% 提取单帧HOG特征 img = imresize(frames(:,:,i), [128, 128]); cellSize = [8 8]; % 每个cell大小 hog = extractHOGFeatures(img, 'CellSize', cellSize, 'BlockSize', [2 2]);CellSize决定了特征的分辨率。8x8的cell在128x128的输入图上会得到16x16的cell网格,最终特征维数在几千数量级。如果把CellSize改成 [16 16],特征维度会大幅下降,但空间细节也会丢失,对拳击这类上半身小幅度动作会明显不利。反过来,用 [4 4] 会让维度爆炸,训练时长剧增,而准确率提升有限。
3.2 单帧特征到视频级特征的聚合方法
拿到每帧的HOG向量后,需要把一段视频的所有帧特征合并成一个全局特征向量,才能交给分类器。最简单的聚合方法是取平均,但会造成时间信息的丢失。比如"慢跑"和"跑步"的区别主要在运动周期快慢上,单纯平均会抹平这种速度差异。
更常用的方案是采用"分块时序统计"。把抽出的N帧分成三段(前段、中段、后段),对每段分别计算HOG均值与标准差,最后拼接成超向量。这样既保有一定的时序结构,又不会让特征维度变得无法管理。
% 视频级特征聚合:每段视频拼接一个特征向量 function featVector = videoFeatureAggregation(frames) numFrames = size(frames, 3); segLen = floor(numFrames / 3); feats = []; for s = 1:3 segFrames = frames(:,:,((s-1)*segLen+1) : min(s*segLen, numFrames)); segHog = zeros(1, 0); for i = 1:size(segFrames, 3) img = imresize(segFrames(:,:,i), [128, 128]); hog = extractHOGFeatures(img, 'CellSize', [8 8]); segHog = [segHog; hog]; % 行拼接 end segMean = mean(segHog, 1); segStd = std(segHog, 0, 1); feats = [feats, segMean, segStd]; end featVector = feats; end这段代码把每段视频的特征维度从单个HOG维度 * N帧压缩为单个HOG维度 * 2 * 3,在保留了动作阶段信息的同时大幅降低了过拟合风险。如果你对运行时序特征有更高要求,可以在分段后叠加光流直方图,不过这会显著增加计算量,需要根据毕设时间安排取舍。
3.3 光流方向直方图:补充运动方向信息
HOG描述的是静态姿态,光流描述的是运动方向和幅度。KTH数据集最有价值的地方在于动作模式相对规整,运动方向的可分性很强(跑步是水平方向主运动,挥手是垂直方向为主)。MATLAB中可以用opticalFlowLK或者opticalFlowFarneback来计算稠密光流。
% 使用Farneback光流计算并生成方向直方图 flow = opticalFlowFarneback; for i = 1:size(frames, 3)-1 imgA = im2double(frames(:,:,i)); imgB = im2double(frames(:,:,i+1)); estimateFlow(flow, imgA); estimateFlow(flow, imgB); magnitude = flow.Magnitude; direction = flow.Orientation; % 弧度值 % 将方向量化到9个bin binIdx = max(1, min(9, floor((direction + pi) / (2*pi/9)) + 1)); histFeat = accumarray(binIdx(:), magnitude(:), [9 1]); % 归一化 histFeat = histFeat / (sum(histFeat) + eps); end光流直方图的核心是把每个像素的运动方向映射到离散的bin中,并用该像素的运动幅度作为加权值。opticalFlowFarneback的参数(如NumIterations、NeighborhoodSize)会影响光流密度和计算速度,默认值在KTH数据集上已经够用。如果实验中发现慢跑和跑步的分类混淆严重,可以尝试提高光流计算的层数(金字塔层数从3增加到5),这能捕捉更大尺度的运动。
4. 用MATLAB搭建KTH动作识别的最小分类系统
4.1 选型:多分类SVM是毕业设计最稳的起点
在特征已经提取完毕的前提下,分类器的选择直接影响最终论文的对比实验部分。K近邻(KNN)实现最简单,但准确率偏低;随机森林调参容易,但在高维特征上表现平庸;深度学习入门门槛高,在KTH这种小数据集上容易过拟合。多分类SVM(采用一对多的编码设计)是动作识别论文中出现频率最高的组合,原因是它天然适合中等规模样本、高维稀疏特征,并且在MATLAB里有现成的fitcecoc函数。
KTH训练集通常选用前16个人的视频,测试集选用后9个人的视频。这个划分原则必须保证训练集和测试集的人员不重叠,否则会出现身份泄露(同一人的不同视频片段同时出现在训练和测试中),导致准确率虚高到98%以上。
% 使用fitcecoc训练多分类SVM % trainFeatures: MxN 矩阵,M为视频片段数,N为特征维数 % trainLabels: Mx1 分类标签向量 t = templateSVM('KernelFunction', 'rbf', 'BoxConstraint', 1, 'KernelScale', 'auto'); mdl = fitcecoc(trainFeatures, trainLabels, 'Learners', t, 'Coding', 'onevsone');BoxConstraint是最关键的参数,它控制错误分类的惩罚力度。值越大,决策边界越复杂,容易过拟合;值越小,边界越平滑,容易欠拟合。KTH数据集特征维度高但样本量只有几百,通常建议从0.1开始尝试,观察交叉验证准确率的曲线。KernelScale设为'auto'时,MATLAB会用启发式方法自动估计RBF核的宽度,这比手动指定固定值更省心,不过会在训练时增加少量计算开销。
4.2 训练集构建与标签管理
在进入fitcecoc之前,需要把从600余段视频中提取的特征和标签正确组织成矩阵。一个常见错误是把所有视频的特征直接拼接,而忘记记录每个特征向量对应的动作类别,导致后面无法评估模型。
% 构建训练数据集 trainFeatures = []; trainLabels = []; actionNames = {'boxing', 'handclapping', 'handwaving', 'jogging', 'running', 'walking'}; for p = 1:16 % 前16人作为训练 for a = 1:length(actionNames) action = actionNames{a}; files = dir(fullfile('KTH', sprintf('person%02d_%s_*', p, action))); for f = 1:length(files) videoPath = fullfile(files(f).folder, files(f).name); frames = extractUniformFrames(videoPath, 16); feat = videoFeatureAggregation(frames); trainFeatures = [trainFeatures; feat]; trainLabels = [trainLabels; categorical({action})]; end end end这段代码中的dir(sprintf(...))模式匹配利用了KTH文件名中的规律,比如person01_boxing_d1_uncomp.avi和person01_boxing_d2_uncomp.avi都会被匹配到。这里建议将HOG特征提取部分单独写成函数,因为每次运行都会重新读取视频并提取特征,耗时长,可以把中间结果保存为.mat文件,后续调参时直接load即可。
4.3 测试集评估与混淆矩阵
训练完成后,对后9个人的视频提取同样的特征,用predict函数获得预测结果,再与真实标签比较。不要只看整体准确率,要打印出混淆矩阵,因为KTH数据集中"跑"和"走"两类动作的混淆度通常最高,混淆矩阵能帮你定位问题出在哪个动作对。
% 测试集评估 testFeatures = []; testLabels = []; % ... 与训练集相同的特征提取逻辑 ... predLabels = predict(mdl, testFeatures); acc = mean(predLabels == testLabels); fprintf('测试集准确率: %.2f%%\n', acc*100); figure; confusionchart(testLabels, predLabels);confusionchart会自动生成带颜色深浅的混淆矩阵图,这个图可以直接用于毕业设计论文。KTH数据集在这种传统特征+SVM的框架下,准确率通常在85%~93%之间(具体取决于特征设计和参数),如果能超过90%,说明特征提取设计得比较到位。
提示:如果你发现混淆矩阵里"跑步"和"走路"互相混淆,不要急着调SVM参数,优先回头检查光流直方图的时间聚合方式。可以考虑把光流特征单独可视化,看两段视频在特征空间中的距离是否真的可分。
5. 实验评估与毕业设计答辩必备的对比基准
5.1 交叉验证策略:留出法 vs 留一法
毕业设计论文里,动作识别部分的实验设计是否严谨,比最终准确率数字更重要。KTH数据集的标准评估协议是"留出法",即按人员ID划分训练集和测试集。但很多本科论文会在这个基础上加一组"留一法(Leave-One-Person-Out)"实验作为对比,即在25个人中依次选1人作为测试、其余24人作为训练,循环25次取平均准确率。
留一法得到的准确率通常比留出法高1~3个百分点,因为它使用了更多的训练样本。但这会让训练时间变成原来的25倍,需要提前估算:如果留出法训练一次要30秒,留一法就是12.5分钟,这在毕业设计的时间预算里还能接受。
% LOPO留一法评估片段 personIDs = 1:25; accList = zeros(1, 25); for testID = 1:25 trainIdx = personIDs ~= testID; % 用 personIDs 划分特征矩阵... mdl = fitcecoc(trainFeatures(trainIdx,:), trainLabels(trainIdx), ... 'Learners', t, 'Coding', 'onevsone'); pred = predict(mdl, testFeatures(~trainIdx,:)); accList(testID) = mean(pred == testLabels(~trainIdx)); end meanAcc = mean(accList);LOPO实验还能额外输出每个测试人的准确率,这比单一的平均准确率更有说服力。答辩时如果被问到"你的方法在哪些人上表现不稳定",你能直接针对准确率偏低的personID来分析原因——是帧率问题、动作幅度差异还是场景影响。
5.2 特征组合实验与维度分析
毕业设计论文中,对比实验通常需要说明"为什么最终选择这种特征组合"。常见的做法是设置三组实验:仅HOG特征、仅光流直方图、HOG+光流拼接。用表格记录每组特征在相同SVM参数下的准确率。
| 特征方案 | 特征维度 | 平均准确率 | 训练时间(秒) |
|---|---|---|---|
| HOG均值+标准差 | 约8000 | 88.3% | 18.6 |
| 光流直方图 | 约1000 | 82.1% | 5.2 |
| HOG + 光流拼接 | 约9000 | 91.7% | 24.3 |
需要说明的是,特征维度并越高越好。如果拼接后准确率没有明显提升,反而训练时间翻倍,那就要考虑做特征筛选(比如用relieff算法计算特征权重,保留topN特征)。这既是论文的加分项,也能让分类器训练更快、泛化更好。
有些同学会在论文里声称用了PCA降维,但如果只是把所有特征直接PCA到50维,后果是准确率明显下降。KTH数据集的类内差异较复杂,直接无监督降维会破坏类间判别信息。更合理的做法是先用LDA(线性判别分析)监督降维到5维(类别数-1),再送入SVM。LDA在MATLAB里的实现很简单:LDA函数在统计工具箱中可直接调用,不过需要注意的是它要求特征数小于样本数,因此在PCA降维后再做LDA是常见组合。
5.3 毕业设计论文中的结果表述技巧
最终的实验结果部分,准确率数字只是基础,更重要的是分析错误模式。答辩时老师大概率会问:为什么跑步和慢跑容易混淆?此时你需要能从特征层面作答:跑步和慢跑的运动频率差异不大,而KTH数据集中的帧率只有25fps,抽16帧后每帧间隔约100毫秒,这导致光流时间窗口内的累计位移差异不够明显;如果增加每秒采样帧数或使用更长时间窗口,区分度会上升。
另外,可以准备一张"特征可视化"图,用TSNE或PCA将HOG特征投影到二维平面,不同颜色表示不同动作类别。这张图能直观展示特征的可分性,比任何文字描述都有说服力。
6. 进阶技巧:用TimeSformer替代手工特征的验证路径
在毕业设计中,如果你的选题偏向深度学习方向,或想在期末赶上新方向,可以考虑把前面手工特征+SVM的框架当作baseline,再引入TimeSformer(一个基于Transformer的视频理解模型)作为对比。之所以在这个阶段才提深度学习,是因为KTH数据集只有599段视频,直接用VideoMAE或SlowFast从头训练会严重过拟合。TimeSformer的优势在于它把视频分成时空patch后,用注意力机制建模长程运动关系,在KTH这种规模的数据上反而可以在预训练权重的基础上做微调,达成不错的迁移效果。
在MATLAB中直接实现完整的TimeSformer训练并不方便,但可以借助MATLAB的深度学习工具箱加载ONNX模型,或者用MATLAB调用Python环境中的PyTorch模型。常见做法是:用Python训练好TimeSformer模型,通过ONNX导出后,在MATLAB中对特征提取结果做一致性对比。这里更推荐的做法是只做推理验证:
% 加载ONNX格式的TimeSformer模型到MATLAB net = importNetworkFromONNX('timesformer_kth.onnx'); % 输入尺寸通常为 (numFrames, Height, Width, Channels) inputData = zeros(8, 224, 224, 3, 'single'); output = predict(net, inputData); % output 的最后一维是类别得分 [~, predIdx] = max(output);importNetworkFromONNX是深度学习工具箱提供的函数,能直接导入外部的ONNX模型。这里有两个常见问题:一是KTH的原始视频分辨率只有160x120,如果直接放大到224x224,姿态会变糊,建议先做双三次插值再送入模型;二是TimeSformer的输入通常是8帧或32帧,帧数不同会直接影响网络的时序注意力长度,需要与你训练时的设置保持一致。
在毕业设计论文里,你可以在实验章节放一张红外热力图,用Grad-CAM展示TimeSformer在KTH视频上关注的注意力区域。这个图能直观说明深度学习模型的决策依据,与手工特征SVM方案形成互补。最终的结论部分强调TimeSformer更强大,但在工业界的可解释性和部署成本上远不如手工特征方案,两者不是互相取代的关系,而是在不同限制条件下各有适用场景。
本文还有配套的精品资源,点击获取