Skip to content

Commit

Permalink
Refactor resource provider (#27)
Browse files Browse the repository at this point in the history
* These pointers are C++ strings, not C strings

* Create a `DummyResourceManager` as an example

* Hide unsafe APIs

* Oops

* Using a `Span` results in segfaults :(

* Handle exceptions in `provideResource`

* Remove stray comment

* Document the delegates

Co-authored-by: Ayane Satomi <[email protected]>
  • Loading branch information
Speykious and sr229 authored Mar 11, 2022
1 parent ec44eb0 commit a099ea9
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 37 deletions.
26 changes: 26 additions & 0 deletions Mediapipe.Net.Examples.FaceMesh/DummyResourceManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) homuler and The Vignette Authors
// This file is part of MediaPipe.NET.
// MediaPipe.NET is licensed under the MIT License. See LICENSE for details.

using System;
using System.IO;
using Mediapipe.Net.Util;

namespace Mediapipe.Net.Examples.FaceMesh
{
public class DummyResourceManager : ResourceManager
{
public override PathResolver ResolvePath => (path) =>
{
Console.WriteLine($"PathResolver: (not) resolving path '{path}'");
return path;
};

public unsafe override ResourceProvider ProvideResource => (path) =>
{
Console.WriteLine($"ResourceProvider: providing resource '{path}'");
byte[] bytes = File.ReadAllBytes(path);
return bytes;
};
}
}
3 changes: 3 additions & 0 deletions Mediapipe.Net.Examples.FaceMesh/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,8 @@ public class Options

[Option('h', "height", Default = null, HelpText = "The height of the camera input")]
public int? Height { get; set; }

[Option("use-resource-manager", Default = false, HelpText = "Whether to use a resource manager.")]
public bool UseResourceManager { get; set; }
}
}
4 changes: 4 additions & 0 deletions Mediapipe.Net.Examples.FaceMesh/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Mediapipe.Net.External;
using Mediapipe.Net.Framework.Format;
using Mediapipe.Net.Framework.Protobuf;
using Mediapipe.Net.Util;
using SeeShark;
using SeeShark.Device;
using SeeShark.FFmpeg;
Expand All @@ -21,6 +22,7 @@ public static class Program
private static Camera? camera;
private static FrameConverter? converter;
private static FaceMeshCpuCalculator? calculator;
private static ResourceManager? resourceManager;

public static void Main(string[] args)
{
Expand All @@ -37,6 +39,8 @@ public static void Main(string[] args)

FFmpegManager.SetupFFmpeg("/usr/lib");
Glog.Initialize("stuff");
if (parsed.UseResourceManager)
resourceManager = new DummyResourceManager();

// Get a camera device
using (CameraManager manager = new CameraManager())
Expand Down
5 changes: 3 additions & 2 deletions Mediapipe.Net/Native/SafeNativeMethods/Utils/ResourceUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ namespace Mediapipe.Net.Native
{
internal unsafe partial class SafeNativeMethods : NativeMethods
{
public delegate bool UnsafeResourceProvider(string path, void* output);

[DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)]
public static extern void mp__SetCustomGlobalResourceProvider__P(
ResourceManager.ResourceProvider provider);
public static extern void mp__SetCustomGlobalResourceProvider__P(UnsafeResourceProvider provider);

[DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)]
public static extern void mp__SetCustomGlobalPathResolver__P(
Expand Down
4 changes: 2 additions & 2 deletions Mediapipe.Net/Native/UnsafeNativeMethods/External/Stdlib.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ internal unsafe partial class UnsafeNativeMethods : NativeMethods

#region String
[DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)]
public static extern void std_string__delete(sbyte* str);
public static extern void std_string__delete(void* str);

[DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)]
public static extern MpReturnCode std_string__PKc_i(byte[] bytes, int size, out sbyte* str);
public static extern MpReturnCode std_string__PKc_i(byte[] bytes, int size, out void* str);

[DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)]
public static extern void std_string__swap__Rstr(void* src, void* dst);
Expand Down
61 changes: 28 additions & 33 deletions Mediapipe.Net/Util/ResourceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,33 @@
// MediaPipe.NET is licensed under the MIT License. See LICENSE for details.

using System;
using System.Collections;
using System.IO;
using Mediapipe.Net.External;
using Mediapipe.Net.Native;

namespace Mediapipe.Net.Util
{
/// <summary>
/// Class to manage assets that MediaPipe accesses.
/// Class to manage MediaPipe resources, such as `.tflite` and `.pbtxt` files that it requests.
/// </summary>
/// <remarks>
/// There must not be more than one instance at the same time.
/// </remarks>
public unsafe abstract class ResourceManager
{
public delegate string PathResolver(string path);

/// <summary>
/// Resolves a path to a resource name.
/// If the resource name returned is different from the path, the <see cref="ResourceProvider" /> delegate will receive the resource name instead of the file path.
/// </summary>
public abstract PathResolver ResolvePath { get; }
public delegate bool ResourceProvider(string path, void* output);

/// <summary>
/// Reads a resource that MediaPipe requests.
/// </summary>
/// <param name="path">File path or name of the resource.</param>
/// <returns>Content of the MediaPipe resource as a byte array.</returns>
public delegate byte[] ResourceProvider(string path);
public abstract ResourceProvider ProvideResource { get; }

private static readonly object initLock = new object();
Expand All @@ -33,42 +43,27 @@ public ResourceManager()
throw new InvalidOperationException("ResourceManager can be initialized only once");

SafeNativeMethods.mp__SetCustomGlobalPathResolver__P(ResolvePath);
SafeNativeMethods.mp__SetCustomGlobalResourceProvider__P(ProvideResource);
SafeNativeMethods.mp__SetCustomGlobalResourceProvider__P(provideResource);
isInitialized = true;
}
}

/// <param name="name">Asset name</param>
/// <returns>
/// Returns true if <paramref name="name" /> is already prepared (saved locally on the device).
/// </returns>
public abstract bool IsPrepared(string name);

/// <summary>
/// Saves <paramref name="name" /> as <paramref name="uniqueKey" /> asynchronously.
/// </summary>
/// <param name="overwrite">
/// Specifies whether <paramref name="uniqueKey" /> will be overwritten if it already exists.
/// </param>
public abstract IEnumerator PrepareAssetAsync(string name, string uniqueKey, bool overwrite = true);

public IEnumerator PrepareAssetAsync(string name, bool overwrite = true)
=> PrepareAssetAsync(name, name, overwrite);

protected static string GetAssetNameFromPath(string assetPath)
private bool provideResource(string path, void* output)
{
var assetName = Path.GetFileNameWithoutExtension(assetPath);
var extension = Path.GetExtension(assetPath);
try
{
byte[] bytes = ProvideResource(path);

StdString strOutput = new StdString(output, isOwner: false);
StdString strSpan = new StdString(bytes);
strOutput.Swap(strSpan);

switch (extension)
return true;
}
catch (Exception ex)
{
case ".binarypb":
case ".tflite":
return $"{assetName}.bytes";
case ".pbtxt":
return $"{assetName}.txt";
default:
return $"{assetName}{extension}";
Glog.Log(Glog.Severity.Error, $"Error while trying to provide resource '{path}': {ex}");
return false;
}
}
}
Expand Down

0 comments on commit a099ea9

Please sign in to comment.