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}");
            }
        }
    }
}

下载地址:

https://download.csdn.net/download/xingchengaiwei/92113628

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