当前位置: 首页 > news >正文

C++,OpenCV,VS2015,HOG+SVM行人检测项目一整套,具体包括以下内容: 1...

C++,OpenCV,VS2015,HOG+SVM行人检测项目一整套,具体包括以下内容: 1.行人检测数据集,正负样本 2.数据集准备,模型训练,模型测试,视频测试和图片测试 3.界面,使用Qt搭建可视化交互界面 代码操作流程说明书,Word版

刚把毕设收尾的HOG+SVM行人检测项目翻出来整理了一遍,从数据集准备到Qt可视化界面全流程撸完了,用的是VS2015+OpenCV3.4.10,今天就把整个过程唠唠,踩过的坑和能用的代码片段都给你们扒出来,绝对不整那些干巴巴的论文套话。

首先得先搞数据集,这是最磨人的一步,别嫌麻烦。正样本就是纯行人的图,得统一缩成64*128像素,这是HOG特征要求的输入尺寸,我最开始偷懒没统一,结果训练的时候直接报错说特征维度不对,差点把我整崩溃。负样本就是不带行人的背景图,比如街道、草地、车流啥的,数量最好比正样本多一点,我当时是1:3的比例,大概2000张正样本配6000张负样本,负样本找的时候一定要仔细,别把行人混进去,不然训练出来的模型会瞎识别。

给你们看一段批量预处理正样本的代码,当时我写了个小脚本干这个活:

#include <opencv2/opencv.hpp> #include <iostream> #include <vector> #include <filesystem> // 划重点!VS2015默认不支持C++17的filesystem,记得去项目属性里把C++语言标准改成C++17或者更高,不然编译的时候会报错找不到头文件 namespace fs = std::filesystem; int main() { // 原始正样本文件夹和保存路径,记得提前建好 std::string src_path = "./raw_pos/"; std::string save_path = "./norm_pos/"; if(!fs::exists(save_path)) fs::create_directory(save_path); for(auto& entry : fs::directory_iterator(src_path)){ // 只处理jpg和png格式的图,其他的跳过 if(entry.path().extension() != ".jpg" && entry.path().extension() != ".png") continue; cv::Mat img = cv::imread(entry.path().string()); if(img.empty()) continue; cv::Mat resize_img; // 统一缩成64*128,刚好符合HOG的输入尺寸 cv::resize(img, resize_img, cv::Size(64,128)); std::string save_name = save_path + entry.path().filename().string(); cv::imwrite(save_name, resize_img); } std::cout << "正样本预处理搞定!" << std::endl; return 0; }

这个代码其实挺直白的,就是遍历文件夹里的所有图片,统一缩成要求的尺寸,负样本的预处理也是一样的逻辑,只是不用抠图,直接用原图缩就行,毕竟负样本只要尺寸对就行。

C++,OpenCV,VS2015,HOG+SVM行人检测项目一整套,具体包括以下内容: 1.行人检测数据集,正负样本 2.数据集准备,模型训练,模型测试,视频测试和图片测试 3.界面,使用Qt搭建可视化交互界面 代码操作流程说明书,Word版

接下来是模型训练,用OpenCV自带的HOG+SVM模块就行,不用自己从头写特征提取,省了好多事。我写了个封装好的特征提取函数,重点来了,所有参数必须和后面检测的时候完全一致,一个都不能改,不然特征维度对不上,模型直接废了:

#include <opencv2/opencv.hpp> #include <opencv2/ml.hpp> using namespace cv; using namespace cv::ml; // 提取单张图片的HOG特征 Mat getHogFeature(const Mat& img) { HOGDescriptor hog; // 这里的参数必须和检测时的参数完全一致! hog.winSize = Size(64,128); hog.blockSize = Size(16,16); hog.blockStride = Size(8,8); hog.cellSize = Size(8,8); hog.nbins = 9; std::vector<float> descriptors; hog.compute(img, descriptors, Size(8,8), Size(0,0)); Mat feature = Mat(descriptors).clone(); return feature; }

然后训练的主函数大概是这样的:

