1. 项目背景与核心目标
最近在Windows平台上部署YOLOv6-Pose模型时,发现很多开发者遇到环境配置和接口调用的兼容性问题。这个项目主要解决如何在Windows系统下,使用C++配合CMake构建工具,通过ONNX Runtime和OpenCV实现YOLOv6-Pose姿态估计模型的完整部署流程。
YOLOv6-Pose是YOLO系列中专门用于人体姿态估计的模型,相比常规目标检测,它需要处理的关键点更多(通常17个关键点),对推理速度和精度都有更高要求。选择ONNX Runtime作为推理引擎,主要考虑其跨平台性和对ONNX模型的原生支持,而OpenCV则负责图像预处理和后处理中的各种矩阵操作。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与工具链配置
2.1 基础环境要求
- Windows 10/11 64位系统
- Visual Studio 2019/2022(建议使用Community版)
- CMake 3.20+(必须支持FindPackage标准)
- Git(用于克隆必要仓库)
2.2 关键依赖安装
首先需要安装ONNX Runtime和OpenCV的Windows版本。建议使用vcpkg进行管理:
bash复制vcpkg install opencv[contrib]:x64-windows
vcpkg install onnxruntime:x64-windows
对于没有使用vcpkg的情况,可以手动下载预编译包:
- OpenCV: 从官网下载Windows pack
- ONNX Runtime: 从GitHub Release页面下载Windows版本
注意:ONNX Runtime有GPU和CPU两个版本,如果使用GPU加速需要额外配置CUDA和cuDNN
2.3 CMake工程初始化
创建基本的CMake工程结构:
code复制yolo6pose_deploy/
├── CMakeLists.txt
├── include/
├── src/
└── models/
基础CMake配置示例:
cmake复制cmake_minimum_required(VERSION 3.20)
project(YOLOv6_Pose_Deploy)
set(CMAKE_CXX_STANDARD 17)
find_package(OpenCV REQUIRED)
find_package(ONNXRuntime REQUIRED)
add_executable(yolo_pose_demo src/main.cpp)
target_link_libraries(yolo_pose_demo
PRIVATE
${OpenCV_LIBS}
onnxruntime
)
3. 模型准备与转换
3.1 获取YOLOv6-Pose模型
可以从官方仓库获取预训练模型:
bash复制git clone https://github.com/meituan/YOLOv6
cd YOLOv6
python tools/export_onnx.py --weights yolov6s_pose.pt --img 640 --batch 1
3.2 ONNX模型优化
使用ONNX Runtime提供的优化工具:
python复制import onnxruntime as ort
from onnxruntime.tools import optimize_model
optimized_model = optimize_model("yolov6s_pose.onnx",
model_type='bert', # 即使不是BERT也适用
num_heads=8,
hidden_size=512)
optimized_model.save("yolov6s_pose_opt.onnx")
4. 核心推理实现
4.1 初始化ONNX Runtime会话
cpp复制#include <onnxruntime_cxx_api.h>
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "YOLOv6-Pose");
Ort::SessionOptions session_options;
session_options.SetIntraOpNumThreads(1);
session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL);
Ort::Session session(env, L"yolov6s_pose_opt.onnx", session_options);
4.2 输入输出处理
cpp复制// 获取输入输出信息
auto memory_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
std::vector<int64_t> input_shape = {1, 3, 640, 640};
std::vector<float> input_tensor_values(1*3*640*640);
// 使用OpenCV进行图像预处理
cv::Mat image = cv::imread("test.jpg");
cv::Mat resized, float_img;
cv::resize(image, resized, cv::Size(640, 640));
resized.convertTo(float_img, CV_32F, 1.0/255.0);
// 转换为NCHW格式并填充到input_tensor_values
// ... (具体实现代码)
4.3 执行推理
cpp复制Ort::Value input_tensor = Ort::Value::CreateTensor<float>(
memory_info,
input_tensor_values.data(),
input_tensor_values.size(),
input_shape.data(),
input_shape.size()
);
auto output_tensors = session.Run(
Ort::RunOptions{nullptr},
input_names.data(),
&input_tensor,
1,
output_names.data(),
output_names.size()
);
5. 后处理与关键点解析
5.1 解析模型输出
YOLOv6-Pose的输出通常包含:
- 边界框坐标 (xywh)
- 关键点坐标 (17个点,每个点包含x,y,visibility)
- 类别置信度
cpp复制struct PoseKeyPoint {
float x, y;
float visibility;
};
struct Detection {
cv::Rect bbox;
std::vector<PoseKeyPoint> keypoints;
float confidence;
};
std::vector<Detection> parse_output(
const float* output_data,
int output_size,
float conf_threshold=0.5
) {
std::vector<Detection> detections;
// 具体解析逻辑...
return detections;
}
5.2 可视化结果
使用OpenCV绘制关键点和骨骼连接:
cpp复制void draw_pose(cv::Mat& image, const std::vector<Detection>& detections) {
static const std::vector<std::pair<int,int>> skeleton = {
{0,1}, {1,2}, {2,3}, {3,4}, {1,5}, {5,6}, {6,7}, {1,8},
{8,9}, {9,10}, {10,11}, {8,12}, {12,13}, {13,14}
};
for (const auto& det : detections) {
// 绘制关键点
for (const auto& kp : det.keypoints) {
if (kp.visibility > 0.5) {
cv::circle(image, cv::Point(kp.x, kp.y), 3, cv::Scalar(0,255,0), -1);
}
}
// 绘制骨骼连接
for (const auto& [i,j] : skeleton) {
if (det.keypoints[i].visibility > 0.5 &&
det.keypoints[j].visibility > 0.5) {
cv::line(image,
cv::Point(det.keypoints[i].x, det.keypoints[i].y),
cv::Point(det.keypoints[j].x, det.keypoints[j].y),
cv::Scalar(255,0,0), 2);
}
}
}
}
6. 性能优化技巧
6.1 ONNX Runtime配置优化
cpp复制// 启用CUDA加速(如果可用)
OrtCUDAProviderOptions cuda_options;
cuda_options.device_id = 0;
session_options.AppendExecutionProvider_CUDA(cuda_options);
// 启用TensorRT加速(需要额外安装TensorRT)
OrtTensorRTProviderOptions trt_options;
trt_options.device_id = 0;
trt_options.trt_max_workspace_size = 1 << 30;
session_options.AppendExecutionProvider_TensorRT(trt_options);
6.2 多线程处理
cpp复制// 设置线程数
session_options.SetIntraOpNumThreads(std::thread::hardware_concurrency());
session_options.SetInterOpNumThreads(2);
// 异步推理实现
std::future<std::vector<Detection>> async_detect(
Ort::Session& session,
const cv::Mat& image
) {
return std::async(std::launch::async, [&] {
// 预处理和推理代码
return parse_output(...);
});
}
7. 常见问题与解决方案
7.1 模型加载失败
问题现象:Failed to load model yolov6s_pose.onnx
可能原因:
- ONNX模型文件路径错误
- ONNX Runtime版本不兼容
- 模型使用了不支持的算子
解决方案:
- 检查模型路径是否为绝对路径
- 使用
onnxruntime::GetAvailableProviders()确认执行提供者 - 使用
netron工具检查模型结构
7.2 推理结果异常
问题现象:关键点坐标明显错误
可能原因:
- 图像预处理未正确归一化
- 输出解析时维度顺序错误
- 模型输入尺寸不匹配
解决方案:
- 确认预处理是否与训练时一致(RGB/BGR、归一化范围)
- 打印输出张量形状验证:
cpp复制auto shape = output_tensors[0].GetTensorTypeAndShapeInfo().GetShape(); std::cout << "Output shape: "; for (auto d : shape) std::cout << d << " ";
7.3 内存泄漏问题
问题现象:长时间运行后内存持续增长
可能原因:
- ONNX Runtime会话未正确释放
- OpenCV矩阵未释放
- 输出张量未释放
解决方案:
- 使用RAII管理资源:
cpp复制struct ORTSessionDeleter { void operator()(Ort::Session* session) const { delete session; } }; using ORTSessionPtr = std::unique_ptr<Ort::Session, ORTSessionDeleter>; - 定期检查内存:
cpp复制#include <windows.h> SIZE_T get_memory_usage() { PROCESS_MEMORY_COUNTERS pmc; GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)); return pmc.WorkingSetSize; }
8. 工程化建议
8.1 封装为C++类
建议将核心功能封装为易用的类接口:
cpp复制class YOLOv6PoseDetector {
public:
YOLOv6PoseDetector(const std::string& model_path, bool use_gpu=false);
std::vector<Detection> detect(const cv::Mat& image);
void visualize(cv::Mat& image, const std::vector<Detection>& detections);
private:
Ort::Env env;
Ort::Session session;
// 其他成员变量...
};
8.2 添加单元测试
使用Google Test框架添加测试用例:
cpp复制TEST(YOLOv6PoseTest, BasicDetection) {
YOLOv6PoseDetector detector("models/yolov6s_pose.onnx");
cv::Mat test_image = cv::imread("test_data/single_person.jpg");
auto detections = detector.detect(test_image);
EXPECT_EQ(detections.size(), 1);
EXPECT_EQ(detections[0].keypoints.size(), 17);
}
8.3 性能监控
添加推理时间统计:
cpp复制class Timer {
public:
Timer() : start(std::chrono::high_resolution_clock::now()) {}
double elapsed() const {
auto end = std::chrono::high_resolution_clock::now();
return std::chrono::duration<double>(end - start).count();
}
private:
std::chrono::time_point<std::chrono::high_resolution_clock> start;
};
// 使用示例
Timer timer;
auto detections = detector.detect(image);
std::cout << "Inference time: " << timer.elapsed() << "s\n";
9. 扩展与进阶
9.1 支持视频流处理
cpp复制void process_video(const std::string& video_path, YOLOv6PoseDetector& detector) {
cv::VideoCapture cap(video_path);
cv::Mat frame;
while (cap.read(frame)) {
auto detections = detector.detect(frame);
detector.visualize(frame, detections);
cv::imshow("Pose Estimation", frame);
if (cv::waitKey(1) == 27) break;
}
}
9.2 添加3D姿态估计
结合MediaPipe的3D姿态估计方法:
cpp复制std::vector<cv::Point3f> estimate_3d_pose(
const std::vector<PoseKeyPoint>& keypoints_2d,
const cv::Mat& camera_matrix,
const cv::Mat& dist_coeffs
) {
// 2D到3D的转换逻辑
// 需要预先标定相机参数
}
9.3 模型量化加速
使用ONNX Runtime的量化工具:
bash复制python -m onnxruntime.quantization.preprocess \
--input yolov6s_pose.onnx \
--output yolov6s_pose_quant.onnx \
--opset 13
10. 完整CMake工程示例
以下是完整的CMakeLists.txt配置示例:
cmake复制cmake_minimum_required(VERSION 3.20)
project(YOLOv6_Pose_Deploy)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 查找依赖
find_package(OpenCV REQUIRED)
find_package(ONNXRuntime REQUIRED)
# 添加可执行文件
add_executable(yolo_pose_demo
src/main.cpp
src/detector.cpp
src/visualizer.cpp
)
target_include_directories(yolo_pose_demo
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_link_libraries(yolo_pose_demo
PRIVATE
${OpenCV_LIBS}
onnxruntime
)
# 添加测试
if(BUILD_TESTING)
enable_testing()
find_package(GTest REQUIRED)
add_executable(yolo_pose_tests
tests/test_detector.cpp
src/detector.cpp
)
target_link_libraries(yolo_pose_tests
PRIVATE
GTest::GTest
${OpenCV_LIBS}
onnxruntime
)
add_test(NAME YOLOv6PoseTests COMMAND yolo_pose_tests)
endif()
在Windows上构建时,建议使用Visual Studio的开发者命令提示符:
bash复制mkdir build
cd build
cmake .. -G "Visual Studio 16 2019" -A x64
cmake --build . --config Release
