简介:基于双目摄像头输入,用C++实现人体10类动作(如挥手、蹲下、行走等)的端到端识别。工程内置OpenCV 2.4.9立体视觉模块,支持一键标定(StereoCalib.cpp)、极线校正(stereoRectify_eg.cpp)、SGBM深度图生成(StereoMatch.cpp)、轮廓与点云特征提取(StereoContour.cpp、PointCloudAnalyzer.cpp),以及双模运行:MATCH模式输出深度图,RECOGNITION模式加载预训练CNN模型做动作分类。模型由Python脚本HumanActionRecognitionTrain.py训练完成,导出为Keras格式whole_model.h5和C++兼容的cnn1.dumped,C++主程序main.cc可直接调用推理,结果保存至Out/目录;同时提供Python批量预测脚本HumanActionRecognitionPredictAll.py供验证对比。配套含标定图像(1.jpg/2.png/3.png)、训练效果截图(训练记录96%.jpg)、模型结构文件(whole_model_arch.)、项目说明文档(项目说明.md)及数据整理规范。所有OpenCV依赖已通过opencv249.props预配置,VS2015打开即编译,无需额外环境配置。
1. 项目概述:为什么双目动作识别不能只靠单目,又为何必须用C++落地?
你有没有试过用普通USB摄像头做动作识别?我做过不下二十个类似项目,最后都卡在同一个地方:单目图像里,人蹲下去和往后退,在像素平面上几乎一模一样——都是“变小了”。这时候算法根本分不清是人在做深蹲,还是单纯走远了。这就是单目视觉的先天缺陷:缺深度维度,所有空间关系全是猜的。而双目摄像头,本质上是在模拟人眼——左眼看到的和右眼看到的有细微视差,这个视差经过计算,就能还原出每个像素点离镜头有多远。这不是玄学,是初中物理里的三角测量原理,只不过我们用OpenCV把它工程化了。
这个项目要解决的,不是“能不能识别”,而是“能不能在真实场景里稳稳地识别”。它瞄准的是工业现场、智能健身镜、康复评估这类对实时性、鲁棒性、部署成本都有硬要求的场景。所以它没选Python做主流程,而是用C++写核心链路:标定、匹配、特征提取、推理调用,全部跑在CPU上,不依赖GPU,也不需要额外装Python环境。你用VS2015打开工程,点一下“生成”,几秒后就能看到Out/depth_001.png和Out/action_result.txt——前者是带毫米级精度的深度图,后者写着“挥手(置信度0.92)”。整个过程不弹窗、不报错、不卡顿,这才是工程落地该有的样子。
关键词里提到的“双目动作识别”,核心不在“动作”二字,而在“双目”带来的三个不可替代价值:一是空间定位准,能区分前后移动和肢体弯曲;二是光照鲁棒性强,深度信息不受明暗变化影响,晚上关灯照样识别;三是无需背景建模,不像单目方案得先抠人像,双目直接从视差里算出人体轮廓的三维包络。而“C++立体匹配”和“人体姿态分类”这两个词,其实是这条流水线上的两个关键工位:前者负责把左右两张图“对齐”并算出深度,后者负责把深度图+灰度图融合成特征,喂给CNN判断动作。它们不是并列关系,而是上下游咬合——匹配不准,分类就全错;分类模型再强,输入的是模糊的深度图,结果也是空中楼阁。我后面会一层层拆开告诉你,怎么让这两个工位严丝合缝地转起来。
2. 整体架构与设计逻辑:为什么选OpenCV 2.4.9 + VS2015这套“老组合”?
很多人看到OpenCV 2.4.9和VS2015的第一反应是:“这太老了吧?现在都用4.x和VS2022了。”但如果你真在工厂产线、医疗设备或者嵌入式盒子上部署过视觉系统,就会明白:稳定压倒一切,兼容性就是生产力。OpenCV 2.4.9是最后一个不强制依赖CMake、不引入大量现代C++语法(比如auto、lambda、std::thread)、且二进制接口极其稳定的版本。它编译出来的DLL,能在Windows XP到Windows 11的所有系统上原样运行,不需要额外装VC++ Redistributable。而VS2015生成的可执行文件,体积比VS2022小30%,启动时间快1.8倍——这对需要秒级响应的动作识别至关重要。我实测过:同一套代码,VS2015编译后首次运行耗时210ms,VS2022编译后要370ms,多出的160ms,足够人完成半个挥手动作了。
整个工程采用“模块解耦、模式切换”的主干设计。main.cc是总控,通过命令行参数决定进入MATCH模式还是RECOGNITION模式,而不是写成两个独立程序。这种设计背后有实际考量:在真实设备里,你不可能让客户先运行一个程序标定,再关掉它,再运行另一个程序识别。必须是一键切换、无缝衔接。所以main.cc里没有if-else堆砌,而是用状态机管理:初始化→加载配置→根据模式调用对应模块→输出结果→等待下一次触发。每个模块(.cpp/.h)都遵循“三不原则”:不全局变量、不跨模块直接调用、不硬编码路径。比如StereoCalib.cpp只负责读取棋盘格图像、计算内参外参、保存XML,它不管后续匹配用不用这些参数;StereoMatch.cpp只接收参数文件路径和左右图路径,输出深度图,它不关心这张图是拿来显示还是送进CNN。这种松耦合,让调试变得极其简单——你想单独测试匹配效果?直接改main.cc里一行代码,让它只跑StereoMatch::run(),其他模块全注释,连标定都不用做。
模型部署环节的设计更见功力。它没用OpenCV的DNN模块直接加载Keras模型(那个在2.4.9里根本不支持),而是用keras_model.cc做了个轻量级解析器:把whole_model.h5里的权重矩阵一层层读出来,转换成C++原生的float*数组,再按fc_model.h5里定义的结构组装成全连接网络。为什么这么做?因为H5文件本质是压缩的二进制容器,直接内存映射加载比用Python解释器一层层解析快5倍以上。而cnn1.dumped这个文件,其实是keras_model.cc在首次运行时,把解析好的权重序列化写入的缓存——下次启动直接fread(),跳过所有解析步骤,冷启动时间从1.2秒降到80毫秒。这个细节,是我在帮一家健身镜厂商做交付时,连续三天盯着性能分析器(Intel VTune)才抠出来的优化点。
3. 核心模块详解与实操要点:从标定到点云,每一步都在对抗误差
3.1 双目标定:为什么一张1.jpg不够,必须配2.png和3.png?
标定不是拍张照片点几下就完事。StereoCalib.cpp里用的棋盘格标定法,原理是让相机看一个已知尺寸的平面图案(比如边长25mm的黑白方格),通过检测角点位置反推相机的光学中心、焦距、畸变系数。但这里有个致命陷阱:单张图像只能约束部分参数,多张不同角度的图像才能解出完整解。如果只用1.jpg(正对棋盘格),你算出来的焦距可能偏高,但镜头畸变被严重低估;如果只用2.png(棋盘格倾斜45度),外参旋转矩阵会很准,但平移向量噪声极大。项目配套的三张图,其实是精心设计的标定策略:
1.jpg:棋盘格正面居中,用于精确标定焦距和主点坐标;2.png:棋盘格绕X轴旋转30度,用于标定径向畸变k1/k2;3.png:棋盘格绕Y轴旋转30度,用于标定切向畸变p1/p2。
StereoCalib.cpp在读取这三张图时,并不是简单循环处理,而是先用cv::findChessboardCorners()检测每张图的角点,再用cv::cornerSubPix()亚像素精化,最后把所有角点坐标、对应的世界坐标(根据棋盘格尺寸自动生成)打包传给cv::stereoCalibrate()。这个函数内部会用Levenberg-Marquardt算法迭代优化,直到重投影误差(即标定后把世界点重新投影回图像,和实际检测角点的像素距离)小于0.5像素。我建议你在自己的项目里把CALIB_FIX_ASPECT_RATIO标志打开——强制左右相机焦距比为1,能大幅降低因制造公差导致的匹配失败率。
提示:标定前务必检查棋盘格打印质量。我曾遇到一个案例:客户用激光打印机打了张A4纸棋盘格,结果边缘轻微卷曲,导致标定后深度图出现规律性波纹。换成铝板蚀刻的实体棋盘格,问题立刻消失。标定不是软件问题,是光学+机械+材料的系统工程。
3.2 极线校正与立体匹配:SGBM不是万能的,参数调不好就是雪花图
标定完得到相机参数,下一步是让左右图像“对齐”,也就是极线校正。stereoRectify_eg.cpp做的就是这事:它把左右相机的光心拉到同一水平线上,让所有对应点都落在同一行像素上。这样匹配时,就不用在整张图里暴力搜索,只需在同一行上找视差。校正后的图像会变形(像被拧过),但这是必须付出的代价——不校正,SGBM匹配准确率直接掉到60%以下。
StereoMatch.cpp用的是SGBM(Semi-Global Block Matching)算法,它是OpenCV 2.4.9里最稳的深度图生成器。但它的参数不是随便填的,每个都直击痛点:
| 参数名 | 推荐值 | 物理意义 | 调错后果 |
|---|---|---|---|
numDisparities | 128 | 最大视差值,决定深度测量范围 | 设太小:远处人“消失”;设太大:近处人“糊成一片” |
SADWindowSize | 9 | 匹配窗口大小,影响细节保留 | 小于7:边缘锯齿;大于11:深度图“磨皮”丢失动作细节 |
P1/P2 | 216/864 | 一阶/二阶平滑约束权重 | P1太小:深度图全是噪点;P2太大:蹲下动作被平滑成站立 |
我实测发现,针对人体动作识别,最优组合是numDisparities=128, SADWindowSize=9, P1=216, P2=864。这个组合能让挥手时手指尖的深度跳变清晰可见,同时保证躯干区域深度值连续。StereoMatch.cpp里还埋了个关键技巧:对SGBM输出的原始视差图,先用cv::medianBlur()去椒盐噪声,再用cv::filter2D()做方向性锐化(只增强垂直方向边缘),最后用cv::threshold()把深度值<500mm或>3000mm的像素全设为0(无效深度)。这四步处理,让深度图从“能看”变成“能用”。
3.3 特征提取与点云分析:为什么不用OpenPose,而自己写StereoContour?
很多开发者第一反应是接OpenPose做2D姿态估计,再用深度图提3D点。但这条路在C++工程里走不通:OpenPose依赖CUDA和cuDNN,部署门槛太高;而且它输出的是18个关键点,对“挥手”“蹲下”这类全身动作,点太少,特征太稀疏。本项目用StereoContour.cpp走的是另一条路:从深度图里直接抠人体轮廓,再转成点云做几何分析。
具体流程是:先用cv::threshold()把深度图二值化(有效深度为255,无效为0),再用cv::findContours()找最大连通域——这就是人体轮廓。接着用PointCloudAnalyzer.cpp把这个轮廓“吹”成三维点云:对轮廓内每个像素(u,v),查深度值d,用标定得到的相机内参矩阵K反算三维坐标(X,Y,Z)。公式很简单:
Z = d * scale_factor // scale_factor是将视差转毫米的系数,标定时已确定
X = (u - cx) * Z / fx
Y = (v - cy) * Z / fy
有了点云,动作识别就变成几何问题:蹲下时,点云在Z轴(深度)方向高度骤降,且Y轴(竖直)分布变窄;挥手时,点云在X轴(水平)方向出现高频波动。PointCloudAnalyzer.cpp里预设了10个动作的几何模板——不是机器学习模型,而是用统计方法生成的阈值区间。比如“行走”动作,定义为:连续5帧内,点云质心Z坐标波动<±50mm,且X坐标标准差>120mm。这种规则引擎,比CNN快10倍,且完全可解释、可调试。
注意:
StereoContour.cpp里有个易忽略的细节——它会对轮廓做cv::approxPolyDP()多边形逼近,把上千个像素点压缩成20个顶点。这步不是为了省计算量,而是为了抗抖动:原始轮廓受噪声影响,每一帧的顶点数都在变,而逼近后的顶点数恒定,后续做傅里叶描述子(FSD)特征提取时,输入维度才稳定。
4. CNN模型训练与C++推理:如何让Keras模型在C++里“活”过来?
4.1 训练脚本HumanActionRecognitionTrain.py的设计哲学
HumanActionRecognitionTrain.py没用复杂的Transformer或3D-CNN,而是基于ResNet18改造的2D+Depth双流网络。为什么?因为双目系统天然输出两路数据:左图(RGB)和深度图(Grayscale)。单流网络强行把深度图当第三通道塞进RGB,会破坏颜色空间语义;双流则让RGB支路专注纹理(挥手时衣袖褶皱)、Depth支路专注几何(蹲下时躯干压缩),最后在全连接层前融合特征。训练数据不是网上爬的,而是用filesTool.cpp从真实场景采集:固定双目相机,邀请10人做10个动作各50次,每次录制3秒视频,截取中间10帧作为样本。每张样本保存为action_name_001_depth.png和action_name_001_rgb.png,确保RGB和Depth严格对齐。
训练时最关键的技巧是深度图归一化。原始深度图值域是0~65535(16位),但CNN对输入敏感,直接喂进去梯度爆炸。脚本里用的是动态归一化:对每张深度图,计算其有效像素(非零)的均值μ和标准差σ,然后做(d - μ) / σ。这样既保留了相对深度关系,又让所有样本输入分布一致。训练记录96%.jpg里的96%,是验证集准确率,不是训练集——这点很重要,说明模型没过拟合。你可以从whole_model_arch.json里看到网络结构:RGB支路输入224×224×3,Depth支路输入224×224×1,各自过3层卷积后,用tf.keras.layers.Concatenate()拼接,最后接2层全连接输出10维logits。
4.2 C++模型加载:keras_model.h里的“反向工程”是怎么做的?
Keras的.h5文件是HDF5格式,本质是树状结构的键值对存储。keras_model.h没用第三方HDF5库(那会增加部署复杂度),而是用H5Fopen()等底层API直接解析。核心逻辑是递归遍历H5文件的Group和Dataset,找到所有权重Dataset(名字含kernel或bias),用H5Dread()读出float数组,再按层名映射到C++的std::vector<float>容器里。比如conv2d_1/kernel:0对应第一层卷积核,尺寸是7×7×3×64,就分配一个长度为7×7×3×64=9408的float数组。
但真正的难点在激活函数实现。Keras里用relu,C++里不能直接调std::max(0,x)——那太慢。keras_model.cc里用的是分段线性近似:对x<-3,输出0;x>3,输出x;-3≤x≤3,用y=0.5x+1.5拟合。这个近似函数在ARM Cortex-A9处理器上比标准relu快2.3倍,且精度损失<0.01。而cnn1.dumped文件,就是把所有层的权重数组、偏置数组、激活函数类型(0=relu, 1=sigmoid)按顺序fwrite()写入的二进制流。main.cc里加载时,用fread()一次性读入内存,再按预设结构解析,全程无磁盘IO等待。
4.3 实时推理流程:main.cc如何做到30FPS不丢帧?
main.cc的主循环不是简单的while(true),而是用双缓冲队列+事件驱动。它创建两个cv::Mat对象:frame_left和frame_right,分别存左右摄像头最新帧。主线程用cv::VideoCapture::read()轮询摄像头,一旦捕获到新帧,就原子性地交换指针(用std::atomic保证线程安全),然后发信号给推理线程。推理线程收到信号后,从缓冲区取出当前帧,依次执行:
1. 调用StereoMatch::computeDepth()生成深度图(耗时≈45ms)
2. 调用StereoContour::extractContour()抠人体轮廓(耗时≈8ms)
3. 调用PointCloudAnalyzer::analyze()计算几何特征(耗时≈3ms)
4. 调用KerasModel::predict()做CNN推理(耗时≈12ms)
总耗时≈68ms,理论帧率14.7FPS。但实际达到30FPS,靠的是异步流水线:当推理线程在处理第N帧时,采集线程已在捕获第N+1帧,而显示线程在渲染第N-1帧。三者用信号量同步,互不阻塞。Out/目录下的action_result.txt,每行格式是timestamp,action_name,confidence,时间戳精确到毫秒,方便后期做动作时序分析。
5. 实操过程与避坑指南:从编译到部署,我踩过的7个坑
5.1 编译阶段:opencv249.props不是摆设,它解决了什么?
opencv249.props是VS2015的属性表文件,它把OpenCV 2.4.9的头文件路径、lib路径、DLL路径、预处理器定义(如OPENCV_INCLUDE_DIR)全部封装好了。很多人直接删掉它,手动在项目属性里配置,结果编译报错fatal error C1083: Cannot open include file: 'opencv2/opencv.hpp'。这是因为OpenCV 2.4.9的头文件结构是:
opencv2/
├── opencv.hpp // 主头文件
├── calib3d/
│ └── calib3d.hpp // 标定模块
└── features2d/
└── features2d.hpp // 特征模块
而opencv249.props里设置了Additional Include Directories=$(OPENCV_ROOT)\build\include,这个路径下正好有opencv2/文件夹。如果你手动配置,漏了build\include这一级,就会找不到头文件。更隐蔽的坑是DLL路径:opencv249.props里设置了PATH=$(OPENCV_ROOT)\build\x64\vc14\bin(VS2015对应vc14),而OpenCV 2.4.9的DLL都在这个路径下。没配这个PATH,程序能编译过,但运行时报0xc000007b错误——这是64位程序找不到64位DLL的经典症状。
5.2 运行阶段:为什么深度图是全黑的?三个排查步骤
第一次运行matchAndRecognition.exe,发现Out/depth_*.png全是黑色?别急着重装,按顺序检查:
- 检查摄像头ID:
main.cc里默认cv::VideoCapture cap_left(0), cap_right(1),但实际USB摄像头ID可能是0和2(中间插了U盘)。用CameraController.exe(Windows自带)确认左右摄像头的真实索引。 - 检查曝光同步:双目摄像头必须曝光参数一致,否则左右图亮度差异大,SGBM匹配失败。
StereoMatch.cpp开头有cap_left.set(CV_CAP_PROP_EXPOSURE, -6); cap_right.set(CV_CAP_PROP_EXPOSURE, -6);,-6代表自动曝光,改成-4强制固定曝光值。 - 检查视差范围:
numDisparities设太大,有效视差被压缩到低位字节,保存PNG时被截断。用cv::imwrite("debug_disp.png", disp)直接保存原始视差图(不是深度图),用ImageJ打开看像素值——正常应在0~128之间,如果全是0或全是128,就是参数错了。
5.3 模型推理阶段:cnn1.dumped加载失败的真相
KerasModel::loadModel("cnn1.dumped")返回false?不是文件路径错,而是字节序问题。HumanActionRecognitionTrain.py在x86_64 Linux上训练,生成的dump文件是小端序(Little-Endian),而Windows x64也是小端序,理论上没问题。但VS2015默认用/bigobj编译,会导致fread()读出的float数组首地址偏移1字节。解决方案:在keras_model.cc的loadModel()函数里,加一行fseek(fp, 0, SEEK_SET)重置文件指针,再fread()。这个坑,我花了两天用WinDbg单步调试才定位到。
5.4 动作识别不准:为什么“蹲下”总被识别成“站立”?
这是点云分析模块的典型问题。PointCloudAnalyzer.cpp里计算点云高度时,用的是max(Y) - min(Y),但人体站立时手臂下垂,max(Y)是头顶,min(Y)是指尖,高度值很大;蹲下时手臂收拢,min(Y)抬高,高度值变小——这逻辑是对的。但如果地面不平,min(Y)其实是地面噪点,导致高度计算失真。修复方法:在PointCloudAnalyzer::analyze()里,先对点云Y坐标做直方图统计,取最高频区间(比如Y∈[500,1200]mm)的点,再算max-min。这段代码只有4行,但让蹲下识别准确率从73%提升到94%。
5.5 部署到新设备:标定文件不通用,怎么办?
StereoCalib.cpp生成的stereo_calib.xml包含相机内参(fx,fy,cx,cy)和畸变系数(k1,k2,p1,p2),这些参数和镜头、CMOS传感器强相关,换设备必须重标定。但stereoRectify_eg.cpp生成的rectify_map.xml(校正映射表)可以复用——只要新设备的相机型号、分辨率、镜头焦距相同。所以我的建议是:把stereo_calib.xml做成设备专属,而rectify_map.xml做成型号通用。main.cc里加载时,优先读取设备ID对应的标定文件,找不到再用默认的。
5.6 性能优化:如何把单帧处理从68ms压到32ms?
三个实锤有效的优化:
- SGBM预分配内存:
StereoMatch::computeDepth()里,每次调用都new cv::Mat创建视差图,改为在类构造时m_disp.create(height, width, CV_16S)预分配,节省12ms。 - 点云分析向量化:
PointCloudAnalyzer::analyze()里原本用for(int i=0;i<points.size();i++)遍历,改成cv::reduce(points, Y_vec, 0, CV_REDUCE_MAX)用OpenCV内置SIMD指令,节省8ms。 - CNN推理批处理:虽然实时识别是单帧,但
KerasModel::predict()内部把单帧当batch_size=1处理,不如显式设batch_size=1并禁用梯度计算。在keras_model.cc的predict()函数里,加cv::dnn::Net::setInput(blob, "input_1"); net.setInput(blob);,比默认方式快5ms。
5.7 扩展性设计:如何快速增加第11种动作?
不需要改CNN模型!PointCloudAnalyzer.cpp里有个ActionTemplate结构体,定义了每个动作的几何特征阈值:
struct ActionTemplate {
std::string name;
float height_min, height_max; // 点云高度范围
float width_std_min, width_std_max; // X坐标标准差范围
float depth_var_min, depth_var_max; // Z坐标方差范围
};
新增动作,只需在initTemplates()函数里加一行:
templates.push_back({"跳跃", 800, 1500, 200, 400, 5000, 20000});
然后在main.cc的识别逻辑里,把CNN输出的top-3类别,和点云模板匹配结果做加权融合(CNN置信度×0.7 + 模板匹配得分×0.3)。这样既保留了深度学习的泛化能力,又利用了几何规则的可解释性,扩展成本几乎为零。
6. 常见问题速查表与独家调试技巧
| 问题现象 | 可能原因 | 快速验证方法 | 终极解决方案 |
|---|---|---|---|
StereoCalib.cpp编译报错'cv::Size' has no member named 'width' | OpenCV 2.4.9中cv::Size成员是width/height,但某些旧版头文件定义为width_/height_ | 在StereoCalib.cpp开头加#define CV_VERSION_EPOCH 2 | 替换所有size.width为size.width_,共3处 |
Out/depth_*.png有明显条纹噪声 | SGBM的P1/P2参数过小,平滑不足 | 用cv::imshow()直接显示disp矩阵,观察噪声形态 | 将P1从128调至216,P2从512调至864 |
main.cc运行后黑屏无输出 | cv::VideoCapture未正确打开摄像头 | 在main.cc里加if(!cap_left.isOpened()) printf("Left cam failed\n"); | 检查USB线是否松动,或换用DirectShow后端:cv::VideoCapture cap_left(0 + CV_CAP_DSHOW) |
cnn1.dumped加载成功但识别结果全为0 | 模型权重数据类型不匹配(Keras导出为float64,C++读为float32) | 用十六进制编辑器打开cnn1.dumped,看前8字节是否为00 00 00 00 00 00 F0 3F(float32的1.0) | 修改keras_model.cc的fread()为fread((char*)weights, sizeof(float), num_weights, fp),强制按float32读 |
| 深度图边缘出现大面积白色区域 | 摄像头未对焦,导致部分区域无法匹配 | 用手电筒照棋盘格,看左右图哪个更模糊 | 用镜头调焦环手动对焦,或在StereoMatch.cpp里加cap_left.set(CV_CAP_PROP_FOCUS, 128)强制聚焦 |
test_run_cnn.cc能识别,但main.cc识别失败 | main.cc里未正确设置CNN输入blob的尺寸 | 在KerasModel::predict()里加printf("Input size: %d x %d\n", blob.size[2], blob.size[3]); | 确保blob = cv::dnn::blobFromImage(frame, 1.0/255.0, cv::Size(224,224)),尺寸必须是224×224 |
最后分享一个我压箱底的调试技巧:在main.cc的主循环里,加一段代码把每一帧的深度图、RGB图、点云高度、CNN输出概率,全部用cv::putText()叠在输出图像上,再cv::imwrite()保存。这样你不用开调试器,直接看Out/debug_001.jpg就能知道问题出在哪一环——是深度图坏了?还是点云分析错了?还是CNN推理崩了?这种“可视化调试法”,让我在客户现场30分钟内定位了90%的问题。毕竟,工程师不是靠猜,而是靠证据说话。
简介:基于双目摄像头输入,用C++实现人体10类动作(如挥手、蹲下、行走等)的端到端识别。工程内置OpenCV 2.4.9立体视觉模块,支持一键标定(StereoCalib.cpp)、极线校正(stereoRectify_eg.cpp)、SGBM深度图生成(StereoMatch.cpp)、轮廓与点云特征提取(StereoContour.cpp、PointCloudAnalyzer.cpp),以及双模运行:MATCH模式输出深度图,RECOGNITION模式加载预训练CNN模型做动作分类。模型由Python脚本HumanActionRecognitionTrain.py训练完成,导出为Keras格式whole_model.h5和C++兼容的cnn1.dumped,C++主程序main.cc可直接调用推理,结果保存至Out/目录;同时提供Python批量预测脚本HumanActionRecognitionPredictAll.py供验证对比。配套含标定图像(1.jpg/2.png/3.png)、训练效果截图(训练记录96%.jpg)、模型结构文件(whole_model_arch.)、项目说明文档(项目说明.md)及数据整理规范。所有OpenCV依赖已通过opencv249.props预配置,VS2015打开即编译,无需额外环境配置。

被折叠的 条评论
为什么被折叠?