int trainModel() { // 先加载所有预处理好的正负样本,这里省略遍历文件夹读图片的代码,直接给个大概的逻辑 std::vector<Mat> pos_imgs, neg_imgs; // 读取norm_pos和norm_neg文件夹里的所有图片到vector里 Mat train_data, train_labels; // 正样本标签是1,负样本是-1 for(auto& img : pos_imgs){ Mat feat = getHogFeature(img); if(feat.empty()) continue; // 把每个样本的特征按行拼到训练集里 train_data.push_back(feat); train_labels.push_back(1.0); } for(auto& img : neg_imgs){ Mat feat = getHogFeature(img); if(feat.empty()) continue; train_data.push_back(feat); train_labels.push_back(-1.0); } // 初始化SVM分类器 Ptr<SVM> svm = SVM::create(); svm->setType(SVM::C_SVC); svm->setKernel(SVM::LINEAR); svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER | TermCriteria::EPS, 1000, 1e-6)); // 开始训练 svm->train(train_data, ROW_SAMPLE, train_labels); // 保存训练好的模型 svm->save("hog_svm_model.xml"); std::cout << "模型训练完成,已保存!" << std::endl; return 0; }

我当时第一次训练的时候就是改了blockStride的大小,结果检测的时候一个行人都没找不出来,调了快俩小时才发现是参数没对上,血的教训。

然后是模型测试,单张图片测试的代码就更简单了,直接加载模型,然后用HOG自带的detectMultiScale来检测,这个函数自带非极大值抑制,不用自己写,省了好多事:

// 单张图片检测 void detectSingleImg(const std::string& img_path) { Mat img = cv::imread(img_path); if(img.empty()){ std::cout << "图片打不开啊!" << std::endl; return; } // 加载训练好的模型 Ptr<SVM> svm = SVM::load("hog_svm_model.xml"); HOGDescriptor hog; // 参数必须和训练的时候完全一致! hog.winSize = Size(64,128); hog.blockSize = Size(16,16); hog.blockStride = Size(8,8); hog.cellSize = Size(8,8); hog.nbins =9; // 把SVM分类器加载到HOG里 hog.setSVMDetector(svm->getSupportVectors()); std::vector<Rect> found; // 检测行人框,参数可以自己调,比如缩放步长和置信度阈值 hog.detectMultiScale(img, found, 0, Size(8,8), Size(0,0), 1.05, 2); // 把检测到的框画在图上 for(auto& rect : found){ cv::rectangle(img, rect, cv::Scalar(0,255,0), 2); } cv::imshow("行人检测结果", img); cv::waitKey(0); }

视频检测的话就是用VideoCapture循环读每一帧,调用上面的检测逻辑就行:

// 视频检测,支持摄像头和本地视频文件 void detectVideo(const std::string& video_path) { cv::VideoCapture cap(video_path); if(!cap.isOpened()){ std::cout << "视频打不开!" << std::endl; return; } Ptr<SVM> svm = SVM::load("hog_svm_model.xml"); HOGDescriptor hog; // 同样设置参数 Mat frame; while(cap.read(frame)){ if(frame.empty()) break; std::vector<Rect> found; hog.detectMultiScale(frame, found, 0, Size(8,8), Size(0,0), 1.05, 2); for(auto& r : found) cv::rectangle(frame, r, cv::Scalar(0,255,0),2); cv::imshow("视频检测", frame); if(cv::waitKey(30)>=0) break; } }

然后是Qt界面的部分,这个是用来做可视化的,我当时做了个简单的界面,有选图片、选视频、检测的按钮,还有显示结果的标签,大概的Qt代码片段:

// Qt的MainWindow类里的槽函数,选图片按钮的部分 void MainWindow::on_btn_selectImg_clicked() { QString img_path = QFileDialog::getOpenFileName(this, "选择测试图片", "./", "图片文件(*.jpg *.png"); if(img_path.isEmpty()) return; // 显示原图 ui->label_origin->setPixmap(QPixmap(img_path)); // 调用检测函数,把结果转成QImage显示 Mat img = cv::imread(img_path.toStdString()); // 检测代码和之前的一样,然后把带框的图转成QImage QImage q_img = QImage(img.data, img.cols, img.rows, img.step, QImage::Format_RGB888); ui->label_result->setPixmap(QPixmap::fromImage(q_img.rgbSwapped())); }

搭Qt界面的时候还要注意把OpenCV的dll要放到Qt的编译输出目录里,不然运行的时候找不到,我当时就是把dll直接扔到了exe同目录下才解决的这个问题。

最后还整理了一份Word版的操作说明书,大概的目录是:

  1. 开发环境配置(VS2015配置OpenCV、Qt5.x配置VS2015)
  2. 数据集准备(正负样本收集、预处理代码)
  3. 模型训练代码使用说明)
  4. 单张/视频/图片测试代码使用说明)
  5. Qt界面搭建步骤)
  6. 常见问题解决(比如dll找不到、检测不出行人、特征维度不对)

