using Mediapipe.Extension; using Mediapipe.TranMarshal; namespace Mediapipe.Tasks.Components.Containers; /// /// Represents one detected object in the object detector's results. /// public readonly struct DetectionResultItem { public const int DefaultCategoryIndex = -1; /// /// A list of objects. /// public readonly List Categories; /// /// The bounding box location. /// public readonly Rect BoundingBox; /// /// Optional list of keypoints associated with the detection. Keypoints /// represent interesting points related to the detection. For example, the /// keypoints represent the eye, ear and mouth from face detection model. Or /// in the template matching detection, e.g. KNIFT, they can represent the /// feature points for template matching. /// public readonly List? Keypoints; internal DetectionResultItem(List categories, Rect boundingBox, List? keypoints) { Categories = categories; BoundingBox = boundingBox; Keypoints = keypoints; } public static DetectionResultItem CreateFrom(Detection proto) { DetectionResultItem result = default; Copy(proto, ref result); return result; } public static void Copy(Detection proto, ref DetectionResultItem destination) { List categories = destination.Categories ?? new List(proto.Score.Count); categories.Clear(); for (int idx = 0; idx < proto.Score.Count; idx++) categories.Add(new Category( proto.LabelId.Count > idx ? proto.LabelId[idx] : DefaultCategoryIndex, proto.Score[idx], proto.Label.Count > idx ? proto.Label[idx] : "", proto.DisplayName.Count > idx ? proto.DisplayName[idx] : "" )); Rect boundingBox = proto.LocationData != null ? new Rect( proto.LocationData.BoundingBox.Xmin, proto.LocationData.BoundingBox.Ymin, proto.LocationData.BoundingBox.Xmin + proto.LocationData.BoundingBox.Width, proto.LocationData.BoundingBox.Ymin + proto.LocationData.BoundingBox.Height ) : new Rect(0, 0, 0, 0); if (proto.LocationData?.RelativeKeypoints.Count == 0) { destination = new DetectionResultItem(categories, boundingBox, null); return; } List keypoints = destination.Keypoints ?? new List(proto.LocationData?.RelativeKeypoints.Count ?? 0); keypoints.Clear(); for (int i = 0; i < proto.LocationData?.RelativeKeypoints.Count; i++) { LocationData.Types.RelativeKeypoint? keypoint = proto.LocationData.RelativeKeypoints[i]; keypoints.Add(new NormalizedKeypoint( keypoint.X, keypoint.Y, keypoint.HasKeypointLabel ? keypoint.KeypointLabel : null!, keypoint.HasScore ? keypoint.Score : null )); } destination = new DetectionResultItem(categories, boundingBox, keypoints); } internal static void Copy(in NativeDetection source, ref DetectionResultItem destination) { List categories = destination.Categories ?? new List((int)source.categoriesCount); categories.Clear(); foreach (NativeCategory nativeCategory in source.Categories) categories.Add(new Category(nativeCategory)); Rect boundingBox = new(source.boundingBox); List keypoints = destination.Keypoints ?? new List((int)source.keypointsCount); keypoints.Clear(); foreach (NativeNormalizedKeypoint nativeKeypoint in source.Keypoints) keypoints.Add(new NormalizedKeypoint(nativeKeypoint)); destination = new DetectionResultItem(categories, boundingBox, keypoints); } public override string ToString() { return $"{{ \"categories\": {Util.Format(Categories)}, \"boundingBox\": {BoundingBox}, \"keypoints\": {Util.Format(Keypoints)} }}"; } } /// /// Represents the list of detected objects. /// public readonly struct DetectionResult { /// /// A list of objects. /// public readonly List Detections; internal DetectionResult(List detections) { Detections = detections; } public static DetectionResult Alloc(int capacity) { return new DetectionResult(new List(capacity)); } public static DetectionResult CreateFrom(List detectionsProto) { DetectionResult result = default; Copy(detectionsProto, ref result); return result; } public static void Copy(List source, ref DetectionResult destination) { List detections = destination.Detections ?? new List(source.Count); detections.ResizeTo(source.Count); for (int i = 0; i < source.Count; i++) { DetectionResultItem detection = detections[i]; DetectionResultItem.Copy(source[i], ref detection); detections[i] = detection; } destination = new DetectionResult(detections); } internal static void Copy(NativeDetectionResult source, ref DetectionResult destination) { List detections = destination.Detections ?? new List((int)source.detectionsCount); detections.ResizeTo((int)source.detectionsCount); int i = 0; foreach (NativeDetection nativeDetection in source.AsReadOnlySpan()) { DetectionResultItem detection = detections[i]; DetectionResultItem.Copy(nativeDetection, ref detection); detections[i++] = detection; } destination = new DetectionResult(detections); } public override string ToString() { return $"{{ \"detections\": {Util.Format(Detections)} }}"; } }