Skip to content

Commit

Permalink
feat: port ClassificationResult and Landmark (#991)
Browse files Browse the repository at this point in the history
* feat: port ClassificationResult and Landmark

* fix: fix compile errors on Unity 2020.3.x
  • Loading branch information
homuler committed Aug 12, 2023
1 parent 915f22d commit 7252d53
Show file tree
Hide file tree
Showing 15 changed files with 449 additions and 16 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright (c) 2023 homuler
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

using System.Collections.Generic;

namespace Mediapipe.Tasks.Components.Containers
{
/// <summary>
/// Defines classification results for a given classifier head.
/// </summary>
public readonly struct Classifications
{
/// <summary>
/// The array of predicted categories, usually sorted by descending scores,
/// e.g. from high to low probability.
/// </summary>
public readonly IReadOnlyList<Category> categories;
/// <summary>
/// The index of the classifier head (i.e. output tensor) these categories
/// refer to. This is useful for multi-head models.
/// </summary>
public readonly int headIndex;
/// <summary>
/// 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
/// </summary>
public readonly string headName;

internal Classifications(IReadOnlyList<Category> categories, int headIndex, string headName)
{
this.categories = categories;
this.headIndex = headIndex;
this.headName = headName;
}

public static Classifications CreateFrom(Proto.Classifications proto)
{
var categories = new List<Category>(proto.ClassificationList.Classification.Count);
foreach (var classification in proto.ClassificationList.Classification)
{
categories.Add(Category.CreateFrom(classification));
}
return new Classifications(categories, proto.HeadIndex, proto.HasHeadName ? proto.HeadName : null);
}

public static Classifications CreateFrom(ClassificationList proto, int headIndex = 0, string headName = null)
{
var categories = new List<Category>(proto.Classification.Count);
foreach (var classification in proto.Classification)
{
categories.Add(Category.CreateFrom(classification));
}
return new Classifications(categories, headIndex, headName);
}

public override string ToString()
=> $"{{ \"categories\": {Util.Format(categories)}, \"headIndex\": {headIndex}, \"headName\": {Util.Format(headName)} }}";
}

/// <summary>
/// Defines classification results of a model.
/// </summary>
public readonly struct ClassificationResult
{
/// <summary>
/// The classification results for each head of the model.
/// </summary>
public readonly IReadOnlyList<Classifications> classifications;

/// <summary>
/// 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.
/// </summary>
public readonly long? timestampMs;

internal ClassificationResult(IReadOnlyList<Classifications> classifications, long? timestampMs)
{
this.classifications = classifications;
this.timestampMs = timestampMs;
}

public static ClassificationResult CreateFrom(Proto.ClassificationResult proto)
{
var classifications = new List<Classifications>(proto.Classifications.Count);
foreach (var classification in proto.Classifications)
{
classifications.Add(Classifications.CreateFrom(classification));
}
#pragma warning disable IDE0004 // for Unity 2020.3.x
return new ClassificationResult(classifications, proto.HasTimestampMs ? (long?)proto.TimestampMs : null);
#pragma warning restore IDE0004 // for Unity 2020.3.x
}

public override string ToString() => $"{{ \"classifications\": {Util.Format(classifications)}, \"timestampMs\": {Util.Format(timestampMs)} }}";
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
// https://opensource.org/licenses/MIT.

using System.Collections.Generic;
using System.Linq;

namespace Mediapipe.Tasks.Components.Containers
{
Expand Down Expand Up @@ -81,11 +80,7 @@ public static Detection CreateFrom(Mediapipe.Detection proto)
}

public override string ToString()
{
var categoriesStr = $"[{string.Join(", ", categories.Select(category => category.ToString()))}]";
var keypointsStr = keypoints == null ? "null" : $"[{string.Join(", ", keypoints.Select(keypoint => keypoint.ToString()))}]";
return $"{{\"categories\": {categoriesStr}, \"boundingBox\": {boundingBox}, \"keypoints\": {keypointsStr}}}";
}
=> $"{{ \"categories\": {Util.Format(categories)}, \"boundingBox\": {boundingBox}, \"keypoints\": {Util.Format(keypoints)} }}";
}

/// <summary>
Expand Down Expand Up @@ -113,10 +108,6 @@ public static DetectionResult CreateFrom(IReadOnlyList<Mediapipe.Detection> dete
return new DetectionResult(detections);
}

public override string ToString()
{
var detectionsStr = string.Join(", ", detections.Select(detection => detection.ToString()));
return $"{{ \"detections\": [{detectionsStr}] }}";
}
public override string ToString() => $"{{ \"detections\": {Util.Format(detections)} }}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,6 @@ internal NormalizedKeypoint(float x, float y, string label, float? score)
this.score = score;
}

public override string ToString()
{
var scoreStr = score == null ? "null" : $"{score}";
return $"{{ \"x\": {x}, \"y\": {y}, \"label\": \"{label}\", \"score\": {scoreStr} }}";
}
public override string ToString() => $"{{ \"x\": {x}, \"y\": {y}, \"label\": \"{label}\", \"score\": {Util.Format(score)} }}";
}
}
Loading

0 comments on commit 7252d53

Please sign in to comment.