其实这个项目虽然不算难,但是踩过的坑还是挺多的,比如一开始没统一样本尺寸、参数不一致、dll找不到这些,要是你们也在做这个项目的话,可以参考一下我上面的代码,有啥问题也可以在评论区唠唠。

http://www.jsqmd.com/news/566830/

相关文章:

  • Windows/Mac双平台实测:FORCE PRO 6.3.0求解器从注册到下载的完整配置流程
  • 智能资源解析引擎:跨平台网络资源获取解决方案
  • 企业采购Agent,如何保障落地效果?AI Agent驱动的采购数字化转型深度指南
  • 构建企业级工作流引擎:workflow-bpmn-modeler架构设计与实战应用
  • 别只盯着ChatGPT了!SpringAI工具调用帮你低成本打造专属‘AI员工’(避坑指南)
  • DMXAPI 搭配 Docker MCP Tool:大模型工具生态里最容易被忽视的执行层
  • ESP32传感器数据采集实战:从电位器到卡尔曼滤波的完整指南(附代码)
  • 3分钟夺回你的数字音乐资产:Unlock Music浏览器解密全攻略 [特殊字符]
  • 提升研发效率:基于快马平台快速定制与导出yolov8网络架构图
  • 2026年行业内口碑好的回收电缆导线服务口碑推荐,收购光缆/回收电力物资/回收互感器,回收电缆导线公司怎么选择 - 品牌推荐师
  • 从“单点防御”到“全局联动”:手把手教你用EDR和NDR构建企业安全闭环
  • DDR5内存新玩法:Write Pattern Command如何帮你省电30%?(附配置指南)
  • Web安全入门:从零开始搭建Pikachu靶场实战(附思维导图)
  • 中兴光猫配置解密工具:3步解锁家庭网络自主权
  • 告别依赖地狱:在Windows上用Qt Creator和MinGW编译GStreamer 1.16.2的完整避坑指南
  • 【5G核心网】5GC中SMF的会话管理与流量控制机制解析
  • 3步打造云播放革命:开源播放器解锁网盘视频自由
  • WireMock技术解构:从入门到架构师的实践指南
  • 5分钟快速打造智能虚拟助手:Live2D AI动画完整指南
  • 别再死磕状态机了!用Verilog实现I2C主从通信的另一种思路(附完整代码)
  • DSQC346G 3HAB8101-8 机器人伺服驱动单元
  • OpCore Simplify:重构黑苹果EFI配置的技术债务解决方案
  • LumiPixel赋能个性化营销:生成千人千面的广告代言人
  • Guohua Diffusion 跨风格融合实验:生成赛博朋克与古典园林结合的概念艺术
  • Snipaste高阶玩法:用贴图功能搭建临时多屏开发环境(附4K显示器适配指南)
  • 从零实现一个审批流引擎:状态机模式在OA系统中的最佳实践
  • 国产MCU实战:雅特力AT32F403A串口printf输出全攻略(基于V2库)
  • DOL-CHS-MODS:一站式游戏本地化与增强解决方案
  • 【2024最新】Polars 2.0清洗效率提升417%实测报告:从default配置到生产就绪配置的7阶演进路径
  • ATAC-seq实战:从BAM到TSS富集热图的ComputeMatrix全流程解析