DetectionResult.cs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. using OpenCvSharp.Dnn;
  2. using OpenCvSharp;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. using Sdcb.OpenVINO;
  9. namespace TeamAAS_VP.Core
  10. {
  11. public class DetectionResult
  12. {
  13. public int ClassId { get; }
  14. public string Class { get; }
  15. public Rect Rect { get; }
  16. public float Confidence { get; }
  17. public DetectionResult(int classId, string @class, Rect rect, float confidence)
  18. {
  19. ClassId = classId;
  20. Class = @class;
  21. Rect = rect;
  22. Confidence = confidence;
  23. }
  24. public static DetectionResult[] FromYolov8DetectionResult(ReadOnlySpan<float> tensorData, Shape shape, Size2f sizeRatio, string[] dicts)
  25. {
  26. // tensorData: 1x84x8400=705600xF32
  27. // shape: 1x84x8400, 84=(x, y, width, height)+80 class confidences, 8400=possible object count(code should for loop 8400 first)
  28. float[] t = Transpose(tensorData, shape[1], shape[2]);
  29. List<DetectionResult> detResults = new List<DetectionResult>();
  30. int objectCount = shape[2];
  31. int clsRowCount = shape[1];
  32. if (dicts.Length != clsRowCount - 4) throw new ArgumentException($"dicts length {dicts.Length} does not match shape cls row count{clsRowCount}.");
  33. for (int i = 0; i < objectCount; i++)
  34. {
  35. int startIdx = i * clsRowCount;
  36. ReadOnlySpan<float> rectData = t.AsSpan().Slice(startIdx, 4);
  37. ReadOnlySpan<float> confidenceInfo = t.AsSpan().Slice(startIdx + 4, clsRowCount - 4);
  38. int maxConfidenceClsId = IndexOfMax(confidenceInfo);
  39. float confidence = confidenceInfo[maxConfidenceClsId];
  40. int centerX = (int)(rectData[0] * sizeRatio.Width);
  41. int centerY = (int)(rectData[1] * sizeRatio.Height);
  42. int width = (int)(rectData[2] * sizeRatio.Width);
  43. int height = (int)(rectData[3] * sizeRatio.Height);
  44. detResults.Add(new DetectionResult(
  45. maxConfidenceClsId, dicts[maxConfidenceClsId],
  46. new Rect(centerX - width / 2, centerY - height / 2, width, height),
  47. confidence));
  48. }
  49. CvDnn.NMSBoxes(detResults.Select(x => x.Rect).ToList(), detResults.Select(x => x.Confidence).ToList(), scoreThreshold: 0.5f, nmsThreshold: 0.5f, out int[] indices);
  50. return detResults.Where((x, i) => indices.Contains(i)).ToArray();
  51. }
  52. private static int IndexOfMax(ReadOnlySpan<float> data)
  53. {
  54. if (data.Length == 0) throw new ArgumentException("The provided data span is null or empty.");
  55. // 初始化最大值及其索引
  56. int maxIndex = 0;
  57. float maxValue = data[0];
  58. // 遍历跨度查找最大值及其索引
  59. for (int i = 1; i < data.Length; i++)
  60. {
  61. if (data[i] > maxValue)
  62. {
  63. maxValue = data[i];
  64. maxIndex = i;
  65. }
  66. }
  67. // 返回最大值及其索引
  68. return maxIndex;
  69. }
  70. private static unsafe float[] Transpose(ReadOnlySpan<float> tensorData, int rows, int cols)
  71. {
  72. float[] transposedTensorData = new float[tensorData.Length];
  73. fixed (float* pTensorData = tensorData)
  74. {
  75. fixed (float* pTransposedData = transposedTensorData)
  76. {
  77. for (int i = 0; i < rows; i++)
  78. {
  79. for (int j = 0; j < cols; j++)
  80. {
  81. // Index in the original tensor
  82. int index = i * cols + j;
  83. // Index in the transposed tensor
  84. int transposedIndex = j * rows + i;
  85. pTransposedData[transposedIndex] = pTensorData[index];
  86. }
  87. }
  88. }
  89. }
  90. return transposedTensorData;
  91. }
  92. }
  93. }