C#+OpenCV+Yolo11n-pose使用示例
·
1、开发环境
<PackageReference Include="MathNet.Numerics" Version="5.0.0" />
<PackageReference Include="Microsoft.ML.OnnxRuntime" Version="1.22.1" />
<PackageReference Include="Microsoft.ML.OnnxRuntime.Gpu" Version="1.22.1" />
<PackageReference Include="OpenCvSharp4" Version="4.11.0.20250507" />
<PackageReference Include="OpenCvSharp4.runtime.win" Version="4.11.0.20250507" />

2、运行效果如下图

3、代码如下
using OpenCvSharp;
using OpenCvSharp.Dnn;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleAppYoloDemo
{
public class PoseEstimator
{
private Net _net;
private readonly float _confThreshold = 0.5f; // 目标检测置信度阈值
private readonly float _keypointThreshold = 0.3f; // 关键点置信度阈值
private readonly int _inputSize = 640; // 模型输入尺寸
// 人体骨架连接点定义(COCO格式17个关键点)
private static readonly int[,] Skeleton = {
{16, 14}, {14, 12}, {17, 15}, {15, 13}, {12, 13}, {6, 12}, {7, 13}, {6, 7},
{6, 8}, {7, 9}, {8, 10}, {9, 11}, {2, 3}, {1, 2}, {1, 3}, {2, 4}, {3, 5}, {4, 6}, {5, 7}
};
// 初始化模型
public void LoadModel(string modelPath)
{
_net = CvDnn.ReadNetFromOnnx(modelPath);
_net.SetPreferableBackend(Backend.OPENCV);
_net.SetPreferableTarget(Target.CPU);
}
// 执行姿态估计
public Mat EstimatePose(Mat image)
{
// 1. 预处理图像
Mat blob = CvDnn.BlobFromImage(
image: image,
1.0 / 255, // 归一化
size: new Size(_inputSize, _inputSize),
mean: new Scalar(0, 0, 0),
swapRB: true, // BGR转RGB
crop: false
);
// 2. 执行推理
_net.SetInput(blob);
Mat output = _net.Forward();
// 3. 后处理解析结果
var results = ProcessOutput(output, image.Width, image.Height);
// 4. 绘制关键点和骨架
return DrawPoses(image, results);
}
// 解析模型输出
private List<PoseDetection> ProcessOutput(Mat output, int origWidth, int origHeight)
{
var detections = new List<PoseDetection>();
float ratio = Math.Min(_inputSize / (float)origWidth, _inputSize / (float)origHeight);
// 输出维度: [1, 56, 8400] - 56 = 17*3 + 5 (5: x,y,w,h,conf)
for (int i = 0; i < output.Size(2); i++)
{
// 提取目标置信度
float confidence = output.At<float>(0, 4, i);
if (confidence < _confThreshold) continue;
// 解析边界框 (转换到原图坐标)
float cx = output.At<float>(0, 0, i) / ratio;
float cy = output.At<float>(0, 1, i) / ratio;
float w = output.At<float>(0, 2, i) / ratio;
float h = output.At<float>(0, 3, i) / ratio;
// 解析关键点 (17个关键点,每个点有x,y,conf)
var keypoints = new List<Point2f>();
var keypointConfs = new List<float>();
for (int k = 0; k < 17; k++)
{
int offset = 5 + k * 3;
float x = output.At<float>(0, offset, i) / ratio;
float y = output.At<float>(0, offset + 1, i) / ratio;
float conf = output.At<float>(0, offset + 2, i);
keypoints.Add(new Point2f(x, y));
keypointConfs.Add(conf);
}
detections.Add(new PoseDetection(
new Rect((int)(cx - w / 2), (int)(cy - h / 2), (int)w, (int)h),
confidence,
keypoints,
keypointConfs
));
}
// 应用非极大值抑制 (NMS)
return ApplyNMS(detections);
}
// 非极大值抑制
private List<PoseDetection> ApplyNMS(List<PoseDetection> detections, float iouThreshold = 0.5f)
{
var results = new List<PoseDetection>();
var boundingBoxes = detections.Select(d => d.BoundingBox).ToArray();
var confidences = detections.Select(d => d.Confidence).ToArray();
// 获取NMS索引
CvDnn.NMSBoxes(boundingBoxes, confidences, _confThreshold, iouThreshold, out int[] indices);
foreach (int idx in indices)
{
results.Add(detections[idx]);
}
return results;
}
// 绘制结果
private Mat DrawPoses(Mat image, List<PoseDetection> results)
{
Mat resultImg = image.Clone();
foreach (var pose in results)
{
// 绘制边界框
Cv2.Rectangle(resultImg, pose.BoundingBox, Scalar.Red, 2);
// 绘制关键点
for (int i = 0; i < pose.Keypoints.Count; i++)
{
if (pose.KeypointConfs[i] < _keypointThreshold) continue;
Cv2.Circle(resultImg, (Point)pose.Keypoints[i], 5,
new Scalar(0, 255, 0), -1); // 实心圆点
}
// 绘制骨架
for (int i = 0; i < Skeleton.GetLength(0); i++)
{
int startIdx = Skeleton[i, 0] - 1;
int endIdx = Skeleton[i, 1] - 1;
if (startIdx < pose.Keypoints.Count && endIdx < pose.Keypoints.Count &&
pose.KeypointConfs[startIdx] > _keypointThreshold &&
pose.KeypointConfs[endIdx] > _keypointThreshold)
{
Cv2.Line(resultImg,
(Point)pose.Keypoints[startIdx],
(Point)pose.Keypoints[endIdx],
new Scalar(0, 255, 255), 2);
}
}
}
return resultImg;
}
}
// 姿态检测结果类
public class PoseDetection
{
public Rect BoundingBox { get; }
public float Confidence { get; }
public List<Point2f> Keypoints { get; }
public List<float> KeypointConfs { get; }
public PoseDetection(Rect bbox, float conf, List<Point2f> kpts, List<float> kptConfs)
{
BoundingBox = bbox;
Confidence = conf;
Keypoints = kpts;
KeypointConfs = kptConfs;
}
}
}
using ConsoleAppYoloDemo;
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
using OpenCvSharp;
using OpenCvSharp.Dnn;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using Point = OpenCvSharp.Point;
using Size = OpenCvSharp.Size;
namespace YOLOv11Demo
{
class Program
{
static void Main(string[] args)
{
string modelPath = @"E:\Yolo\YoloDemo\ConsoleAppYoloDemo\bin\Debug\net6.0\yolo11n-pose.onnx";
string imagePath = @"E:\Yolo\YoloDemo\ConsoleAppYoloDemo\bin\Debug\net6.0\bus1.jpg";
string outputPath = @"E:\Yolo\YoloDemo\ConsoleAppYoloDemo\bin\Debug\net6.0\output.jpg";
// 验证文件存在
if (!File.Exists(modelPath))
{
Console.WriteLine($"Error: Model file not found at {modelPath}");
return;
}
if (!File.Exists(imagePath))
{
Console.WriteLine($"Error: Image file not found at {imagePath}");
return;
}
try
{
// 加载模型并处理图像
var estimator = new PoseEstimator();
estimator.LoadModel(modelPath);
using (var image = new Mat(imagePath))
{
Console.WriteLine($"Processing image: {Path.GetFileName(imagePath)}");
Console.WriteLine($"Image size: {image.Width}x{image.Height}");
var result = estimator.EstimatePose(image);
result.SaveImage(outputPath);
Console.WriteLine($"Results saved to: {outputPath}");
// 显示图像
Cv2.ImShow("Pose Estimation", result);
Cv2.WaitKey(0);
}
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
}
下载地址:
更多推荐
所有评论(0)