From a099ea939f4e5bb6f601121de54508ce19123f69 Mon Sep 17 00:00:00 2001 From: Speykious Date: Fri, 11 Mar 2022 14:52:33 +0100 Subject: [PATCH] Refactor resource provider (#27) * 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 --- .../DummyResourceManager.cs | 26 ++++++++ Mediapipe.Net.Examples.FaceMesh/Options.cs | 3 + Mediapipe.Net.Examples.FaceMesh/Program.cs | 4 ++ .../SafeNativeMethods/Utils/ResourceUtil.cs | 5 +- .../UnsafeNativeMethods/External/Stdlib.cs | 4 +- Mediapipe.Net/Util/ResourceManager.cs | 61 +++++++++---------- 6 files changed, 66 insertions(+), 37 deletions(-) create mode 100644 Mediapipe.Net.Examples.FaceMesh/DummyResourceManager.cs diff --git a/Mediapipe.Net.Examples.FaceMesh/DummyResourceManager.cs b/Mediapipe.Net.Examples.FaceMesh/DummyResourceManager.cs new file mode 100644 index 00000000..78d197fb --- /dev/null +++ b/Mediapipe.Net.Examples.FaceMesh/DummyResourceManager.cs @@ -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; + }; + } +} diff --git a/Mediapipe.Net.Examples.FaceMesh/Options.cs b/Mediapipe.Net.Examples.FaceMesh/Options.cs index 1dbe41bb..dd8af3b2 100644 --- a/Mediapipe.Net.Examples.FaceMesh/Options.cs +++ b/Mediapipe.Net.Examples.FaceMesh/Options.cs @@ -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; } } } diff --git a/Mediapipe.Net.Examples.FaceMesh/Program.cs b/Mediapipe.Net.Examples.FaceMesh/Program.cs index 28fe04b2..81dfdbb9 100644 --- a/Mediapipe.Net.Examples.FaceMesh/Program.cs +++ b/Mediapipe.Net.Examples.FaceMesh/Program.cs @@ -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; @@ -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) { @@ -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()) diff --git a/Mediapipe.Net/Native/SafeNativeMethods/Utils/ResourceUtil.cs b/Mediapipe.Net/Native/SafeNativeMethods/Utils/ResourceUtil.cs index 324a0790..f83c345e 100644 --- a/Mediapipe.Net/Native/SafeNativeMethods/Utils/ResourceUtil.cs +++ b/Mediapipe.Net/Native/SafeNativeMethods/Utils/ResourceUtil.cs @@ -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( diff --git a/Mediapipe.Net/Native/UnsafeNativeMethods/External/Stdlib.cs b/Mediapipe.Net/Native/UnsafeNativeMethods/External/Stdlib.cs index bbebaf59..262f1055 100644 --- a/Mediapipe.Net/Native/UnsafeNativeMethods/External/Stdlib.cs +++ b/Mediapipe.Net/Native/UnsafeNativeMethods/External/Stdlib.cs @@ -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); diff --git a/Mediapipe.Net/Util/ResourceManager.cs b/Mediapipe.Net/Util/ResourceManager.cs index c305d332..b77d6af4 100644 --- a/Mediapipe.Net/Util/ResourceManager.cs +++ b/Mediapipe.Net/Util/ResourceManager.cs @@ -3,14 +3,13 @@ // 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 { /// - /// Class to manage assets that MediaPipe accesses. + /// Class to manage MediaPipe resources, such as `.tflite` and `.pbtxt` files that it requests. /// /// /// There must not be more than one instance at the same time. @@ -18,8 +17,19 @@ namespace Mediapipe.Net.Util public unsafe abstract class ResourceManager { public delegate string PathResolver(string path); + + /// + /// Resolves a path to a resource name. + /// If the resource name returned is different from the path, the delegate will receive the resource name instead of the file path. + /// public abstract PathResolver ResolvePath { get; } - public delegate bool ResourceProvider(string path, void* output); + + /// + /// Reads a resource that MediaPipe requests. + /// + /// File path or name of the resource. + /// Content of the MediaPipe resource as a byte array. + public delegate byte[] ResourceProvider(string path); public abstract ResourceProvider ProvideResource { get; } private static readonly object initLock = new object(); @@ -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; } } - /// Asset name - /// - /// Returns true if is already prepared (saved locally on the device). - /// - public abstract bool IsPrepared(string name); - - /// - /// Saves as asynchronously. - /// - /// - /// Specifies whether will be overwritten if it already exists. - /// - 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; } } }