using OpenCvSharp.Dnn; using OpenCvSharp; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Sdcb.OpenVINO; namespace TeamAAS_VP.Core { public class DetectionResult { public int ClassId { get; } public string Class { get; } public Rect Rect { get; } public float Confidence { get; } public DetectionResult(int classId, string @class, Rect rect, float confidence) { ClassId = classId; Class = @class; Rect = rect; Confidence = confidence; } public static DetectionResult[] FromYolov8DetectionResult(ReadOnlySpan tensorData, Shape shape, Size2f sizeRatio, string[] dicts) { // tensorData: 1x84x8400=705600xF32 // shape: 1x84x8400, 84=(x, y, width, height)+80 class confidences, 8400=possible object count(code should for loop 8400 first) float[] t = Transpose(tensorData, shape[1], shape[2]); List detResults = new List(); int objectCount = shape[2]; int clsRowCount = shape[1]; if (dicts.Length != clsRowCount - 4) throw new ArgumentException($"dicts length {dicts.Length} does not match shape cls row count{clsRowCount}."); for (int i = 0; i < objectCount; i++) { int startIdx = i * clsRowCount; ReadOnlySpan rectData = t.AsSpan().Slice(startIdx, 4); ReadOnlySpan confidenceInfo = t.AsSpan().Slice(startIdx + 4, clsRowCount - 4); int maxConfidenceClsId = IndexOfMax(confidenceInfo); float confidence = confidenceInfo[maxConfidenceClsId]; int centerX = (int)(rectData[0] * sizeRatio.Width); int centerY = (int)(rectData[1] * sizeRatio.Height); int width = (int)(rectData[2] * sizeRatio.Width); int height = (int)(rectData[3] * sizeRatio.Height); detResults.Add(new DetectionResult( maxConfidenceClsId, dicts[maxConfidenceClsId], new Rect(centerX - width / 2, centerY - height / 2, width, height), confidence)); } CvDnn.NMSBoxes(detResults.Select(x => x.Rect).ToList(), detResults.Select(x => x.Confidence).ToList(), scoreThreshold: 0.5f, nmsThreshold: 0.5f, out int[] indices); return detResults.Where((x, i) => indices.Contains(i)).ToArray(); } private static int IndexOfMax(ReadOnlySpan data) { if (data.Length == 0) throw new ArgumentException("The provided data span is null or empty."); // 初始化最大值及其索引 int maxIndex = 0; float maxValue = data[0]; // 遍历跨度查找最大值及其索引 for (int i = 1; i < data.Length; i++) { if (data[i] > maxValue) { maxValue = data[i]; maxIndex = i; } } // 返回最大值及其索引 return maxIndex; } private static unsafe float[] Transpose(ReadOnlySpan tensorData, int rows, int cols) { float[] transposedTensorData = new float[tensorData.Length]; fixed (float* pTensorData = tensorData) { fixed (float* pTransposedData = transposedTensorData) { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { // Index in the original tensor int index = i * cols + j; // Index in the transposed tensor int transposedIndex = j * rows + i; pTransposedData[transposedIndex] = pTensorData[index]; } } } } return transposedTensorData; } } }