using Mediapipe.Extension; using Mediapipe.TranMarshal; namespace Mediapipe.Tasks.Components.Containers; /// /// Defines classification results for a given classifier head. /// public readonly struct Classifications { /// /// The array of predicted categories, usually sorted by descending scores, /// e.g. from high to low probability. /// public readonly List Categories; /// /// The index of the classifier head (i.e. output tensor) these categories /// refer to. This is useful for multi-head models. /// public readonly int HeadIndex; /// /// The optional name of the classifier head, as provided in the TFLite Model /// Metadata [1] if present. This is useful for multi-head models. /// [1]: https://www.tensorflow.org/lite/convert/metadata /// public readonly string? HeadName; internal Classifications(List categories, int headIndex, string? headName) { Categories = categories; HeadIndex = headIndex; HeadName = headName; } public static Classifications CreateFrom(Proto.Classifications proto) { Classifications classifications = default; Copy(proto, ref classifications); return classifications; } public static Classifications CreateFrom(ClassificationList proto, int headIndex = 0, string? headName = null) { Classifications classifications = default; Copy(proto, headIndex, headName, ref classifications); return classifications; } public static void Copy(Proto.Classifications source, ref Classifications destination) { List categories = destination.Categories ?? new List(source.ClassificationList.Classification.Count); categories.Clear(); for (int i = 0; i < source.ClassificationList.Classification.Count; i++) categories.Add(Category.CreateFrom(source.ClassificationList.Classification[i])); destination = new Classifications(categories, source.HeadIndex, source.HasHeadName ? source.HeadName : null); } public static void Copy(ClassificationList source, int headIndex, string? headName, ref Classifications destination) { List categories = destination.Categories ?? new List(source.Classification.Count); categories.Clear(); for (int i = 0; i < source.Classification.Count; i++) categories.Add(Category.CreateFrom(source.Classification[i])); destination = new Classifications(categories, headIndex, headName); } public static void Copy(ClassificationList source, ref Classifications destination) { Copy(source, 0, null, ref destination); } internal static void Copy(NativeClassifications source, ref Classifications destination) { List categories = destination.Categories ?? new List((int)source.categoriesCount); categories.Clear(); foreach (NativeCategory nativeCategory in source.Categories) categories.Add(new Category(nativeCategory)); destination = new Classifications(categories, source.headIndex, source.HeadName); } public override string ToString() { return $"{{ \"categories\": {Util.Format(Categories)}, \"headIndex\": {HeadIndex}, \"headName\": {Util.Format(HeadName)} }}"; } } /// /// Defines classification results of a model. /// public readonly struct ClassificationResult { /// /// The classification results for each head of the model. /// public readonly List classifications; /// /// The optional timestamp (in milliseconds) of the start of the chunk of data /// corresponding to these results. /// This is only used for classification on time series (e.g. audio /// classification). In these use cases, the amount of data to process might /// exceed the maximum size that the model can process: to solve this, the /// input data is split into multiple chunks starting at different timestamps. /// public readonly long? timestampMs; internal ClassificationResult(List classifications, long? timestampMs) { this.classifications = classifications; this.timestampMs = timestampMs; } public static ClassificationResult Alloc(int capacity) { return new ClassificationResult(new List(capacity), null); } public static ClassificationResult CreateFrom(Proto.ClassificationResult proto) { ClassificationResult classificationResult = default; Copy(proto, ref classificationResult); return classificationResult; } public static void Copy(Proto.ClassificationResult source, ref ClassificationResult destination) { List classifications = destination.classifications ?? new List(source.Classifications.Count); for (int i = 0; i < source.Classifications.Count; i++) classifications.Add(Classifications.CreateFrom(source.Classifications[i])); destination = new ClassificationResult(classifications, source.HasTimestampMs ? source.TimestampMs : null); } internal static void Copy(NativeClassificationResult source, ref ClassificationResult destination) { List classificationsList = destination.classifications ?? new List((int)source.classificationsCount); classificationsList.ResizeTo((int)source.classificationsCount); int i = 0; foreach (NativeClassifications nativeClassifications in source.Classifications) { Classifications classifications = classificationsList[i]; Classifications.Copy(nativeClassifications, ref classifications); classificationsList[i++] = classifications; } destination = new ClassificationResult(classificationsList, source.hasTimestampMs ? source.timestampMs : null); } public override string ToString() { return $"{{ \"classifications\": {Util.Format(classifications)}, \"timestampMs\": {Util.Format(timestampMs)} }}"; } }