diff --git a/.vscode/tasks.json b/.vscode/tasks.json index a31425b..ed3a6ef 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -12,6 +12,31 @@ "/consoleloggerparameters:NoSummary" ], "problemMatcher": "$msCompile" - } + }, + { + "label": ".NET Core Test with debugger (that one crashing test)", + "type": "process", + "isBackground": true, + "command": "dotnet", + "args": [ + "test", + "--logger", + "console;verbosity=detailed" + ], + "options": { + "cwd": "${workspaceFolder}/Mediapipe.Net.Tests", + "env": { + "VSTEST_HOST_DEBUG": "1" + }, + }, + "group": "test", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "shared" + }, + "problemMatcher": [] + }, ] } diff --git a/MediaPipe.NET.sln b/MediaPipe.NET.sln index 93cc30e..ec35b65 100644 --- a/MediaPipe.NET.sln +++ b/MediaPipe.NET.sln @@ -19,6 +19,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mediapipe.Net.Examples.Pose EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mediapipe.Net.Examples.OsuFrameworkVisualTests", "Mediapipe.Net.Examples.OsuFrameworkVisualTests\Mediapipe.Net.Examples.OsuFrameworkVisualTests.csproj", "{34F0FFCF-F81E-4556-BC74-ED9AEBA7F731}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mediapipe.Net.Examples.HandsGpu", "Mediapipe.Net.Examples.HandsGpu\Mediapipe.Net.Examples.HandsGpu.csproj", "{52AF90A3-E637-44F4-9FE8-15114829DD5D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -60,5 +62,9 @@ Global {34F0FFCF-F81E-4556-BC74-ED9AEBA7F731}.Debug|Any CPU.Build.0 = Debug|Any CPU {34F0FFCF-F81E-4556-BC74-ED9AEBA7F731}.Release|Any CPU.ActiveCfg = Release|Any CPU {34F0FFCF-F81E-4556-BC74-ED9AEBA7F731}.Release|Any CPU.Build.0 = Release|Any CPU + {52AF90A3-E637-44F4-9FE8-15114829DD5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {52AF90A3-E637-44F4-9FE8-15114829DD5D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {52AF90A3-E637-44F4-9FE8-15114829DD5D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {52AF90A3-E637-44F4-9FE8-15114829DD5D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Mediapipe.Net.Examples.FaceMesh/Mediapipe.Net.Examples.FaceMesh.csproj b/Mediapipe.Net.Examples.FaceMesh/Mediapipe.Net.Examples.FaceMesh.csproj index 97dba70..d320722 100644 --- a/Mediapipe.Net.Examples.FaceMesh/Mediapipe.Net.Examples.FaceMesh.csproj +++ b/Mediapipe.Net.Examples.FaceMesh/Mediapipe.Net.Examples.FaceMesh.csproj @@ -10,7 +10,7 @@ - + diff --git a/Mediapipe.Net.Examples.FaceMesh/Program.cs b/Mediapipe.Net.Examples.FaceMesh/Program.cs index 4c59afd..6463037 100644 --- a/Mediapipe.Net.Examples.FaceMesh/Program.cs +++ b/Mediapipe.Net.Examples.FaceMesh/Program.cs @@ -85,7 +85,7 @@ namespace Mediapipe.Net.Examples.FaceMesh converter ??= new FrameConverter(frame, PixelFormat.Rgba); Frame cFrame = converter.Convert(frame); - using ImageFrame imgframe = new ImageFrame(ImageFormat.Srgba, + using ImageFrame imgframe = new ImageFrame(ImageFormat.Types.Format.Srgba, cFrame.Width, cFrame.Height, cFrame.WidthStep, cFrame.RawData); List? landmarks = calculator.Compute(imgframe); diff --git a/Mediapipe.Net.Examples.FaceMeshGpu/Mediapipe.Net.Examples.FaceMeshGpu.csproj b/Mediapipe.Net.Examples.FaceMeshGpu/Mediapipe.Net.Examples.FaceMeshGpu.csproj index c0f730a..81d857b 100644 --- a/Mediapipe.Net.Examples.FaceMeshGpu/Mediapipe.Net.Examples.FaceMeshGpu.csproj +++ b/Mediapipe.Net.Examples.FaceMeshGpu/Mediapipe.Net.Examples.FaceMeshGpu.csproj @@ -10,7 +10,7 @@ - + diff --git a/Mediapipe.Net.Examples.FaceMeshGpu/Program.cs b/Mediapipe.Net.Examples.FaceMeshGpu/Program.cs index 72e375b..c088fca 100644 --- a/Mediapipe.Net.Examples.FaceMeshGpu/Program.cs +++ b/Mediapipe.Net.Examples.FaceMeshGpu/Program.cs @@ -79,7 +79,7 @@ namespace Mediapipe.Net.Examples.FaceMeshGpu converter ??= new FrameConverter(frame, PixelFormat.Rgba); Frame cFrame = converter.Convert(frame); - using ImageFrame imgframe = new ImageFrame(ImageFormat.Srgba, + using ImageFrame imgframe = new ImageFrame(ImageFormat.Types.Format.Srgba, cFrame.Width, cFrame.Height, cFrame.WidthStep, cFrame.RawData); List? landmarks = calculator.Compute(imgframe); diff --git a/Mediapipe.Net.Examples.Hands/Mediapipe.Net.Examples.Hands.csproj b/Mediapipe.Net.Examples.Hands/Mediapipe.Net.Examples.Hands.csproj index 97dba70..d320722 100644 --- a/Mediapipe.Net.Examples.Hands/Mediapipe.Net.Examples.Hands.csproj +++ b/Mediapipe.Net.Examples.Hands/Mediapipe.Net.Examples.Hands.csproj @@ -10,7 +10,7 @@ - + diff --git a/Mediapipe.Net.Examples.Hands/Program.cs b/Mediapipe.Net.Examples.Hands/Program.cs index 489d489..245f238 100644 --- a/Mediapipe.Net.Examples.Hands/Program.cs +++ b/Mediapipe.Net.Examples.Hands/Program.cs @@ -7,6 +7,7 @@ using CommandLine; using FFmpeg.AutoGen; using Mediapipe.Net.External; using Mediapipe.Net.Framework.Format; +using Mediapipe.Net.Framework.Protobuf; using Mediapipe.Net.Solutions; using Mediapipe.Net.Util; using SeeShark; @@ -93,7 +94,7 @@ namespace Mediapipe.Net.Examples.Hands converter ??= new FrameConverter(frame, PixelFormat.Rgba); Frame cFrame = converter.Convert(frame); - ImageFrame imgframe = new ImageFrame(ImageFormat.Srgba, + ImageFrame imgframe = new ImageFrame(ImageFormat.Types.Format.Srgba, cFrame.Width, cFrame.Height, cFrame.WidthStep, cFrame.RawData); HandsOutput handsOutput = calculator.Compute(imgframe); diff --git a/Mediapipe.Net.Examples.HandsGpu/Mediapipe.Net.Examples.HandsGpu.csproj b/Mediapipe.Net.Examples.HandsGpu/Mediapipe.Net.Examples.HandsGpu.csproj index c0f730a..81d857b 100644 --- a/Mediapipe.Net.Examples.HandsGpu/Mediapipe.Net.Examples.HandsGpu.csproj +++ b/Mediapipe.Net.Examples.HandsGpu/Mediapipe.Net.Examples.HandsGpu.csproj @@ -10,7 +10,7 @@ - + diff --git a/Mediapipe.Net.Examples.HandsGpu/Program.cs b/Mediapipe.Net.Examples.HandsGpu/Program.cs index 4e4099c..3514e34 100644 --- a/Mediapipe.Net.Examples.HandsGpu/Program.cs +++ b/Mediapipe.Net.Examples.HandsGpu/Program.cs @@ -8,6 +8,7 @@ using CommandLine; using FFmpeg.AutoGen; using Mediapipe.Net.External; using Mediapipe.Net.Framework.Format; +using Mediapipe.Net.Framework.Protobuf; using Mediapipe.Net.Solutions; using Mediapipe.Net.Util; using SeeShark; @@ -95,7 +96,7 @@ namespace Mediapipe.Net.Examples.HandsGpu converter ??= new FrameConverter(frame, PixelFormat.Rgba); Frame cFrame = converter.Convert(frame); - ImageFrame imgframe = new ImageFrame(ImageFormat.Srgba, + ImageFrame imgframe = new ImageFrame(ImageFormat.Types.Format.Srgba, cFrame.Width, cFrame.Height, cFrame.WidthStep, cFrame.RawData); HandsOutput handsOutput = calculator.Compute(imgframe); diff --git a/Mediapipe.Net.Examples.OsuFrameworkVisualTests/Mediapipe.Net.Examples.OsuFrameworkVisualTests.csproj b/Mediapipe.Net.Examples.OsuFrameworkVisualTests/Mediapipe.Net.Examples.OsuFrameworkVisualTests.csproj index 51188df..e3633f1 100644 --- a/Mediapipe.Net.Examples.OsuFrameworkVisualTests/Mediapipe.Net.Examples.OsuFrameworkVisualTests.csproj +++ b/Mediapipe.Net.Examples.OsuFrameworkVisualTests/Mediapipe.Net.Examples.OsuFrameworkVisualTests.csproj @@ -11,8 +11,8 @@ - - + + diff --git a/Mediapipe.Net.Examples.OsuFrameworkVisualTests/MediapipeDrawable.cs b/Mediapipe.Net.Examples.OsuFrameworkVisualTests/MediapipeDrawable.cs index 1fba685..537921d 100644 --- a/Mediapipe.Net.Examples.OsuFrameworkVisualTests/MediapipeDrawable.cs +++ b/Mediapipe.Net.Examples.OsuFrameworkVisualTests/MediapipeDrawable.cs @@ -18,6 +18,7 @@ using osu.Framework.Graphics.Textures; using SeeShark; using SeeShark.Device; using SixLabors.ImageSharp.PixelFormats; +using Anchor = osu.Framework.Graphics.Anchor; using Image = SixLabors.ImageSharp.Image; namespace Mediapipe.Net.Examples.OsuFrameworkVisualTests @@ -72,7 +73,7 @@ namespace Mediapipe.Net.Examples.OsuFrameworkVisualTests converter ??= new FrameConverter(frame, PixelFormat.Rgba); Frame cFrame = converter.Convert(frame); - using ImageFrame imgframe = new ImageFrame(ImageFormat.Srgba, + using ImageFrame imgframe = new ImageFrame(ImageFormat.Types.Format.Srgba, cFrame.Width, cFrame.Height, cFrame.WidthStep, cFrame.RawData); List? landmarkList = calculator.Compute(imgframe); diff --git a/Mediapipe.Net.Examples.Pose/Mediapipe.Net.Examples.Pose.csproj b/Mediapipe.Net.Examples.Pose/Mediapipe.Net.Examples.Pose.csproj index 97dba70..d320722 100644 --- a/Mediapipe.Net.Examples.Pose/Mediapipe.Net.Examples.Pose.csproj +++ b/Mediapipe.Net.Examples.Pose/Mediapipe.Net.Examples.Pose.csproj @@ -10,7 +10,7 @@ - + diff --git a/Mediapipe.Net.Examples.Pose/Program.cs b/Mediapipe.Net.Examples.Pose/Program.cs index 282774b..573cf74 100644 --- a/Mediapipe.Net.Examples.Pose/Program.cs +++ b/Mediapipe.Net.Examples.Pose/Program.cs @@ -7,6 +7,7 @@ using CommandLine; using FFmpeg.AutoGen; using Mediapipe.Net.External; using Mediapipe.Net.Framework.Format; +using Mediapipe.Net.Framework.Protobuf; using Mediapipe.Net.Solutions; using Mediapipe.Net.Util; using SeeShark; @@ -86,7 +87,7 @@ namespace Mediapipe.Net.Examples.Pose converter ??= new FrameConverter(frame, PixelFormat.Rgba); Frame cFrame = converter.Convert(frame); - ImageFrame imgframe = new ImageFrame(ImageFormat.Srgba, + ImageFrame imgframe = new ImageFrame(ImageFormat.Types.Format.Srgba, cFrame.Width, cFrame.Height, cFrame.WidthStep, cFrame.RawData); PoseOutput handsOutput = calculator.Compute(imgframe); diff --git a/Mediapipe.Net.Examples.PoseGpu/Mediapipe.Net.Examples.PoseGpu.csproj b/Mediapipe.Net.Examples.PoseGpu/Mediapipe.Net.Examples.PoseGpu.csproj index c0f730a..81d857b 100644 --- a/Mediapipe.Net.Examples.PoseGpu/Mediapipe.Net.Examples.PoseGpu.csproj +++ b/Mediapipe.Net.Examples.PoseGpu/Mediapipe.Net.Examples.PoseGpu.csproj @@ -10,7 +10,7 @@ - + diff --git a/Mediapipe.Net.Examples.PoseGpu/Program.cs b/Mediapipe.Net.Examples.PoseGpu/Program.cs index 348c29e..35202e5 100644 --- a/Mediapipe.Net.Examples.PoseGpu/Program.cs +++ b/Mediapipe.Net.Examples.PoseGpu/Program.cs @@ -7,6 +7,7 @@ using CommandLine; using FFmpeg.AutoGen; using Mediapipe.Net.External; using Mediapipe.Net.Framework.Format; +using Mediapipe.Net.Framework.Protobuf; using Mediapipe.Net.Solutions; using Mediapipe.Net.Util; using SeeShark; @@ -86,7 +87,7 @@ namespace Mediapipe.Net.Examples.PoseGpu converter ??= new FrameConverter(frame, PixelFormat.Rgba); Frame cFrame = converter.Convert(frame); - ImageFrame imgframe = new ImageFrame(ImageFormat.Srgba, + ImageFrame imgframe = new ImageFrame(ImageFormat.Types.Format.Srgba, cFrame.Width, cFrame.Height, cFrame.WidthStep, cFrame.RawData); PoseOutput handsOutput = calculator.Compute(imgframe); diff --git a/Mediapipe.Net.Tests/Framework/CalculatorGraphTest.cs b/Mediapipe.Net.Tests/Framework/CalculatorGraphTest.cs index 45a7694..ef83140 100644 --- a/Mediapipe.Net.Tests/Framework/CalculatorGraphTest.cs +++ b/Mediapipe.Net.Tests/Framework/CalculatorGraphTest.cs @@ -43,8 +43,8 @@ output_stream: ""out"" { using var graph = new CalculatorGraph(valid_config_text); var config = graph.Config(); - Assert.AreEqual(config.InputStream[0], "in"); - Assert.AreEqual(config.OutputStream[0], "out"); + Assert.AreEqual("in", config.InputStream[0]); + Assert.AreEqual("out", config.OutputStream[0]); } #endregion @@ -77,8 +77,8 @@ output_stream: ""out"" } var config = graph.Config(); - Assert.AreEqual(config.InputStream[0], "in"); - Assert.AreEqual(config.OutputStream[0], "out"); + Assert.AreEqual("in", config.InputStream[0]); + Assert.AreEqual("out", config.OutputStream[0]); } [Test] @@ -86,7 +86,7 @@ output_stream: ""out"" { using var graph = new CalculatorGraph(valid_config_text); using var status = graph.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(valid_config_text)); - Assert.AreEqual(status.Code, Status.StatusCode.Internal); + Assert.AreEqual(Status.StatusCode.Internal, status.Code); } [Test] @@ -112,7 +112,7 @@ output_stream: ""out"" var config = CalculatorGraphConfig.Parser.ParseFromTextFormat(valid_config_text); using var status = graph.Initialize(config, sidePackets); - Assert.AreEqual(status.Code, Status.StatusCode.Internal); + Assert.AreEqual(Status.StatusCode.Internal, status.Code); } #endregion @@ -137,7 +137,7 @@ output_stream: ""out"" using var graph = new CalculatorGraph(valid_config_text); Assert.True(graph.StartRun().Ok()); graph.Cancel(); - Assert.AreEqual(graph.WaitUntilDone().Code, Status.StatusCode.Cancelled); + Assert.AreEqual(Status.StatusCode.Cancelled, graph.WaitUntilDone().Code); } #endregion } diff --git a/Mediapipe.Net.Tests/Framework/Format/ImageFrameTest.cs b/Mediapipe.Net.Tests/Framework/Format/ImageFrameTest.cs index 65a89a1..5e1ce61 100644 --- a/Mediapipe.Net.Tests/Framework/Format/ImageFrameTest.cs +++ b/Mediapipe.Net.Tests/Framework/Format/ImageFrameTest.cs @@ -6,67 +6,60 @@ using System; using System.Linq; using Mediapipe.Net.Core; using Mediapipe.Net.Framework.Format; +using Mediapipe.Net.Framework.Protobuf; +using Mediapipe.Net.Tests; using NUnit.Framework; -namespace Mediapipe.Net.Tests.Framework.Format +namespace Mediapipe.Tests { public class ImageFrameTest { #region Constructor - [Test, SignalAbort] - public void Ctor_ShouldInstantiateImageFrame_When_CalledWithNoArguments() + [Test] + public unsafe void Ctor_ShouldInstantiateImageFrame_When_CalledWithNoArguments() { using var imageFrame = new ImageFrame(); -#pragma warning disable IDE0058 - Assert.AreEqual(imageFrame.Format, ImageFormat.Unknown); - Assert.AreEqual(imageFrame.Width, 0); - Assert.AreEqual(imageFrame.Height, 0); - // As these are now properties, i had to ToString() them so that they are run. - Assert.Throws(() => imageFrame.ChannelSize.ToString()); - Assert.Throws(() => imageFrame.NumberOfChannels.ToString()); - Assert.Throws(() => imageFrame.ByteDepth.ToString()); - Assert.AreEqual(imageFrame.WidthStep, 0); - Assert.AreEqual(imageFrame.PixelDataSize, 0); - Assert.Throws(() => imageFrame.PixelDataSizeStoredContiguously.ToString()); + Assert.AreEqual(ImageFormat.Types.Format.Unknown, imageFrame.Format); + Assert.AreEqual(0, imageFrame.Width); + Assert.AreEqual(0, imageFrame.Height, 0); + Assert.AreEqual(0, imageFrame.ChannelSize); + Assert.AreEqual(0, imageFrame.NumberOfChannels); + Assert.AreEqual(0, imageFrame.ByteDepth); + Assert.AreEqual(0, imageFrame.WidthStep); + Assert.AreEqual(0, imageFrame.PixelDataSize); + Assert.AreEqual(0, imageFrame.PixelDataSizeStoredContiguously); Assert.True(imageFrame.IsEmpty); Assert.False(imageFrame.IsContiguous); Assert.False(imageFrame.IsAligned(16)); - unsafe - { - Assert.True(imageFrame.MutablePixelData == null); - } -#pragma warning restore IDE0058 + Assert.True(imageFrame.MutablePixelData == null); } [Test] - public void Ctor_ShouldInstantiateImageFrame_When_CalledWithFormat() + public unsafe void Ctor_ShouldInstantiateImageFrame_When_CalledWithFormat() { - using var imageFrame = new ImageFrame(ImageFormat.Sbgra, 640, 480); - Assert.AreEqual(imageFrame.Format, ImageFormat.Sbgra); - Assert.AreEqual(imageFrame.Width, 640); - Assert.AreEqual(imageFrame.Height, 480); - Assert.AreEqual(imageFrame.ChannelSize, 1); - Assert.AreEqual(imageFrame.NumberOfChannels, 4); - Assert.AreEqual(imageFrame.ByteDepth, 1); - Assert.AreEqual(imageFrame.WidthStep, 640 * 4); - Assert.AreEqual(imageFrame.PixelDataSize, 640 * 480 * 4); - Assert.AreEqual(imageFrame.PixelDataSizeStoredContiguously, 640 * 480 * 4); + using var imageFrame = new ImageFrame(ImageFormat.Types.Format.Sbgra, 640, 480); + Assert.AreEqual(ImageFormat.Types.Format.Sbgra, imageFrame.Format); + Assert.AreEqual(640, imageFrame.Width); + Assert.AreEqual(480, imageFrame.Height); + Assert.AreEqual(1, imageFrame.ChannelSize); + Assert.AreEqual(4, imageFrame.NumberOfChannels); + Assert.AreEqual(1, imageFrame.ByteDepth); + Assert.AreEqual(640 * 4, imageFrame.WidthStep); + Assert.AreEqual(640 * 480 * 4, imageFrame.PixelDataSize); + Assert.AreEqual(640 * 480 * 4, imageFrame.PixelDataSizeStoredContiguously); Assert.False(imageFrame.IsEmpty); Assert.True(imageFrame.IsContiguous); Assert.True(imageFrame.IsAligned(16)); - unsafe - { - Assert.True(imageFrame.MutablePixelData != null); - } + Assert.True(imageFrame.MutablePixelData != null); } [Test] public void Ctor_ShouldInstantiateImageFrame_When_CalledWithFormatAndAlignmentBoundary() { - using var imageFrame = new ImageFrame(ImageFormat.Gray8, 100, 100, 8); - Assert.AreEqual(imageFrame.Width, 100); - Assert.AreEqual(imageFrame.NumberOfChannels, 1); - Assert.AreEqual(imageFrame.WidthStep, 104); + using var imageFrame = new ImageFrame(ImageFormat.Types.Format.Gray8, 100, 100, 8); + Assert.AreEqual(100, imageFrame.Width); + Assert.AreEqual(1, imageFrame.NumberOfChannels); + Assert.AreEqual(104, imageFrame.WidthStep); } [Test] @@ -77,71 +70,83 @@ namespace Mediapipe.Net.Tests.Framework.Format 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, }; - using var imageFrame = new ImageFrame(ImageFormat.Sbgra, 4, 2, 16, srcBytes); - Assert.AreEqual(imageFrame.Width, 4); - Assert.AreEqual(imageFrame.Height, 2); + using var imageFrame = new ImageFrame(ImageFormat.Types.Format.Sbgra, 4, 2, 16, srcBytes); + Assert.AreEqual(4, imageFrame.Width); + Assert.AreEqual(2, imageFrame.Height); Assert.False(imageFrame.IsEmpty); - byte[] bytes = imageFrame.CopyToByteBuffer(srcBytes.Length); + byte[] bytes = new byte[32]; + imageFrame.CopyToBuffer(bytes); Assert.IsEmpty(bytes.Where((x, i) => x != srcBytes[i])); } + [Test, SignalAbort] - public void Ctor_ShouldThrowMediapipeException_When_CalledWithInvalidArgument() + public void Ctor_ShouldThrowMediaPipeException_When_CalledWithInvalidArgument() { #pragma warning disable IDE0058 - Assert.Throws(() => { new ImageFrame(ImageFormat.Sbgra, 640, 480, 0); }); + Assert.Throws(() => { new ImageFrame(ImageFormat.Types.Format.Sbgra, 640, 480, 0); }); #pragma warning restore IDE0058 } #endregion - #region IsDisposed + #region #isDisposed [Test] public void IsDisposed_ShouldReturnFalse_When_NotDisposedYet() { - using var imageFrame = new ImageFrame(); + using ImageFrame imageFrame = new ImageFrame(); Assert.False(imageFrame.IsDisposed); } [Test] public void IsDisposed_ShouldReturnTrue_When_AlreadyDisposed() { - var imageFrame = new ImageFrame(); + ImageFrame imageFrame = new ImageFrame(); imageFrame.Dispose(); Assert.True(imageFrame.IsDisposed); } #endregion - #region SetToZero + #region #SetToZero [Test] public void SetToZero_ShouldSetZeroToAllBytes() { - using var imageFrame = new ImageFrame(ImageFormat.Gray8, 10, 10); - var origBytes = imageFrame.CopyToByteBuffer(100); - + using var imageFrame = new ImageFrame(ImageFormat.Types.Format.Gray8, 10, 10); imageFrame.SetToZero(); - var bytes = imageFrame.CopyToByteBuffer(100); + var bytes = new byte[100]; + imageFrame.CopyToBuffer(bytes); Assert.True(bytes.All((x) => x == 0)); } #endregion - #region SetAlignmentPaddingAreas + #region #SetAlignmentPaddingAreas [Test] public void SetAlignmentPaddingAreas_ShouldNotThrow() { - using var imageFrame = new ImageFrame(ImageFormat.Gray8, 10, 10, 16); + using var imageFrame = new ImageFrame(ImageFormat.Types.Format.Gray8, 10, 10, 16); Assert.DoesNotThrow(() => { imageFrame.SetAlignmentPaddingAreas(); }); } #endregion #region CopyToBuffer + [Test, SignalAbort] + public void CopyToByteBuffer_ShouldThrowException_When_BufferDepthIsWrong() + { + using var imageFrame = new ImageFrame(ImageFormat.Types.Format.Gray16, 10, 10); +#pragma warning disable IDE0058 + Assert.Throws(() => { imageFrame.CopyToBuffer(new byte[100]); }); +#pragma warning restore IDE0058 + } + [Test] public void CopyToByteBuffer_ShouldReturnByteArray_When_BufferSizeIsLargeEnough() { - using var imageFrame = new ImageFrame(ImageFormat.Gray8, 10, 10); - var normalBuffer = imageFrame.CopyToByteBuffer(100); - var largeBuffer = imageFrame.CopyToByteBuffer(120); + using var imageFrame = new ImageFrame(ImageFormat.Types.Format.Gray8, 10, 10); + var normalBuffer = new byte[100]; + var largeBuffer = new byte[120]; + imageFrame.CopyToBuffer(normalBuffer); + imageFrame.CopyToBuffer(largeBuffer); Assert.IsEmpty(normalBuffer.Where((x, i) => x != largeBuffer[i])); } @@ -149,18 +154,29 @@ namespace Mediapipe.Net.Tests.Framework.Format [Test, SignalAbort] public void CopyToByteBuffer_ShouldThrowException_When_BufferSizeIsTooSmall() { - using var imageFrame = new ImageFrame(ImageFormat.Gray8, 10, 10); + using var imageFrame = new ImageFrame(ImageFormat.Types.Format.Gray8, 10, 10); #pragma warning disable IDE0058 - Assert.Throws(() => { imageFrame.CopyToByteBuffer(99); }); + Assert.Throws(() => { imageFrame.CopyToBuffer(new byte[99]); }); +#pragma warning restore IDE0058 + } + + [Test, SignalAbort] + public void CopyToUshortBuffer_ShouldThrowException_When_BufferDepthIsWrong() + { + using var imageFrame = new ImageFrame(ImageFormat.Types.Format.Gray8, 10, 10); +#pragma warning disable IDE0058 + Assert.Throws(() => { imageFrame.CopyToBuffer(new ushort[100]); }); #pragma warning restore IDE0058 } [Test] public void CopyToUshortBuffer_ShouldReturnUshortArray_When_BufferSizeIsLargeEnough() { - using var imageFrame = new ImageFrame(ImageFormat.Gray16, 10, 10); - var normalBuffer = imageFrame.CopyToUshortBuffer(100); - var largeBuffer = imageFrame.CopyToUshortBuffer(120); + using var imageFrame = new ImageFrame(ImageFormat.Types.Format.Gray16, 10, 10); + var normalBuffer = new ushort[100]; + var largeBuffer = new ushort[120]; + imageFrame.CopyToBuffer(normalBuffer); + imageFrame.CopyToBuffer(largeBuffer); Assert.IsEmpty(normalBuffer.Where((x, i) => x != largeBuffer[i])); } @@ -168,18 +184,29 @@ namespace Mediapipe.Net.Tests.Framework.Format [Test, SignalAbort] public void CopyToUshortBuffer_ShouldThrowException_When_BufferSizeIsTooSmall() { - using var imageFrame = new ImageFrame(ImageFormat.Gray16, 10, 10); + using var imageFrame = new ImageFrame(ImageFormat.Types.Format.Gray16, 10, 10); #pragma warning disable IDE0058 - Assert.Throws(() => { imageFrame.CopyToUshortBuffer(99); }); + Assert.Throws(() => { imageFrame.CopyToBuffer(new ushort[99]); }); +#pragma warning restore IDE0058 + } + + [Test, SignalAbort] + public void CopyToFloatBuffer_ShouldThrowException_When_BufferDepthIsWrong() + { + using var imageFrame = new ImageFrame(ImageFormat.Types.Format.Gray8, 10, 10); +#pragma warning disable IDE0058 + Assert.Throws(() => { imageFrame.CopyToBuffer(new float[100]); }); #pragma warning restore IDE0058 } [Test] public void CopyToFloatBuffer_ShouldReturnFloatArray_When_BufferSizeIsLargeEnough() { - using var imageFrame = new ImageFrame(ImageFormat.Vec32f1, 10, 10); - var normalBuffer = imageFrame.CopyToFloatBuffer(100); - var largeBuffer = imageFrame.CopyToFloatBuffer(120); + using var imageFrame = new ImageFrame(ImageFormat.Types.Format.Vec32F1, 10, 10); + var normalBuffer = new float[100]; + var largeBuffer = new float[120]; + imageFrame.CopyToBuffer(normalBuffer); + imageFrame.CopyToBuffer(largeBuffer); Assert.IsEmpty(normalBuffer.Where((x, i) => Math.Abs(x - largeBuffer[i]) > 1e-9)); } @@ -187,11 +214,11 @@ namespace Mediapipe.Net.Tests.Framework.Format [Test, SignalAbort] public void CopyToFloatBuffer_ShouldThrowException_When_BufferSizeIsTooSmall() { - using var imageFrame = new ImageFrame(ImageFormat.Vec32f1, 10, 10); + using var imageFrame = new ImageFrame(ImageFormat.Types.Format.Vec32F1, 10, 10); #pragma warning disable IDE0058 - Assert.Throws(() => { imageFrame.CopyToFloatBuffer(99); }); + Assert.Throws(() => { imageFrame.CopyToBuffer(new float[99]); }); #pragma warning restore IDE0058 } - #endregion } + #endregion } diff --git a/Mediapipe.Net.Tests/Framework/Packets/ImageFramePacketTest.cs b/Mediapipe.Net.Tests/Framework/Packets/ImageFramePacketTest.cs index 46e0319..f2ab01e 100644 --- a/Mediapipe.Net.Tests/Framework/Packets/ImageFramePacketTest.cs +++ b/Mediapipe.Net.Tests/Framework/Packets/ImageFramePacketTest.cs @@ -6,6 +6,7 @@ using System; using Mediapipe.Net.Framework; using Mediapipe.Net.Framework.Format; using Mediapipe.Net.Framework.Packets; +using Mediapipe.Net.Framework.Protobuf; using NUnit.Framework; namespace Mediapipe.Net.Tests.Framework.NewPacket @@ -26,8 +27,8 @@ namespace Mediapipe.Net.Tests.Framework.NewPacket using var statusOrImageFrame = packet.ConsumeImageFrame(); Assert.True(statusOrImageFrame.Ok()); - using var imageFrame = statusOrImageFrame.Value(); - Assert.AreEqual(imageFrame.Format, ImageFormat.Unknown); + using ImageFrame? imageFrame = statusOrImageFrame.Value(); + Assert.AreEqual(imageFrame?.Format, ImageFormat.Types.Format.Unknown); } [Test] @@ -47,7 +48,7 @@ namespace Mediapipe.Net.Tests.Framework.NewPacket Assert.True(statusOrImageFrame.Ok()); using var imageFrame = statusOrImageFrame.Value(); - Assert.AreEqual(imageFrame.Format, ImageFormat.Unknown); + Assert.AreEqual(imageFrame?.Format, ImageFormat.Types.Format.Unknown); Assert.AreEqual(packet.Timestamp(), timestamp); } #endregion @@ -74,9 +75,9 @@ namespace Mediapipe.Net.Tests.Framework.NewPacket [Test] public void Get_ShouldReturnImageFrame_When_DataIsNotEmpty() { - using var packet = PacketFactory.ImageFramePacket(new ImageFrame(ImageFormat.Sbgra, 10, 10)); + using var packet = PacketFactory.ImageFramePacket(new ImageFrame(ImageFormat.Types.Format.Sbgra, 10, 10)); using var imageFrame = packet.GetImageFrame(); - Assert.AreEqual(imageFrame.Format, ImageFormat.Sbgra); + Assert.AreEqual(imageFrame.Format, ImageFormat.Types.Format.Sbgra); Assert.AreEqual(imageFrame.Width, 10); Assert.AreEqual(imageFrame.Height, 10); } @@ -86,14 +87,14 @@ namespace Mediapipe.Net.Tests.Framework.NewPacket [Test] public void Consume_ShouldReturnImageFrame() { - using var packet = PacketFactory.ImageFramePacket(new ImageFrame(ImageFormat.Sbgra, 10, 10)); + using var packet = PacketFactory.ImageFramePacket(new ImageFrame(ImageFormat.Types.Format.Sbgra, 10, 10)); using var statusOrImageFrame = packet.ConsumeImageFrame(); Assert.True(statusOrImageFrame.Ok()); - using var imageFrame = statusOrImageFrame.Value(); - Assert.AreEqual(imageFrame.Format, ImageFormat.Sbgra); - Assert.AreEqual(imageFrame.Width, 10); - Assert.AreEqual(imageFrame.Height, 10); + using ImageFrame? imageFrame = statusOrImageFrame.Value(); + Assert.AreEqual(imageFrame?.Format, ImageFormat.Types.Format.Sbgra); + Assert.AreEqual(imageFrame?.Width, 10); + Assert.AreEqual(imageFrame?.Height, 10); } #endregion diff --git a/Mediapipe.Net.Tests/Framework/Port/StatusOrGpuResourcesTest.cs b/Mediapipe.Net.Tests/Framework/Port/StatusOrGpuResourcesTest.cs index 15bfbf7..5fe64e1 100644 --- a/Mediapipe.Net.Tests/Framework/Port/StatusOrGpuResourcesTest.cs +++ b/Mediapipe.Net.Tests/Framework/Port/StatusOrGpuResourcesTest.cs @@ -15,7 +15,7 @@ namespace Mediapipe.Net.Tests.Framework.Port public void Status_ShouldReturnOk_When_StatusIsOk() { using var statusOrGpuResources = GpuResources.Create(); - Assert.AreEqual(statusOrGpuResources.Status.Code, Status.StatusCode.Ok); + Assert.AreEqual(Status.StatusCode.Ok, statusOrGpuResources.Status.Code); } #endregion diff --git a/Mediapipe.Net.Tests/Framework/Port/StatusOrImageFrameTest.cs b/Mediapipe.Net.Tests/Framework/Port/StatusOrImageFrameTest.cs index ad6991d..70ef1bc 100644 --- a/Mediapipe.Net.Tests/Framework/Port/StatusOrImageFrameTest.cs +++ b/Mediapipe.Net.Tests/Framework/Port/StatusOrImageFrameTest.cs @@ -6,6 +6,7 @@ using Mediapipe.Net.Framework; using Mediapipe.Net.Framework.Format; using Mediapipe.Net.Framework.Packets; using Mediapipe.Net.Framework.Port; +using Mediapipe.Net.Framework.Protobuf; using NUnit.Framework; namespace Mediapipe.Net.Tests.Framework.Port @@ -18,7 +19,7 @@ namespace Mediapipe.Net.Tests.Framework.Port { using var statusOrImageFrame = initializeSubject(); Assert.True(statusOrImageFrame.Ok()); - Assert.AreEqual(statusOrImageFrame.Status.Code, Status.StatusCode.Ok); + Assert.AreEqual(Status.StatusCode.Ok, statusOrImageFrame.Status.Code); } #endregion @@ -48,15 +49,15 @@ namespace Mediapipe.Net.Tests.Framework.Port Assert.True(statusOrImageFrame.Ok()); using var imageFrame = statusOrImageFrame.Value(); - Assert.AreEqual(imageFrame.Width, 10); - Assert.AreEqual(imageFrame.Height, 10); + Assert.AreEqual(10, imageFrame.Width); + Assert.AreEqual(10, imageFrame.Height); Assert.True(statusOrImageFrame.IsDisposed); } #endregion private static StatusOrImageFrame initializeSubject() { - var imageFrame = new ImageFrame(ImageFormat.Sbgra, 10, 10); + var imageFrame = new ImageFrame(ImageFormat.Types.Format.Sbgra, 10, 10); var packet = PacketFactory.ImageFramePacket(imageFrame, new Timestamp(1)); return (StatusOrImageFrame)packet.ConsumeImageFrame(); diff --git a/Mediapipe.Net.Tests/Framework/Port/StatusOrStringTest.cs b/Mediapipe.Net.Tests/Framework/Port/StatusOrStringTest.cs new file mode 100644 index 0000000..3290418 --- /dev/null +++ b/Mediapipe.Net.Tests/Framework/Port/StatusOrStringTest.cs @@ -0,0 +1,75 @@ +// 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 Mediapipe.Net.Framework.Packets; +using Mediapipe.Net.Framework.Port; +using NUnit.Framework; + +namespace Mediapipe.Net.Tests +{ + public class StatusOrStringTest + { + #region #status + [Test] + public void Status_ShouldReturnOk_When_StatusIsOk() + { + using var statusOrString = initializeSubject(""); + Assert.True(statusOrString.Ok()); + Assert.AreEqual(Status.StatusCode.Ok, statusOrString.Status.Code); + } + #endregion + + #region #isDisposed + [Test] + public void IsDisposed_ShouldReturnFalse_When_NotDisposedYet() + { + using var statusOrString = initializeSubject(""); + Assert.False(statusOrString.IsDisposed); + } + + [Test] + public void IsDisposed_ShouldReturnTrue_When_AlreadyDisposed() + { + var statusOrString = initializeSubject(""); + statusOrString.Dispose(); + + Assert.True(statusOrString.IsDisposed); + } + #endregion + + #region #Value + [Test] + public void Value_ShouldReturnString_When_StatusIsOk() + { + var bytes = new byte[] { (byte)'a', (byte)'b', 0, (byte)'c' }; + using var statusOrString = initializeSubject(bytes); + Assert.True(statusOrString.Ok()); + Assert.AreEqual("ab", statusOrString.Value()); + } + #endregion + + #region #ValueAsByteArray + [Test] + public void ValueAsByteArray_ShouldReturnByteArray_When_StatusIsOk() + { + var bytes = new byte[] { (byte)'a', (byte)'b', 0, (byte)'c' }; + using var statusOrString = initializeSubject(bytes); + Assert.True(statusOrString.Ok()); + Assert.AreEqual(bytes, statusOrString.ValueAsByteArray()); + } + #endregion + + private StatusOrString initializeSubject(string str) + { + using Packet packet = PacketFactory.StringPacket(str); + return (StatusOrString)packet.ConsumeString(); + } + + private StatusOrString initializeSubject(byte[] bytes) + { + using Packet packet = PacketFactory.StringPacket(bytes); + return (StatusOrString)packet.ConsumeString(); + } + } +} diff --git a/Mediapipe.Net.Tests/Framework/Port/StatusTest.cs b/Mediapipe.Net.Tests/Framework/Port/StatusTest.cs index b0f3c98..be1da76 100644 --- a/Mediapipe.Net.Tests/Framework/Port/StatusTest.cs +++ b/Mediapipe.Net.Tests/Framework/Port/StatusTest.cs @@ -15,14 +15,14 @@ namespace Mediapipe.Net.Tests.Framework.Port public void Code_ShouldReturnStatusCode_When_StatusIsOk() { using var status = Status.Ok(); - Assert.AreEqual(status.Code, Status.StatusCode.Ok); + Assert.AreEqual(Status.StatusCode.Ok, status.Code); } [Test] public void Code_ShouldReturnStatusCode_When_StatusIsFailedPrecondition() { using var status = Status.FailedPrecondition(); - Assert.AreEqual(status.Code, Status.StatusCode.FailedPrecondition); + Assert.AreEqual(Status.StatusCode.FailedPrecondition, status.Code); } #endregion @@ -49,14 +49,14 @@ namespace Mediapipe.Net.Tests.Framework.Port public void RawCode_ShouldReturnRawCode_When_StatusIsOk() { using var status = Status.Ok(); - Assert.AreEqual(status.RawCode, 0); + Assert.AreEqual(0, status.RawCode); } [Test] public void RawCode_ShouldReturnRawCode_When_StatusIsFailedPrecondition() { using var status = Status.FailedPrecondition(); - Assert.AreEqual(status.RawCode, 9); + Assert.AreEqual(9, status.RawCode); } #endregion @@ -108,8 +108,64 @@ namespace Mediapipe.Net.Tests.Framework.Port { var message = "Some error"; using var status = Status.FailedPrecondition(message); - Assert.AreEqual(status.ToString(), $"FAILED_PRECONDITION: {message}"); + Assert.AreEqual($"FAILED_PRECONDITION: {message}", status.ToString()); + } + + [Test] + public void ToString_ShouldReturnMessage_When_StatusIsAborted() + { + var message = "Some error"; + using Status status = Status.Aborted(message); + Assert.AreEqual($"ABORTED: {message}", status.ToString()); + } + + [Test] + public void ToString_ShouldReturnMessage_When_StatusIsOutOfRange() + { + string message = "Some error"; + using Status status = Status.OutOfRange(message); + Assert.AreEqual($"OUT_OF_RANGE: {message}", status.ToString()); + } + + [Test] + public void ToString_ShouldReturnMessage_When_StatusIsUnimplemented() + { + var message = "Some error"; + using Status status = Status.Unimplemented(message); + Assert.AreEqual($"UNIMPLEMENTED: {message}", status.ToString()); + } + + [Test] + public void ToString_ShouldReturnMessage_When_StatusIsInternal() + { + var message = "Some error"; + using Status status = Status.Internal(message); + Assert.AreEqual($"INTERNAL: {message}", status.ToString()); + } + + [Test] + public void ToString_ShouldReturnMessage_When_StatusIsUnavailable() + { + var message = "Some error"; + using Status status = Status.Unavailable(message); + Assert.AreEqual($"UNAVAILABLE: {message}", status.ToString()); + } + + [Test] + public void ToString_ShouldReturnMessage_When_StatusIsDataLoss() + { + var message = "Some error"; + using Status status = Status.DataLoss(message); + Assert.AreEqual($"DATA_LOSS: {message}", status.ToString()); + } + + [Test] + public void ToString_ShouldReturnMessage_When_StatusIsUnauthenticated() + { + var message = "Some error"; + using Status status = Status.Unauthenticated(message); + Assert.AreEqual($"UNAUTHENTICATED: {message}", status.ToString()); } - #endregion } + #endregion } diff --git a/Mediapipe.Net.Tests/Framework/TimestampTest.cs b/Mediapipe.Net.Tests/Framework/TimestampTest.cs index 5754b7d..13b2ecf 100644 --- a/Mediapipe.Net.Tests/Framework/TimestampTest.cs +++ b/Mediapipe.Net.Tests/Framework/TimestampTest.cs @@ -32,7 +32,7 @@ namespace Mediapipe.Net.Tests.Framework public void Value_ShouldReturnValue() { using var timestamp = new Timestamp(10); - Assert.AreEqual(timestamp.Value, 10); + Assert.AreEqual(10, timestamp.Value); } #endregion @@ -41,7 +41,7 @@ namespace Mediapipe.Net.Tests.Framework public void Seconds_ShouldReturnValueInSeconds() { using var timestamp = new Timestamp(1_000_000); - Assert.AreEqual(timestamp.Seconds, 1d, 1e-9); + Assert.AreEqual(1d, timestamp.Seconds, 1e-9); } #endregion @@ -50,7 +50,7 @@ namespace Mediapipe.Net.Tests.Framework public void Microseconds_ShouldReturnValueInMicroseconds() { using var timestamp = new Timestamp(1_000_000); - Assert.AreEqual(timestamp.Microseconds, 1_000_000); + Assert.AreEqual(1_000_000, timestamp.Microseconds); } #endregion @@ -142,14 +142,14 @@ namespace Mediapipe.Net.Tests.Framework public void DebugString_ShouldReturnDebugString() { using var timestamp = new Timestamp(1); - Assert.AreEqual(timestamp.DebugString, "1"); + Assert.AreEqual("1", timestamp.DebugString); } [Test] public void DebugString_ShouldReturnDebugString_When_TimestampIsUnset() { using var timestamp = Timestamp.Unset(); - Assert.AreEqual(timestamp.DebugString, "Timestamp::Unset()"); + Assert.AreEqual("Timestamp::Unset()", timestamp.DebugString); } #endregion @@ -159,7 +159,7 @@ namespace Mediapipe.Net.Tests.Framework { using var timestamp = new Timestamp(1); using var nextTimestamp = timestamp.NextAllowedInStream(); - Assert.AreEqual(nextTimestamp.Microseconds, 2); + Assert.AreEqual(2, nextTimestamp.Microseconds); } [Test] @@ -167,7 +167,7 @@ namespace Mediapipe.Net.Tests.Framework { using var timestamp = Timestamp.PostStream(); using var nextTimestamp = timestamp.NextAllowedInStream(); - Assert.AreEqual(nextTimestamp, Timestamp.OneOverPostStream()); + Assert.AreEqual(Timestamp.OneOverPostStream(), nextTimestamp); } #endregion @@ -177,7 +177,7 @@ namespace Mediapipe.Net.Tests.Framework { using var timestamp = new Timestamp(1); using var nextTimestamp = timestamp.PreviousAllowedInStream(); - Assert.AreEqual(nextTimestamp.Microseconds, 0); + Assert.AreEqual(0, nextTimestamp.Microseconds); } [Test] @@ -185,7 +185,7 @@ namespace Mediapipe.Net.Tests.Framework { using var timestamp = Timestamp.PreStream(); using var nextTimestamp = timestamp.PreviousAllowedInStream(); - Assert.AreEqual(nextTimestamp, Timestamp.Unstarted()); + Assert.AreEqual(Timestamp.Unstarted(), nextTimestamp); } #endregion @@ -194,7 +194,7 @@ namespace Mediapipe.Net.Tests.Framework public void FromSeconds_ShouldReturnTimestamp() { using var timestamp = Timestamp.FromSeconds(1d); - Assert.AreEqual(timestamp.Microseconds, 1_000_000); + Assert.AreEqual(1_000_000, timestamp.Microseconds); } #endregion } diff --git a/Mediapipe.Net.Tests/Framework/Tool/NameUtilTest.cs b/Mediapipe.Net.Tests/Framework/Tool/NameUtilTest.cs index a2957fd..395abbe 100644 --- a/Mediapipe.Net.Tests/Framework/Tool/NameUtilTest.cs +++ b/Mediapipe.Net.Tests/Framework/Tool/NameUtilTest.cs @@ -18,7 +18,7 @@ namespace Mediapipe.Net.Tests.Framework.Tool public void GetUnusedNodeName_ShouldReturnUniqueName(string configJson, string nameBase, string uniqueName) { var config = CalculatorGraphConfig.Parser.ParseJson(configJson); - Assert.AreEqual(GetUnusedNodeName(config, nameBase), uniqueName); + Assert.AreEqual(uniqueName, GetUnusedNodeName(config, nameBase)); } [TestCase("{}", "base", "base")] @@ -28,7 +28,7 @@ namespace Mediapipe.Net.Tests.Framework.Tool public void GetUnusedSidePacketName_ShouldReturnUniqueName(string configJson, string nameBase, string uniqueName) { var config = CalculatorGraphConfig.Parser.ParseJson(configJson); - Assert.AreEqual(GetUnusedSidePacketName(config, nameBase), uniqueName); + Assert.AreEqual(uniqueName, GetUnusedSidePacketName(config, nameBase)); } [TestCase(@"{""node"":[{""name"":""x""}]}", 0, "x")] @@ -43,7 +43,7 @@ namespace Mediapipe.Net.Tests.Framework.Tool public void CanonicalNodeName_ShouldReturnCanonicalNodeName_When_NodeIdIsValid(string configJson, int nodeId, string name) { var config = CalculatorGraphConfig.Parser.ParseJson(configJson); - Assert.AreEqual(CanonicalNodeName(config, nodeId), name); + Assert.AreEqual(name, CanonicalNodeName(config, nodeId)); } [Test] @@ -78,7 +78,7 @@ namespace Mediapipe.Net.Tests.Framework.Tool [TestCase("TAG:1:x", "x")] public void ParseNameFromStream_ShouldReturnName_When_InputIsValid(string stream, string name) { - Assert.AreEqual(ParseNameFromStream(stream), name); + Assert.AreEqual(name, ParseNameFromStream(stream)); } [TestCase(":stream")] @@ -99,8 +99,8 @@ namespace Mediapipe.Net.Tests.Framework.Tool { var output = ParseTagIndex(tagIndex); - Assert.AreEqual(output.Item1, tag); - Assert.AreEqual(output.Item2, index); + Assert.AreEqual(tag, output.Item1); + Assert.AreEqual(index, output.Item2); } [TestCase("tag")] @@ -121,8 +121,8 @@ namespace Mediapipe.Net.Tests.Framework.Tool { var output = ParseTagIndexFromStream(stream); - Assert.AreEqual(output.Item1, tag); - Assert.AreEqual(output.Item2, index); + Assert.AreEqual(tag, output.Item1); + Assert.AreEqual(index, output.Item2); } [TestCase(":stream")] @@ -141,7 +141,7 @@ namespace Mediapipe.Net.Tests.Framework.Tool [TestCase("TAG", 1, "TAG:1")] public void CatTag_ShouldReturnTag(string tag, int index, string output) { - Assert.AreEqual(CatTag(tag, index), output); + Assert.AreEqual(output, CatTag(tag, index)); } [TestCase("", -1, "x", "x")] @@ -150,7 +150,7 @@ namespace Mediapipe.Net.Tests.Framework.Tool [TestCase("TAG", 1, "x", "TAG:1:x")] public void CatStream_ShouldReturnStream(string tag, int index, string name, string output) { - Assert.AreEqual(CatStream((tag, index), name), output); + Assert.AreEqual(output, CatStream((tag, index), name)); } } } diff --git a/Mediapipe.Net.Tests/Framework/Tool/ValidateNameTest.cs b/Mediapipe.Net.Tests/Framework/Tool/ValidateNameTest.cs index d563693..df59c90 100644 --- a/Mediapipe.Net.Tests/Framework/Tool/ValidateNameTest.cs +++ b/Mediapipe.Net.Tests/Framework/Tool/ValidateNameTest.cs @@ -112,8 +112,8 @@ namespace Mediapipe.Net.Tests.Framework.Tool { ParseTagAndName(input, out var tag, out var name); - Assert.AreEqual(tag, expectedTag); - Assert.AreEqual(name, expectedName); + Assert.AreEqual(expectedTag, tag); + Assert.AreEqual(expectedName, name); } [TestCase(":humphrey")] @@ -128,8 +128,8 @@ namespace Mediapipe.Net.Tests.Framework.Tool #pragma warning disable IDE0058 Assert.Throws(() => { ParseTagAndName(input, out tag, out name); }); - Assert.AreEqual(tag, "UNTOUCHED"); - Assert.AreEqual(name, "untouched"); + Assert.AreEqual("UNTOUCHED", tag); + Assert.AreEqual("untouched", name); #pragma warning restore IDE0058 } @@ -156,9 +156,9 @@ namespace Mediapipe.Net.Tests.Framework.Tool { ParseTagIndexName(input, out var tag, out var index, out var name); - Assert.AreEqual(tag, expectedTag); - Assert.AreEqual(index, expectedIndex); - Assert.AreEqual(name, expectedName); + Assert.AreEqual(expectedTag, tag); + Assert.AreEqual(expectedIndex, index); + Assert.AreEqual(expectedName, name); } [TestCase("")] @@ -205,9 +205,9 @@ namespace Mediapipe.Net.Tests.Framework.Tool #pragma warning disable IDE0058 Assert.Throws(() => { ParseTagIndexName(input, out tag, out index, out name); }); - Assert.AreEqual(tag, "UNTOUCHED"); - Assert.AreEqual(index, -1); - Assert.AreEqual(name, "untouched"); + Assert.AreEqual("UNTOUCHED", tag); + Assert.AreEqual(-1, index); + Assert.AreEqual("untouched", name); #pragma warning restore IDE0058 } @@ -235,8 +235,8 @@ namespace Mediapipe.Net.Tests.Framework.Tool { ParseTagIndex(input, out var tag, out var index); - Assert.AreEqual(tag, expectedTag); - Assert.AreEqual(index, expectedIndex); + Assert.AreEqual(expectedTag, tag); + Assert.AreEqual(expectedIndex, index); } [TestCase("a")] @@ -270,8 +270,8 @@ namespace Mediapipe.Net.Tests.Framework.Tool #pragma warning disable IDE0058 Assert.Throws(() => { ParseTagIndex(input, out tag, out index); }); - Assert.AreEqual(tag, "UNTOUCHED"); - Assert.AreEqual(index, -1); + Assert.AreEqual("UNTOUCHED", tag); + Assert.AreEqual(-1, index); #pragma warning restore IDE0058 } diff --git a/Mediapipe.Net.Tests/Framework/ValidatedGraphConfigTest.cs b/Mediapipe.Net.Tests/Framework/ValidatedGraphConfigTest.cs new file mode 100644 index 0000000..50c103b --- /dev/null +++ b/Mediapipe.Net.Tests/Framework/ValidatedGraphConfigTest.cs @@ -0,0 +1,743 @@ +// 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.Linq; +using Mediapipe.Net.Framework; +using Mediapipe.Net.Framework.Packets; +using Mediapipe.Net.Framework.Port; +using Mediapipe.Net.Framework.Protobuf; +using Mediapipe.Net.Framework.ValidatedGraphConfig; +using NUnit.Framework; + +namespace Mediapipe.Net.Tests +{ + public class ValidatedGraphConfigTest + { + private const string pass_through_config_text = @" +node { + calculator: ""PassThroughCalculator"" + input_stream: ""in"" + output_stream: ""out1"" +} +node { + calculator: ""PassThroughCalculator"" + input_stream: ""out1"" + output_stream: ""out"" +} +input_stream: ""in"" +output_stream: ""out"" +"; + + private const string flow_limiter_config_text = @" +input_stream: ""input_video"" +input_stream: ""output"" + +node { + calculator: ""FlowLimiterCalculator"" + input_stream: ""input_video"" + input_stream: ""FINISHED:output"" + input_stream_info: { + tag_index: ""FINISHED"" + back_edge: true + } + input_side_packet: ""MAX_IN_FLIGHT:max_in_flight"" + input_side_packet: ""OPTIONS:flow_limiter_calculator_options"" + output_stream: ""throttled_input_video"" +} +"; + + private const string image_transformation_config_text = @" +input_stream: ""input_video"" + +node: { + calculator: ""ImageTransformationCalculator"" + input_stream: ""IMAGE:input_video"" + input_side_packet: ""ROTATION_DEGREES:input_rotation"" + input_side_packet: ""FLIP_HORIZONTALLY:input_horizontally_flipped"" + input_side_packet: ""FLIP_VERTICALLY:input_vertically_flipped"" + output_stream: ""IMAGE:transformed_input_video"" +} +"; + + private const string constant_side_packet_config_text = @" +node { + calculator: ""ConstantSidePacketCalculator"" + output_side_packet: ""PACKET:0:int_packet"" + output_side_packet: ""PACKET:1:float_packet"" + output_side_packet: ""PACKET:2:bool_packet"" + output_side_packet: ""PACKET:3:string_packet"" + options: { + [mediapipe.ConstantSidePacketCalculatorOptions.ext]: { + packet { int_value: 256 } + packet { float_value: 0.5f } + packet { bool_value: false } + packet { string_value: ""string"" } + } + } +} +"; + + private const string face_detection_short_range_common_config_text = @" +input_stream: ""detection_tensors"" +input_stream: ""transform_matrix"" + +node { + calculator: ""FaceDetectionShortRangeCommon"" + input_stream: ""TENSORS:detection_tensors"" + input_stream: ""MATRIX:transform_matrix"" + output_stream: ""DETECTIONS:detections"" +} +"; + + #region Constructor + [Test] + public void Ctor_ShouldInstantiateValidatedGraphConfig() + { + Assert.DoesNotThrow(() => + { + var config = new ValidatedGraphConfig(); + config.Dispose(); + }); + } + #endregion + + #region #IsDisposed + [Test] + public void IsDisposed_ShouldReturnFalse_When_NotDisposedYet() + { + using (var config = new ValidatedGraphConfig()) + { + Assert.False(config.IsDisposed); + } + } + + [Test] + public void IsDisposed_ShouldReturnTrue_When_AlreadyDisposed() + { + var config = new ValidatedGraphConfig(); + config.Dispose(); + + Assert.True(config.IsDisposed); + } + #endregion + + #region #Initialize + [Test] + public void Initialize_ShouldReturnOk_When_CalledWithConfig() + { + using (var config = new ValidatedGraphConfig()) + { + using (var status = config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(pass_through_config_text))) + { + Assert.True(status.Ok()); + } + Assert.True(config.Initialized()); + } + } + + [Test] + public void Initialize_ShouldReturnOk_When_CalledWithValidGraphType() + { + using (var config = new ValidatedGraphConfig()) + { + using (var status = config.Initialize("SwitchContainer")) + { + Assert.True(status.Ok()); + } + Assert.True(config.Initialized()); + } + } + + [Test] + public void Initialize_ShouldReturnInternalError_When_CalledWithInvalidGraphType() + { + using (var config = new ValidatedGraphConfig()) + { + using (var status = config.Initialize("InvalidSubgraph")) + { + Assert.AreEqual(Status.StatusCode.NotFound, status.Code); + } + Assert.False(config.Initialized()); + } + } + #endregion + + #region #ValidateRequiredSidePackets + [Test] + public void ValidateRequiredSidePackets_ShouldReturnOk_When_TheConfigDoesNotRequireSidePackets_And_SidePacketIsEmpty() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(pass_through_config_text)).AssertOk(); + using (var sidePackets = new SidePackets()) + { + using (var status = config.ValidateRequiredSidePackets(sidePackets)) + { + Assert.True(status.Ok()); + } + } + } + } + + [Test] + public void ValidateRequiredSidePackets_ShouldReturnOk_When_TheConfigDoesNotRequireSidePackets_And_SidePacketIsNotEmpty() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(pass_through_config_text)).AssertOk(); + using (var sidePackets = new SidePackets()) + { + sidePackets.Emplace("in", PacketFactory.IntPacket(0)); + using (var status = config.ValidateRequiredSidePackets(sidePackets)) + { + Assert.True(status.Ok()); + } + } + } + } + + [Test] + public void ValidateRequiredSidePackets_ShouldReturnOk_When_AllTheSidePacketsAreOptional_And_SidePacketIsEmpty() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(flow_limiter_config_text)).AssertOk(); + using (var sidePackets = new SidePackets()) + { + using (var status = config.ValidateRequiredSidePackets(sidePackets)) + { + Assert.True(status.Ok()); + } + } + } + } + + [Test] + public void ValidateRequiredSidePackets_ShouldReturnInvalidArgumentError_When_TheConfigRequiresSidePackets_And_SidePacketIsEmpty() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(image_transformation_config_text)).AssertOk(); + using (var sidePackets = new SidePackets()) + { + using (var status = config.ValidateRequiredSidePackets(sidePackets)) + { + Assert.AreEqual(Status.StatusCode.InvalidArgument, status.Code); + } + } + } + } + + [Test] + public void ValidateRequiredSidePackets_ShouldReturnInvalidArgumentError_When_AllTheRequiredSidePacketsAreNotGiven() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(image_transformation_config_text)).AssertOk(); + using (var sidePackets = new SidePackets()) + { + sidePackets.Emplace("input_horizontally_flipped", PacketFactory.BoolPacket(false)); + sidePackets.Emplace("input_vertically_flipped", PacketFactory.BoolPacket(true)); + using (var status = config.ValidateRequiredSidePackets(sidePackets)) + { + Assert.AreEqual(Status.StatusCode.InvalidArgument, status.Code); + } + } + } + } + + [Test] + public void ValidateRequiredSidePackets_ShouldReturnInvalidArgumentError_When_TheSidePacketValuesAreWrong() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(image_transformation_config_text)).AssertOk(); + using (var sidePackets = new SidePackets()) + { + sidePackets.Emplace("input_horizontally_flipped", PacketFactory.BoolPacket(false)); + sidePackets.Emplace("input_vertically_flipped", PacketFactory.BoolPacket(true)); + sidePackets.Emplace("input_rotation", PacketFactory.StringPacket("0")); + using (var status = config.ValidateRequiredSidePackets(sidePackets)) + { + Assert.AreEqual(Status.StatusCode.InvalidArgument, status.Code); + } + } + } + } + + [Test] + public void ValidateRequiredSidePackets_ShouldReturnOk_When_AllTheRequiredSidePacketsAreGiven() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(image_transformation_config_text)).AssertOk(); + using (var sidePackets = new SidePackets()) + { + sidePackets.Emplace("input_horizontally_flipped", PacketFactory.BoolPacket(false)); + sidePackets.Emplace("input_vertically_flipped", PacketFactory.BoolPacket(true)); + sidePackets.Emplace("input_rotation", PacketFactory.IntPacket(0)); + using (var status = config.ValidateRequiredSidePackets(sidePackets)) + { + Assert.True(status.Ok()); + } + } + } + } + #endregion + + #region Config + [Test] + public void Config_ShouldReturnAnEmptyConfig_When_NotInitialized() + { + using (var config = new ValidatedGraphConfig()) + { + var canonicalizedConfig = config.Config(); + Assert.AreEqual(canonicalizedConfig.CalculateSize(), 0); + } + } + + [Test] + public void Config_ShouldReturnTheCanonicalizedConfig_When_TheConfigIsPassThroughConfig() + { + using (var config = new ValidatedGraphConfig()) + { + var originalConfig = CalculatorGraphConfig.Parser.ParseFromTextFormat(pass_through_config_text); + config.Initialize(originalConfig).AssertOk(); + var canonicalizedConfig = config.Config(); + + Assert.AreEqual(originalConfig.Node, canonicalizedConfig.Node); + Assert.AreEqual(originalConfig.InputStream, canonicalizedConfig.InputStream); + Assert.AreEqual(originalConfig.OutputStream, canonicalizedConfig.OutputStream); + Assert.IsEmpty(originalConfig.Executor); + Assert.AreEqual(1, canonicalizedConfig.Executor.Count); + Assert.AreEqual(0, canonicalizedConfig.Executor[0].CalculateSize()); + + Assert.AreEqual(80, originalConfig.CalculateSize()); + Assert.AreEqual(82, canonicalizedConfig.CalculateSize()); + } + } + + [Test] + public void Config_ShouldReturnTheCanonicalizedConfig_When_TheConfigIsFaceDetectionShortRangeCommonConfig() + { + using (var config = new ValidatedGraphConfig()) + { + var originalConfig = CalculatorGraphConfig.Parser.ParseFromTextFormat(face_detection_short_range_common_config_text); + config.Initialize(originalConfig).AssertOk(); + var canonicalizedConfig = config.Config(); + + Assert.AreEqual(145, originalConfig.CalculateSize()); + Assert.AreEqual(936, canonicalizedConfig.CalculateSize()); + } + } + #endregion + + #region InputStreamInfos + [Test] + public void InputStreamInfos_ShouldReturnEmptyList_When_NotInitialized() + { + using (var config = new ValidatedGraphConfig()) + { + Assert.IsEmpty(config.InputStreamInfos()); + } + } + + [Test] + public void InputStreamInfos_ShouldReturnEmptyList_When_NoInputStreamExists() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(constant_side_packet_config_text)).AssertOk(); + Assert.IsEmpty(config.InputStreamInfos()); + } + } + + [Test] + public void InputStreamInfos_ShouldReturnEdgeInfoList_When_InputStreamsExist() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(pass_through_config_text)).AssertOk(); + var inputStreamInfos = config.InputStreamInfos(); + + Assert.AreEqual(inputStreamInfos.Count, 2); + + var inStream = inputStreamInfos.First((edgeInfo) => edgeInfo.Name == "in"); + Assert.AreEqual(0, inStream.Upstream); + Assert.AreEqual(NodeType.Calculator, inStream.ParentNode.Type); + Assert.AreEqual(0, inStream.ParentNode.Index); + Assert.False(inStream.BackEdge); + + var out1Stream = inputStreamInfos.First((edgeInfo) => edgeInfo.Name == "out1"); + Assert.AreEqual(1, out1Stream.Upstream); + Assert.AreEqual(NodeType.Calculator, out1Stream.ParentNode.Type); + Assert.AreEqual(1, out1Stream.ParentNode.Index); + Assert.False(out1Stream.BackEdge); + } + } + #endregion + + #region OutputStreamInfos + [Test] + public void OutputStreamInfos_ShouldReturnEmptyList_When_NotInitialized() + { + using (var config = new ValidatedGraphConfig()) + { + Assert.IsEmpty(config.OutputStreamInfos()); + } + } + + [Test] + public void OutputStreamInfos_ShouldReturnEdgeInfoList_When_OutputStreamsExist() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(pass_through_config_text)).AssertOk(); + var outputStreamInfos = config.OutputStreamInfos(); + + Assert.AreEqual(3, outputStreamInfos.Count); + + var inStream = outputStreamInfos.First((edgeInfo) => edgeInfo.Name == "in"); + Assert.AreEqual(-1, inStream.Upstream); + Assert.AreEqual(NodeType.GraphInputStream, inStream.ParentNode.Type); + Assert.AreEqual(2, inStream.ParentNode.Index, 2); + Assert.False(inStream.BackEdge); + + var out1Stream = outputStreamInfos.First((edgeInfo) => edgeInfo.Name == "out1"); + Assert.AreEqual(-1, out1Stream.Upstream); + Assert.AreEqual(NodeType.Calculator, out1Stream.ParentNode.Type); + Assert.AreEqual(0, out1Stream.ParentNode.Index); + Assert.False(out1Stream.BackEdge); + + var outStream = outputStreamInfos.First((edgeInfo) => edgeInfo.Name == "out"); + Assert.AreEqual(-1, outStream.Upstream); + Assert.AreEqual(NodeType.Calculator, outStream.ParentNode.Type); + Assert.AreEqual(1, outStream.ParentNode.Index); + Assert.False(outStream.BackEdge); + } + } + #endregion + + #region InputSidePacketInfos + [Test] + public void InputSidePacketInfos_ShouldReturnEmptyList_When_NotInitialized() + { + using (var config = new ValidatedGraphConfig()) + { + Assert.IsEmpty(config.InputSidePacketInfos()); + } + } + + [Test] + public void InputSidePacketInfos_ShouldReturnEmptyList_When_NoInputSidePacketExists() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(pass_through_config_text)).AssertOk(); + Assert.IsEmpty(config.InputSidePacketInfos()); + } + } + + [Test] + public void InputSidePacketInfos_ShouldReturnEdgeInfoList_When_InputSidePacketsExist() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(flow_limiter_config_text)).AssertOk(); + var inputSidePacketInfos = config.InputSidePacketInfos(); + + Assert.True(inputSidePacketInfos.Count >= 2); + + var maxInFlightPacket = inputSidePacketInfos.First((edgeInfo) => edgeInfo.Name == "max_in_flight"); + Assert.AreEqual(-1, maxInFlightPacket.Upstream); + Assert.AreEqual(NodeType.Calculator, maxInFlightPacket.ParentNode.Type); + Assert.False(maxInFlightPacket.BackEdge); + + var flowLimiterCalculatorOptionsPacket = inputSidePacketInfos.First((edgeInfo) => edgeInfo.Name == "flow_limiter_calculator_options"); + Assert.AreEqual(-1, flowLimiterCalculatorOptionsPacket.Upstream); + Assert.AreEqual(NodeType.Calculator, flowLimiterCalculatorOptionsPacket.ParentNode.Type); + Assert.False(flowLimiterCalculatorOptionsPacket.BackEdge); + } + } + #endregion + + #region OutputSidePacketInfos + [Test] + public void OutputSidePacketInfos_ShouldReturnEmptyList_When_NotInitialized() + { + using (var config = new ValidatedGraphConfig()) + { + Assert.IsEmpty(config.OutputSidePacketInfos()); + } + } + + [Test] + public void OutputSidePacketInfos_ShouldReturnEmptyList_When_NoOutputSidePacketExists() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(pass_through_config_text)).AssertOk(); + Assert.IsEmpty(config.OutputSidePacketInfos()); + } + } + + [Test] + public void OutputSidePacketInfos_ShouldReturnEdgeInfoList_When_OutputSidePacketsExist() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(constant_side_packet_config_text)).AssertOk(); + var outputSidePacketInfos = config.OutputSidePacketInfos(); + + Assert.AreEqual(4, outputSidePacketInfos.Count); + + var intPacket = outputSidePacketInfos.First((edgeInfo) => edgeInfo.Name == "int_packet"); + Assert.AreEqual(-1, intPacket.Upstream); + Assert.AreEqual(NodeType.Calculator, intPacket.ParentNode.Type); + Assert.False(intPacket.BackEdge); + + var floatPacket = outputSidePacketInfos.First((edgeInfo) => edgeInfo.Name == "float_packet"); + Assert.AreEqual(-1, floatPacket.Upstream); + Assert.AreEqual(NodeType.Calculator, floatPacket.ParentNode.Type); + Assert.False(floatPacket.BackEdge); + + var boolPacket = outputSidePacketInfos.First((edgeInfo) => edgeInfo.Name == "bool_packet"); + Assert.AreEqual(-1, boolPacket.Upstream); + Assert.AreEqual(NodeType.Calculator, boolPacket.ParentNode.Type); + Assert.False(boolPacket.BackEdge); + + var stringPacket = outputSidePacketInfos.First((edgeInfo) => edgeInfo.Name == "string_packet"); + Assert.AreEqual(-1, stringPacket.Upstream); + Assert.AreEqual(NodeType.Calculator, stringPacket.ParentNode.Type); + Assert.False(stringPacket.BackEdge); + } + } + #endregion + + #region OutputStreamIndex + [Test] + public void OutputStreamIndex_ShouldReturnNegativeValue_When_NotInitialized() + { + using (var config = new ValidatedGraphConfig()) + { + Assert.AreEqual(-1, config.OutputStreamIndex("")); + } + } + + [Test] + public void OutputStreamIndex_ShouldReturnNegativeValue_When_TheNameIsInvalid() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(pass_through_config_text)).AssertOk(); + Assert.AreEqual(-1, config.OutputStreamIndex("unknown")); + } + } + + [Test] + public void OutputStreamIndex_ShouldReturnIndex_When_TheNameIsValid() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(pass_through_config_text)).AssertOk(); + Assert.AreEqual(2, config.OutputStreamIndex("out")); + } + } + + [Test] + public void OutputStreamIndex_ShouldReturnIndex_When_TheStreamIsNotPublic() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(pass_through_config_text)).AssertOk(); + Assert.AreEqual(1, config.OutputStreamIndex("out1")); + } + } + #endregion + + #region OutputSidePacketIndex + [Test] + public void OutputSidePacketIndex_ShouldReturnNegativeValue_When_NotInitialized() + { + using (var config = new ValidatedGraphConfig()) + { + Assert.AreEqual(-1, config.OutputSidePacketIndex("")); + } + } + + [Test] + public void OutputSidePacketIndex_ShouldReturnNegativeValue_When_TheNameIsInvalid() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(constant_side_packet_config_text)).AssertOk(); + Assert.AreEqual(-1, config.OutputSidePacketIndex("unknown")); + } + } + + [Test] + public void OutputSidePacketIndex_ShouldReturnIndex_When_TheNameIsValid() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(constant_side_packet_config_text)).AssertOk(); + Assert.AreEqual(0, config.OutputSidePacketIndex("int_packet")); + } + } + #endregion + + + #region OutputStreamToNode + [Test] + public void OutputStreamToNode_ShouldReturnNegativeValue_When_NotInitialized() + { + using (var config = new ValidatedGraphConfig()) + { + Assert.AreEqual(-1, config.OutputStreamToNode("")); + } + } + + [Test] + public void OutputStreamToNode_ShouldReturnNegativeValue_When_TheNameIsInvalid() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(pass_through_config_text)).AssertOk(); + Assert.AreEqual(-1, config.OutputStreamToNode("unknown")); + } + } + + [Test] + public void OutputStreamToNode_ShouldReturnIndex_When_TheNameIsValid() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(pass_through_config_text)).AssertOk(); + Assert.AreEqual(0, config.OutputStreamToNode("out1")); + } + } + #endregion + + #region RegisteredSidePacketTypeName + [Test] + public void RegisteredSidePacketTypeName_ShouldReturnInvalidArgumentError_When_TheSidePacketDoesNotExist() + { + using (var config = new ValidatedGraphConfig()) + { + using (var statusOrString = config.RegisteredSidePacketTypeName("max_in_flight")) + { + Assert.AreEqual(Status.StatusCode.InvalidArgument, statusOrString.Status.Code); + } + } + } + + [Test] + public void RegisteredSidePacketTypeName_ShouldReturnUnknownError_When_TheSidePacketTypeCannotBeDetermined() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(flow_limiter_config_text)).AssertOk(); + using (var statusOrString = config.RegisteredSidePacketTypeName("max_in_flight")) + { + Assert.AreEqual(Status.StatusCode.Unknown, statusOrString.Status.Code); + } + } + } + #endregion + + #region RegisteredStreamTypeName + [Test] + public void RegisteredStreamTypeName_ShouldReturnInvalidArgumentError_When_TheStreamDoesNotExist() + { + using (var config = new ValidatedGraphConfig()) + { + using (var statusOrString = config.RegisteredStreamTypeName("in")) + { + Assert.AreEqual(Status.StatusCode.InvalidArgument, statusOrString.Status.Code); + } + } + } + + [Test] + public void RegisteredStreamTypeName_ShouldReturnUnknownError_When_TheStreamTypeCannotBeDetermined() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(pass_through_config_text)).AssertOk(); + using (var statusOrString = config.RegisteredStreamTypeName("in")) + { + Assert.AreEqual(Status.StatusCode.Unknown, statusOrString.Status.Code); + } + } + } + #endregion + + #region Package + [Test] + public void Package_ShouldReturnNull_When_NotInitialized() + { + using var config = new ValidatedGraphConfig(); + Assert.IsNull(config.Package()); + } + + [Test] + public void Package_ShouldReturnNull_When_TheNamespaceIsNotSet() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(pass_through_config_text)).AssertOk(); + Assert.IsNull(config.Package()); + } + } + #endregion + + #region IsReservedExecutorName + [Test] + public void IsReservedExecutorName_ShouldReturnFalse_When_TheNameIsNotReserved() + { + Assert.False(ValidatedGraphConfig.IsReservedExecutorName("unknown")); + } + + [Test] + public void IsReservedExecutorName_ShouldReturnFalse_When_TheNameIsReserved() + { + Assert.True(ValidatedGraphConfig.IsReservedExecutorName("default")); + Assert.True(ValidatedGraphConfig.IsReservedExecutorName("gpu")); + Assert.True(ValidatedGraphConfig.IsReservedExecutorName("__gpu")); + } + #endregion + + #region IsExternalSidePacket + [Test] + public void IsExternalSidePacket_ShouldReturnFalse_When_NotInitialized() + { + using (var config = new ValidatedGraphConfig()) + { + Assert.False(config.IsExternalSidePacket("max_in_flight")); + } + } + + + [Test] + public void IsExternalSidePacket_ShouldReturnFalse_When_TheSidePacketIsInternal() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(constant_side_packet_config_text)).AssertOk(); + Assert.False(config.IsExternalSidePacket("int_packet")); + } + } + + [Test] + public void IsExternalSidePacket_ShouldReturnTrue_When_TheSidePacketIsExternal() + { + using (var config = new ValidatedGraphConfig()) + { + config.Initialize(CalculatorGraphConfig.Parser.ParseFromTextFormat(flow_limiter_config_text)).AssertOk(); + Assert.True(config.IsExternalSidePacket("max_in_flight")); + } + } + #endregion + } +} diff --git a/Mediapipe.Net.Tests/Gpu/GlCalculatorHelperTest.cs b/Mediapipe.Net.Tests/Gpu/GlCalculatorHelperTest.cs index fea81cc..3c15aff 100644 --- a/Mediapipe.Net.Tests/Gpu/GlCalculatorHelperTest.cs +++ b/Mediapipe.Net.Tests/Gpu/GlCalculatorHelperTest.cs @@ -5,6 +5,7 @@ using System; using Mediapipe.Net.Framework.Format; using Mediapipe.Net.Framework.Port; +using Mediapipe.Net.Framework.Protobuf; using Mediapipe.Net.Gpu; using NUnit.Framework; using NUnit.Framework.Internal; @@ -61,31 +62,18 @@ namespace Mediapipe.Net.Tests.Gpu using var glCalculatorHelper = new GlCalculatorHelper(); glCalculatorHelper.InitializeForTest(GpuResources.Create().Value()); - var status = glCalculatorHelper.RunInGlContext(() => { return Status.Ok(); }); + Status status = glCalculatorHelper.RunInGlContext(() => { }); Assert.True(status.Ok()); } [Test, GpuOnly] - public void RunInGlContext_ShouldReturnInternal_When_FunctionReturnsInternal() + public void RunInGlContext_ShouldReturnInternal_When_FunctionThrows() { using var glCalculatorHelper = new GlCalculatorHelper(); glCalculatorHelper.InitializeForTest(GpuResources.Create().Value()); - var status = glCalculatorHelper.RunInGlContext(() => { return Status.Build(Status.StatusCode.Internal, "error"); }); - Assert.AreEqual(status.Code, Status.StatusCode.Internal); - } - - [Test, GpuOnly] - public void RunInGlContext_ShouldReturnFailedPreCondition_When_FunctionThrows() - { - using var glCalculatorHelper = new GlCalculatorHelper(); - glCalculatorHelper.InitializeForTest(GpuResources.Create().Value()); - -#pragma warning disable IDE0039 - GlCalculatorHelper.GlStatusFunction glStatusFunction = () => { throw new InvalidProgramException(); }; -#pragma warning restore IDE0039 - var status = glCalculatorHelper.RunInGlContext(glStatusFunction); - Assert.AreEqual(status.Code, Status.StatusCode.FailedPrecondition); + Status status = glCalculatorHelper.RunInGlContext((GlCalculatorHelper.GlFunction)(() => { throw new Exception("Function Throws"); })); + Assert.AreEqual(Status.StatusCode.Internal, status.Code); } #endregion @@ -96,16 +84,15 @@ namespace Mediapipe.Net.Tests.Gpu using var glCalculatorHelper = new GlCalculatorHelper(); glCalculatorHelper.InitializeForTest(GpuResources.Create().Value()); - using var imageFrame = new ImageFrame(ImageFormat.Srgba, 32, 24); + using var imageFrame = new ImageFrame(ImageFormat.Types.Format.Srgba, 32, 24); var status = glCalculatorHelper.RunInGlContext(() => { var texture = glCalculatorHelper.CreateSourceTexture(imageFrame); - Assert.AreEqual(texture.Width, 32); - Assert.AreEqual(texture.Height, 24); + Assert.AreEqual(32, texture.Width); + Assert.AreEqual(24, texture.Height); texture.Dispose(); - return Status.Ok(); }); Assert.True(status.Ok()); @@ -113,21 +100,19 @@ namespace Mediapipe.Net.Tests.Gpu } [Test, GpuOnly] + [Ignore("Skip because a thread will hang")] public void CreateSourceTexture_ShouldFail_When_ImageFrameFormatIsInvalid() { using var glCalculatorHelper = new GlCalculatorHelper(); glCalculatorHelper.InitializeForTest(GpuResources.Create().Value()); - using var imageFrame = new ImageFrame(ImageFormat.Sbgra, 32, 24); - var status = glCalculatorHelper.RunInGlContext(() => + using var imageFrame = new ImageFrame(ImageFormat.Types.Format.Sbgra, 32, 24); + Status status = glCalculatorHelper.RunInGlContext(() => { - using (var texture = glCalculatorHelper.CreateSourceTexture(imageFrame)) - { - texture.Release(); - } - return Status.Ok(); + using GlTexture texture = glCalculatorHelper.CreateSourceTexture(imageFrame); + texture.Release(); }); - Assert.AreEqual(status.Code, Status.StatusCode.FailedPrecondition); + Assert.AreEqual(Status.StatusCode.FailedPrecondition, status.Code); status.Dispose(); } @@ -140,20 +125,19 @@ namespace Mediapipe.Net.Tests.Gpu using var glCalculatorHelper = new GlCalculatorHelper(); glCalculatorHelper.InitializeForTest(GpuResources.Create().Value()); - var status = glCalculatorHelper.RunInGlContext(() => + Status status = glCalculatorHelper.RunInGlContext(() => { - var glTexture = glCalculatorHelper.CreateDestinationTexture(32, 24, GpuBufferFormat.KBgra32); + GlTexture glTexture = glCalculatorHelper.CreateDestinationTexture(32, 24, GpuBufferFormat.KBgra32); - Assert.AreEqual(glTexture.Width, 32); - Assert.AreEqual(glTexture.Height, 24); - return Status.Ok(); + Assert.AreEqual(32, glTexture.Width); + Assert.AreEqual(24, glTexture.Height); }); Assert.True(status.Ok()); } #endregion - #region Framebuffer + #region framebuffer [Test, GpuOnly] public void Framebuffer_ShouldReturnGLName() { @@ -161,7 +145,7 @@ namespace Mediapipe.Net.Tests.Gpu glCalculatorHelper.InitializeForTest(GpuResources.Create().Value()); // default frame buffer - Assert.AreEqual(glCalculatorHelper.Framebuffer, 0); + Assert.AreEqual(0, glCalculatorHelper.Framebuffer); } #endregion diff --git a/Mediapipe.Net.Tests/Gpu/GlContextTest.cs b/Mediapipe.Net.Tests/Gpu/GlContextTest.cs index 3f64881..fbc7d80 100644 --- a/Mediapipe.Net.Tests/Gpu/GlContextTest.cs +++ b/Mediapipe.Net.Tests/Gpu/GlContextTest.cs @@ -3,7 +3,6 @@ // MediaPipe.NET is licensed under the MIT License. See LICENSE for details. using System; -using Mediapipe.Net.Framework.Port; using Mediapipe.Net.Gpu; using NUnit.Framework; @@ -28,10 +27,9 @@ namespace Mediapipe.Net.Tests.Gpu glCalculatorHelper.RunInGlContext(() => { - using var glContext = GlContext.GetCurrent(); + using GlContext? glContext = GlContext.GetCurrent(); Assert.NotNull(glContext); Assert.True(glContext?.IsCurrent()); - return Status.Ok(); }).AssertOk(); } #endregion @@ -56,9 +54,9 @@ namespace Mediapipe.Net.Tests.Gpu Assert.True(glContext.EglDisplay != null); Assert.True(glContext.EglConfig != null); Assert.True(glContext.EglContext != null); - Assert.AreEqual(glContext.GlMajorVersion, 3); - Assert.AreEqual(glContext.GlMinorVersion, 2); - Assert.AreEqual(glContext.GlFinishCount, 0); + Assert.AreEqual(3, glContext.GlMajorVersion); + Assert.AreEqual(2, glContext.GlMinorVersion); + Assert.AreEqual(0, glContext.GlFinishCount); } else if (OperatingSystem.IsMacOS()) { diff --git a/Mediapipe.Net.Tests/Gpu/GlTextureTest.cs b/Mediapipe.Net.Tests/Gpu/GlTextureTest.cs index fb11034..a12bc49 100644 --- a/Mediapipe.Net.Tests/Gpu/GlTextureTest.cs +++ b/Mediapipe.Net.Tests/Gpu/GlTextureTest.cs @@ -14,8 +14,8 @@ namespace Mediapipe.Net.Tests.Gpu public void Ctor_ShouldInstantiateGlTexture_When_CalledWithNoArguments() { using var glTexture = new GlTexture(); - Assert.AreEqual(glTexture.Width, 0); - Assert.AreEqual(glTexture.Height, 0); + Assert.AreEqual(0, glTexture.Width); + Assert.AreEqual(0, glTexture.Height); } #endregion @@ -42,7 +42,7 @@ namespace Mediapipe.Net.Tests.Gpu public void Target_ShouldReturnTarget() { using var glTexture = new GlTexture(); - Assert.AreEqual(glTexture.Target, Gl.GL_TEXTURE_2D); + Assert.AreEqual(Gl.GL_TEXTURE_2D, glTexture.Target); } #endregion } diff --git a/Mediapipe.Net.Tests/Mediapipe.Net.Tests.csproj b/Mediapipe.Net.Tests/Mediapipe.Net.Tests.csproj index 39bb84d..4d3ad88 100644 --- a/Mediapipe.Net.Tests/Mediapipe.Net.Tests.csproj +++ b/Mediapipe.Net.Tests/Mediapipe.Net.Tests.csproj @@ -9,9 +9,9 @@ - - - + + + diff --git a/Mediapipe.Net/Core/MpResourceHandle.cs b/Mediapipe.Net/Core/MpResourceHandle.cs index dc025e7..d641ac6 100644 --- a/Mediapipe.Net/Core/MpResourceHandle.cs +++ b/Mediapipe.Net/Core/MpResourceHandle.cs @@ -10,7 +10,17 @@ namespace Mediapipe.Net.Core { public unsafe abstract class MpResourceHandle : Disposable, IMpResourceHandle { - protected void* Ptr; + private void* ptr = null; + protected void* Ptr + { + get => ptr; + set + { + if (value != null && OwnsResource) + throw new InvalidOperationException($"This object owns another resource"); + ptr = value; + } + } protected MpResourceHandle(bool isOwner = true) : this(null, isOwner) { } @@ -34,10 +44,12 @@ namespace Mediapipe.Net.Core if (OwnsResource) DeleteMpPtr(); + ReleaseMpPtr(); TransferOwnership(); } - public bool OwnsResource => IsOwner && Ptr != null; + protected bool IsResourcePresent => Ptr != null; + public bool OwnsResource => IsOwner && IsResourcePresent; #endregion protected override void DisposeUnmanaged() @@ -62,11 +74,14 @@ namespace Mediapipe.Net.Core protected abstract void DeleteMpPtr(); protected delegate MpReturnCode StringOutFunc(void* ptr, out sbyte* strPtr); - protected string MarshalStringFromNative(StringOutFunc func) + protected string? MarshalStringFromNative(StringOutFunc func) { func(MpPtr, out sbyte* strPtr).Assert(); GC.KeepAlive(this); + if (strPtr == null) + return null; + string str = new string(strPtr); UnsafeNativeMethods.delete_array__PKc(strPtr); diff --git a/Mediapipe.Net/External/Protobuf.cs b/Mediapipe.Net/External/Protobuf.cs index 8e0f555..f164c7d 100644 --- a/Mediapipe.Net/External/Protobuf.cs +++ b/Mediapipe.Net/External/Protobuf.cs @@ -2,11 +2,47 @@ // This file is part of MediaPipe.NET. // MediaPipe.NET is licensed under the MIT License. See LICENSE for details. +using System; +using Mediapipe.Net.Native; + namespace Mediapipe.Net.External { - internal static class Protobuf + public static class Protobuf { - public delegate void ProtobufLogHandler(int level, string filename, int line, string message); - // TODO: (from homuler) Overwrite protobuf logger to show logs in Console Window. + public delegate void LogHandler(int level, string filename, int line, string message); + public static readonly LogHandler DefaultLogHandler = logProtobufMessage; + + public static void SetLogHandler(LogHandler logHandler) + { + UnsafeNativeMethods.google_protobuf__SetLogHandler__PF(logHandler).Assert(); + } + + /// + /// Reset the . + /// If is called, this method should be called before the program exits. + /// + public static void ResetLogHandler() + { + UnsafeNativeMethods.google_protobuf__ResetLogHandler().Assert(); + } + + private static void logProtobufMessage(int level, string filename, int line, string message) + { + switch (level) + { + case 1: + Console.Error.WriteLine($"[libprotobuf WARN {filename}:{line}] {message}"); + break; + case 2: + Console.Error.WriteLine($"[libprotobuf ERROR {filename}:{line}] {message}"); + break; + case 3: + Console.Error.WriteLine($"[libprotobuf FATAL {filename}:{line}] {message}"); + break; + default: + Console.Error.WriteLine($"[libprotobuf INFO {filename}:{line}] {message}"); + break; + } + } } } diff --git a/Mediapipe.Net/External/SerializedProto.cs b/Mediapipe.Net/External/SerializedProto.cs index 4833258..751e67f 100644 --- a/Mediapipe.Net/External/SerializedProto.cs +++ b/Mediapipe.Net/External/SerializedProto.cs @@ -10,10 +10,10 @@ using Mediapipe.Net.Util; namespace Mediapipe.Net.External { [StructLayout(LayoutKind.Sequential)] - internal unsafe struct SerializedProto + internal unsafe readonly struct SerializedProto { - public sbyte* StrPtr; - public int Length; + public readonly sbyte* StrPtr; + public readonly int Length; // TODO: That Dispose() method is looking very sus... // Might wanna investigate if it's better as a child of Disposable. diff --git a/Mediapipe.Net/External/SerializedProtoVector.cs b/Mediapipe.Net/External/SerializedProtoVector.cs index 51d2173..f78ff94 100644 --- a/Mediapipe.Net/External/SerializedProtoVector.cs +++ b/Mediapipe.Net/External/SerializedProtoVector.cs @@ -10,19 +10,13 @@ using Mediapipe.Net.Native; namespace Mediapipe.Net.External { [StructLayout(LayoutKind.Sequential)] - internal unsafe struct SerializedProtoVector + internal unsafe readonly struct SerializedProtoVector { - public SerializedProto* Data; - public int Size; + public readonly SerializedProto* Data; + public readonly int Size; - // TODO: This is looking just as sus as SerializedProto.Dispose(). - // Should be investigated in the same way. - public void Dispose() - { - for (int i = 0; i < Size; i++) - Data[i].Dispose(); - UnsafeNativeMethods.mp_api_SerializedProtoArray__delete(Data); - } + // The array element freeing loop has been moved to MediaPipe.NET.Runtime. + public void Dispose() => UnsafeNativeMethods.mp_api_SerializedProtoArray__delete(Data, Size); public List Deserialize(MessageParser parser) where T : IMessage { diff --git a/Mediapipe.Net/Framework/CalculatorGraph.cs b/Mediapipe.Net/Framework/CalculatorGraph.cs index 1d11453..9fe2d5e 100644 --- a/Mediapipe.Net/Framework/CalculatorGraph.cs +++ b/Mediapipe.Net/Framework/CalculatorGraph.cs @@ -17,8 +17,8 @@ namespace Mediapipe.Net.Framework { public unsafe class CalculatorGraph : MpResourceHandle { - public delegate void* NativePacketCallback(void* graphPtr, void* packetPtr); - public delegate Status PacketCallback(Packet packet); + public delegate Status.StatusArgs NativePacketCallback(void* graphPtr, int streamId, void* packetPtr); + public delegate void PacketCallback(Packet packet); public CalculatorGraph() : base() { @@ -26,13 +26,7 @@ namespace Mediapipe.Net.Framework Ptr = ptr; } - public CalculatorGraph(string textFormatConfig) : base() - { - UnsafeNativeMethods.mp_CalculatorGraph__PKc(textFormatConfig, out var ptr).Assert(); - Ptr = ptr; - } - - public CalculatorGraph(byte[] serializedConfig) : base() + private CalculatorGraph(byte[] serializedConfig) : base() { UnsafeNativeMethods.mp_CalculatorGraph__PKc_i(serializedConfig, serializedConfig.Length, out var ptr).Assert(); Ptr = ptr; @@ -40,6 +34,8 @@ namespace Mediapipe.Net.Framework public CalculatorGraph(CalculatorGraphConfig config) : this(config.ToByteArray()) { } + public CalculatorGraph(string textFormatConfig) : this(CalculatorGraphConfig.Parser.ParseFromTextFormat(textFormatConfig)) { } + protected override void DeleteMpPtr() => UnsafeNativeMethods.mp_CalculatorGraph__delete(Ptr); public Status Initialize(CalculatorGraphConfig config) @@ -72,9 +68,9 @@ namespace Mediapipe.Net.Framework return config; } - public Status ObserveOutputStream(string streamName, NativePacketCallback nativePacketCallback, bool observeTimestampBounds = false) + public Status ObserveOutputStream(string streamName, int streamId, NativePacketCallback nativePacketCallback, bool observeTimestampBounds = false) { - UnsafeNativeMethods.mp_CalculatorGraph__ObserveOutputStream__PKc_PF_b(MpPtr, streamName, nativePacketCallback, observeTimestampBounds, out var statusPtr).Assert(); + UnsafeNativeMethods.mp_CalculatorGraph__ObserveOutputStream__PKc_PF_b(MpPtr, streamName, streamId, nativePacketCallback, observeTimestampBounds, out var statusPtr).Assert(); GC.KeepAlive(this); return new Status(statusPtr); @@ -82,24 +78,25 @@ namespace Mediapipe.Net.Framework public Status ObserveOutputStream(string streamName, PacketCallback packetCallback, bool observeTimestampBounds, out GCHandle callbackHandle) { - NativePacketCallback nativePacketCallback = (_, packetPtr) => + NativePacketCallback nativePacketCallback = (void* graphPtr, int streamId, void* packetPtr) => { - Status status; try { Packet packet = new Packet(packetPtr, false); - status = packetCallback(packet); + packetCallback(packet); + // This packet is not being disposed in MediaPipeUnityPlugin? packet.Dispose(); + return Status.StatusArgs.Ok(); } catch (Exception e) { - status = Status.FailedPrecondition(e.ToString()); + return Status.StatusArgs.Internal(e.ToString()); } - return status.MpPtr; }; callbackHandle = GCHandle.Alloc(nativePacketCallback, GCHandleType.Normal); - return ObserveOutputStream(streamName, nativePacketCallback, observeTimestampBounds); + // Thought: why have a streamId at all if we just put 0 in there? + return ObserveOutputStream(streamName, 0, nativePacketCallback, observeTimestampBounds); } public Status ObserveOutputStream(string streamName, PacketCallback packetCallback, out GCHandle callbackHandle) diff --git a/Mediapipe.Net/Framework/Format/ImageFormat.cs b/Mediapipe.Net/Framework/Format/ImageFormat.cs deleted file mode 100644 index e79a9b5..0000000 --- a/Mediapipe.Net/Framework/Format/ImageFormat.cs +++ /dev/null @@ -1,23 +0,0 @@ -// 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. - -namespace Mediapipe.Net.Framework.Format -{ - public enum ImageFormat : int - { - Unknown = 0, - Srgb = 1, - Srgba = 2, - Gray8 = 3, - Gray16 = 4, - Ycbcr420p = 5, - Ycbcr420p10 = 6, - Srgb48 = 7, - Srgba64 = 8, - Vec32f1 = 9, - Vec32f2 = 12, - Lab8 = 10, - Sbgra = 11, - } -} diff --git a/Mediapipe.Net/Framework/Format/ImageFrame.cs b/Mediapipe.Net/Framework/Format/ImageFrame.cs index df0625c..785603b 100644 --- a/Mediapipe.Net/Framework/Format/ImageFrame.cs +++ b/Mediapipe.Net/Framework/Format/ImageFrame.cs @@ -3,8 +3,8 @@ // MediaPipe.NET is licensed under the MIT License. See LICENSE for details. using System; -using System.Runtime.InteropServices; using Mediapipe.Net.Core; +using Mediapipe.Net.Framework.Protobuf; using Mediapipe.Net.Native; namespace Mediapipe.Net.Framework.Format @@ -22,15 +22,15 @@ namespace Mediapipe.Net.Framework.Format public ImageFrame(void* imageFramePtr, bool isOwner = true) : base(imageFramePtr, isOwner) { } - public ImageFrame(ImageFormat format, int width, int height) : this(format, width, height, DefaultAlignmentBoundary) { } + public ImageFrame(ImageFormat.Types.Format format, int width, int height) : this(format, width, height, DefaultAlignmentBoundary) { } - public ImageFrame(ImageFormat format, int width, int height, uint alignmentBoundary) : base() + public ImageFrame(ImageFormat.Types.Format format, int width, int height, uint alignmentBoundary) : base() { UnsafeNativeMethods.mp_ImageFrame__ui_i_i_ui(format, width, height, alignmentBoundary, out var ptr).Assert(); Ptr = ptr; } - public unsafe ImageFrame(ImageFormat format, int width, int height, int widthStep, byte[] pixelData) : base() + public unsafe ImageFrame(ImageFormat.Types.Format format, int width, int height, int widthStep, byte[] pixelData) : base() { fixed (byte* pixelDataPtr = pixelData) { @@ -42,7 +42,7 @@ namespace Mediapipe.Net.Framework.Format } } - public ImageFrame(ImageFormat format, int width, int height, int widthStep, ReadOnlySpan pixelData) : base() + public ImageFrame(ImageFormat.Types.Format format, int width, int height, int widthStep, ReadOnlySpan pixelData) : base() { fixed (byte* pixelDataPtr = pixelData) { @@ -56,6 +56,111 @@ namespace Mediapipe.Net.Framework.Format protected override void DeleteMpPtr() => UnsafeNativeMethods.mp_ImageFrame__delete(Ptr); + /// + /// The number of channels for a . + /// If channels don't make sense in the , returns 0. + /// + /// + /// Unlike the original implementation, this API won't signal SIGABRT. + /// + public static int NumberOfChannelsForFormat(ImageFormat.Types.Format format) + { + switch (format) + { + case ImageFormat.Types.Format.Srgb: + case ImageFormat.Types.Format.Srgb48: + return 3; + case ImageFormat.Types.Format.Srgba: + case ImageFormat.Types.Format.Srgba64: + case ImageFormat.Types.Format.Sbgra: + return 4; + case ImageFormat.Types.Format.Gray8: + case ImageFormat.Types.Format.Gray16: + return 1; + case ImageFormat.Types.Format.Vec32F1: + return 1; + case ImageFormat.Types.Format.Vec32F2: + return 2; + case ImageFormat.Types.Format.Lab8: + return 3; + case ImageFormat.Types.Format.Ycbcr420P: + case ImageFormat.Types.Format.Ycbcr420P10: + case ImageFormat.Types.Format.Unknown: + default: + return 0; + } + } + + /// + /// The channel size for a . + /// If channels don't make sense in the , returns 0. + /// + /// + /// Unlike the original implementation, this API won't signal SIGABRT. + /// + public static int ChannelSizeForFormat(ImageFormat.Types.Format format) + { + switch (format) + { + case ImageFormat.Types.Format.Srgb: + case ImageFormat.Types.Format.Srgba: + case ImageFormat.Types.Format.Sbgra: + return sizeof(byte); + case ImageFormat.Types.Format.Srgb48: + case ImageFormat.Types.Format.Srgba64: + return sizeof(ushort); + case ImageFormat.Types.Format.Gray8: + return sizeof(byte); + case ImageFormat.Types.Format.Gray16: + return sizeof(ushort); + case ImageFormat.Types.Format.Vec32F1: + case ImageFormat.Types.Format.Vec32F2: + // sizeof float may be wrong since it's platform-dependent, but we assume that it's constant across all supported platforms. + return sizeof(float); + case ImageFormat.Types.Format.Lab8: + return sizeof(byte); + case ImageFormat.Types.Format.Ycbcr420P: + case ImageFormat.Types.Format.Ycbcr420P10: + case ImageFormat.Types.Format.Unknown: + default: + return 0; + } + } + + /// + /// The depth of each channel in bytes for a . + /// If channels don't make sense in the , returns 0. + /// + /// + /// Unlike the original implementation, this API won't signal SIGABRT. + /// + public static int ByteDepthForFormat(ImageFormat.Types.Format format) + { + switch (format) + { + case ImageFormat.Types.Format.Srgb: + case ImageFormat.Types.Format.Srgba: + case ImageFormat.Types.Format.Sbgra: + return 1; + case ImageFormat.Types.Format.Srgb48: + case ImageFormat.Types.Format.Srgba64: + return 2; + case ImageFormat.Types.Format.Gray8: + return 1; + case ImageFormat.Types.Format.Gray16: + return 2; + case ImageFormat.Types.Format.Vec32F1: + case ImageFormat.Types.Format.Vec32F2: + return 4; + case ImageFormat.Types.Format.Lab8: + return 1; + case ImageFormat.Types.Format.Ycbcr420P: + case ImageFormat.Types.Format.Ycbcr420P10: + case ImageFormat.Types.Format.Unknown: + default: + return 0; + } + } public bool IsEmpty => SafeNativeMethods.mp_ImageFrame__IsEmpty(MpPtr) > 0; public bool IsContiguous => SafeNativeMethods.mp_ImageFrame__IsContiguous(MpPtr) > 0; @@ -68,61 +173,46 @@ namespace Mediapipe.Net.Framework.Format return value; } - public ImageFormat Format => SafeNativeMethods.mp_ImageFrame__Format(MpPtr); + public ImageFormat.Types.Format Format => SafeNativeMethods.mp_ImageFrame__Format(MpPtr); public int Width => SafeNativeMethods.mp_ImageFrame__Width(MpPtr); public int Height => SafeNativeMethods.mp_ImageFrame__Height(MpPtr); - public int ChannelSize - { - get - { - var code = SafeNativeMethods.mp_ImageFrame__ChannelSize(MpPtr, out var value); + /// + /// The channel size. + /// If channels don't make sense, returns 0. + /// + /// + /// Unlike the original implementation, this API won't signal SIGABRT. + /// + public int ChannelSize => ChannelSizeForFormat(Format); - GC.KeepAlive(this); - return valueOrFormatException(code, value); - } - } + /// + /// The Number of channels. + /// If channels don't make sense, returns 0. + /// + /// + /// Unlike the original implementation, this API won't signal SIGABRT. + /// + public int NumberOfChannels => NumberOfChannelsForFormat(Format); - public int NumberOfChannels - { - get - { - var code = SafeNativeMethods.mp_ImageFrame__NumberOfChannels(MpPtr, out var value); - - GC.KeepAlive(this); - return valueOrFormatException(code, value); - } - } - - public int ByteDepth - { - get - { - var code = SafeNativeMethods.mp_ImageFrame__ByteDepth(MpPtr, out var value); - - GC.KeepAlive(this); - return valueOrFormatException(code, value); - } - } + /// + /// The depth of each image channel in bytes. + /// If channels don't make sense, returns 0. + /// + /// + /// Unlike the original implementation, this API won't signal SIGABRT. + /// + public int ByteDepth => ByteDepthForFormat(Format); public int WidthStep => SafeNativeMethods.mp_ImageFrame__WidthStep(MpPtr); public byte* MutablePixelData => SafeNativeMethods.mp_ImageFrame__MutablePixelData(MpPtr); - public int PixelDataSize => SafeNativeMethods.mp_ImageFrame__PixelDataSize(MpPtr); + public int PixelDataSize => Height * WidthStep; - public int PixelDataSizeStoredContiguously - { - get - { - var code = SafeNativeMethods.mp_ImageFrame__PixelDataSizeStoredContiguously(MpPtr, out var value); - - GC.KeepAlive(this); - return valueOrFormatException(code, value); - } - } + public int PixelDataSizeStoredContiguously => Width * Height * ByteDepth * NumberOfChannels; public void SetToZero() { @@ -136,140 +226,30 @@ namespace Mediapipe.Net.Framework.Format GC.KeepAlive(this); } - public byte[] CopyToByteBuffer(int bufferSize) - => copyToBuffer(UnsafeNativeMethods.mp_ImageFrame__CopyToBuffer__Pui8_i, bufferSize); + public void CopyToBuffer(byte[] buffer) + => copyToBuffer(UnsafeNativeMethods.mp_ImageFrame__CopyToBuffer__Pui8_i, buffer); - public ushort[] CopyToUshortBuffer(int bufferSize) - => copyToBuffer(UnsafeNativeMethods.mp_ImageFrame__CopyToBuffer__Pui16_i, bufferSize); + public void CopyToBuffer(ushort[] buffer) + => copyToBuffer(UnsafeNativeMethods.mp_ImageFrame__CopyToBuffer__Pui16_i, buffer); - public float[] CopyToFloatBuffer(int bufferSize) - => copyToBuffer(UnsafeNativeMethods.mp_ImageFrame__CopyToBuffer__Pf_i, bufferSize); - - - /// - /// Get the value of a specific channel only. - /// It's useful when only one channel is used (e.g. Hair Segmentation mask). - /// - /// - /// Specify from which channel the data will be retrieved. - /// For example, if the format is RGB, 0 means R channel, 1 means G channel, and 2 means B channel. - /// - /// - /// The array to which the output data will be written. - /// - public byte[] GetChannel(int channelNumber, bool flipVertically, byte[] colors) - { - var format = Format; - - switch (format) - { - case ImageFormat.Srgb: - if (channelNumber < 0 || channelNumber > 3) - throw new ArgumentException($"There are only 3 channels, but No. {channelNumber} is specified"); - readChannel(MutablePixelData, channelNumber, 3, Width, Height, WidthStep, flipVertically, colors); - return colors; - case ImageFormat.Srgba: - if (channelNumber < 0 || channelNumber > 4) - throw new ArgumentException($"There are only 4 channels, but No. {channelNumber} is specified"); - readChannel(MutablePixelData, channelNumber, 4, Width, Height, WidthStep, flipVertically, colors); - return colors; - default: - throw new NotImplementedException($"Currently only SRGB and SRGBA format are supported: {format}"); - } - } - - /// - /// Get the value of a specific channel only. - /// It's useful when only one channel is used (e.g. Hair Segmentation mask). - /// - /// - /// Specify from which channel the data will be retrieved. - /// For example, if the format is RGB, 0 means R channel, 1 means G channel, and 2 means B channel. - /// - public byte[] GetChannel(int channelNumber, bool flipVertically) - => GetChannel(channelNumber, flipVertically, new byte[Width * Height]); + public void CopyToBuffer(float[] buffer) + => copyToBuffer(UnsafeNativeMethods.mp_ImageFrame__CopyToBuffer__Pf_i, buffer); private delegate MpReturnCode CopyToBufferHandler(void* ptr, T* buffer, int bufferSize) where T : unmanaged; - private T[] copyToBuffer(CopyToBufferHandler handler, int bufferSize) + private void copyToBuffer(CopyToBufferHandler handler, T[] buffer) where T : unmanaged { - var buffer = new T[bufferSize]; - unsafe { fixed (T* bufferPtr = buffer) { - handler(MpPtr, bufferPtr, bufferSize).Assert(); + handler(MpPtr, bufferPtr, buffer.Length).Assert(); } } GC.KeepAlive(this); - return buffer; - } - - private T valueOrFormatException(MpReturnCode code, T value) - { - try - { - code.Assert(); - return value; - } - catch (MediapipeException) - { - throw new FormatException($"Invalid image format: {Format}"); - } - } - - /// - /// In the source array, pixels are laid out left to right, top to bottom, - /// but in the returned array, left to right, top to bottom. - /// - private static void readChannel(byte* ptr, int channelNumber, int channelCount, int width, int height, int widthStep, bool flipVertically, byte[] colors) - { - if (colors.Length != width * height) - throw new ArgumentException("colors length is invalid"); - var padding = widthStep - channelCount * width; - - unsafe - { - fixed (byte* dest = colors) - { - var pSrc = ptr; - pSrc += channelNumber; - - if (flipVertically) - { - var pDest = dest + colors.Length - 1; - - for (int i = 0; i < height; i++) - { - for (int j = 0; j < width; j++) - { - *pDest-- = *pSrc; - pSrc += channelCount; - } - pSrc += padding; - } - } - else - { - var pDest = dest + width * (height - 1); - - for (int i = 0; i < height; i++) - { - for (int j = 0; j < width; j++) - { - *pDest++ = *pSrc; - pSrc += channelCount; - } - pSrc += padding; - pDest -= 2 * width; - } - } - } - } } } } diff --git a/Mediapipe.Net/Framework/Packets/Packet.cs b/Mediapipe.Net/Framework/Packets/Packet.cs index 9ccaee1..2c1056c 100644 --- a/Mediapipe.Net/Framework/Packets/Packet.cs +++ b/Mediapipe.Net/Framework/Packets/Packet.cs @@ -136,6 +136,14 @@ namespace Mediapipe.Net.Framework.Packets GC.KeepAlive(this); return new StatusOrGpuBuffer(statusOrGpuBufferPtr); } + + public StatusOr ConsumeString() + { + UnsafeNativeMethods.mp_Packet__ConsumeString(MpPtr, out var statusOrStringPtr).Assert(); + + GC.KeepAlive(this); + return new StatusOrString(statusOrStringPtr); + } #endregion #region Validators diff --git a/Mediapipe.Net/Framework/Port/Status.cs b/Mediapipe.Net/Framework/Port/Status.cs index 52cb425..8c7dd62 100644 --- a/Mediapipe.Net/Framework/Port/Status.cs +++ b/Mediapipe.Net/Framework/Port/Status.cs @@ -2,6 +2,8 @@ // This file is part of MediaPipe.NET. // MediaPipe.NET is licensed under the MIT License. See LICENSE for details. +using System; +using System.Runtime.InteropServices; using Mediapipe.Net.Core; using Mediapipe.Net.Native; @@ -30,6 +32,69 @@ namespace Mediapipe.Net.Framework.Port Unauthenticated = 16, } + [StructLayout(LayoutKind.Sequential)] + public readonly struct StatusArgs + { + private readonly StatusCode code; + private readonly IntPtr message; + + private StatusArgs(StatusCode code, string? message = null) + { + this.code = code; + this.message = Marshal.StringToHGlobalAnsi(message); + } + + public static StatusArgs Ok() => new StatusArgs(StatusCode.Ok); + + public static StatusArgs Cancelled(string? message = null) + => new StatusArgs(StatusCode.Cancelled, message); + + public static StatusArgs Unknown(string? message = null) + => new StatusArgs(StatusCode.Unknown, message); + + public static StatusArgs InvalidArgument(string? message = null) + => new StatusArgs(StatusCode.InvalidArgument, message); + + public static StatusArgs DeadlineExceeded(string? message = null) + => new StatusArgs(StatusCode.DeadlineExceeded, message); + + public static StatusArgs NotFound(string? message = null) + => new StatusArgs(StatusCode.NotFound, message); + + public static StatusArgs AlreadyExists(string? message = null) + => new StatusArgs(StatusCode.AlreadyExists, message); + + public static StatusArgs PermissionDenied(string? message = null) + => new StatusArgs(StatusCode.PermissionDenied, message); + + public static StatusArgs ResourceExhausted(string? message = null) + => new StatusArgs(StatusCode.ResourceExhausted, message); + + public static StatusArgs FailedPrecondition(string? message = null) + => new StatusArgs(StatusCode.FailedPrecondition, message); + + public static StatusArgs Aborted(string? message = null) + => new StatusArgs(StatusCode.Aborted, message); + + public static StatusArgs OutOfRange(string? message = null) + => new StatusArgs(StatusCode.OutOfRange, message); + + public static StatusArgs Unimplemented(string? message = null) + => new StatusArgs(StatusCode.Unimplemented, message); + + public static StatusArgs Internal(string? message = null) + => new StatusArgs(StatusCode.Internal, message); + + public static StatusArgs Unavailable(string? message = null) + => new StatusArgs(StatusCode.Unavailable, message); + + public static StatusArgs DataLoss(string? message = null) + => new StatusArgs(StatusCode.DataLoss, message); + + public static StatusArgs Unauthenticated(string? message = null) + => new StatusArgs(StatusCode.Unauthenticated, message); + } + public Status(void* ptr, bool isOwner = true) : base(ptr, isOwner) { } protected override void DeleteMpPtr() => UnsafeNativeMethods.absl_Status__delete(Ptr); @@ -76,7 +141,52 @@ namespace Mediapipe.Net.Framework.Port public static Status Ok(bool isOwner = true) => Build(StatusCode.Ok, "", isOwner); + public static Status Cancelled(string message = "", bool isOwner = true) + => Build(StatusCode.Cancelled, message, isOwner); + + public static Status Unknown(string message = "", bool isOwner = true) + => Build(StatusCode.Unknown, message, isOwner); + + public static Status InvalidArgument(string message = "", bool isOwner = true) + => Build(StatusCode.InvalidArgument, message, isOwner); + + public static Status DeadlineExceeded(string message = "", bool isOwner = true) + => Build(StatusCode.DeadlineExceeded, message, isOwner); + + public static Status NotFound(string message = "", bool isOwner = true) + => Build(StatusCode.NotFound, message, isOwner); + + public static Status AlreadyExists(string message = "", bool isOwner = true) + => Build(StatusCode.AlreadyExists, message, isOwner); + + public static Status PermissionDenied(string message = "", bool isOwner = true) + => Build(StatusCode.PermissionDenied, message, isOwner); + + public static Status ResourceExhausted(string message = "", bool isOwner = true) + => Build(StatusCode.ResourceExhausted, message, isOwner); + public static Status FailedPrecondition(string message = "", bool isOwner = true) => Build(StatusCode.FailedPrecondition, message, isOwner); + + public static Status Aborted(string message = "", bool isOwner = true) + => Build(StatusCode.Aborted, message, isOwner); + + public static Status OutOfRange(string message = "", bool isOwner = true) + => Build(StatusCode.OutOfRange, message, isOwner); + + public static Status Unimplemented(string message = "", bool isOwner = true) + => Build(StatusCode.Unimplemented, message, isOwner); + + public static Status Internal(string message = "", bool isOwner = true) + => Build(StatusCode.Internal, message, isOwner); + + public static Status Unavailable(string message = "", bool isOwner = true) + => Build(StatusCode.Unavailable, message, isOwner); + + public static Status DataLoss(string message = "", bool isOwner = true) + => Build(StatusCode.DataLoss, message, isOwner); + + public static Status Unauthenticated(string message = "", bool isOwner = true) + => Build(StatusCode.Unauthenticated, message, isOwner); } } diff --git a/Mediapipe.Net/Framework/Port/StatusOr.cs b/Mediapipe.Net/Framework/Port/StatusOr.cs index 4347a63..790108d 100644 --- a/Mediapipe.Net/Framework/Port/StatusOr.cs +++ b/Mediapipe.Net/Framework/Port/StatusOr.cs @@ -16,6 +16,6 @@ namespace Mediapipe.Net.Framework.Port public virtual T? ValueOr(T? defaultValue = default) => Ok() ? Value() : defaultValue; /// Thrown when status is not ok - public abstract T Value(); + public abstract T? Value(); } } diff --git a/Mediapipe.Net/Framework/Port/StatusOrString.cs b/Mediapipe.Net/Framework/Port/StatusOrString.cs new file mode 100644 index 0000000..f29ca4f --- /dev/null +++ b/Mediapipe.Net/Framework/Port/StatusOrString.cs @@ -0,0 +1,58 @@ +// 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 Mediapipe.Net.Native; +using Mediapipe.Net.Util; + +namespace Mediapipe.Net.Framework.Port +{ + public unsafe class StatusOrString : StatusOr + { + public StatusOrString(void* ptr) : base(ptr) { } + + protected override void DeleteMpPtr() + { + UnsafeNativeMethods.mp_StatusOrString__delete(Ptr); + } + + private Status? status; + public override Status Status + { + get + { + if (status == null || status.IsDisposed) + { + UnsafeNativeMethods.mp_StatusOrString__status(MpPtr, out var statusPtr).Assert(); + + GC.KeepAlive(this); + status = new Status(statusPtr); + } + return status; + } + } + + public override bool Ok() => SafeNativeMethods.mp_StatusOrString__ok(MpPtr) > 0; + + public override string? Value() + { + var str = MarshalStringFromNative(UnsafeNativeMethods.mp_StatusOrString__value); + Dispose(); // respect move semantics + + return str; + } + + public byte[] ValueAsByteArray() + { + UnsafeNativeMethods.mp_StatusOrString__bytearray(MpPtr, out var strPtr, out var size).Assert(); + GC.KeepAlive(this); + + byte[] bytes = UnsafeUtil.SafeArrayCopy((byte*)strPtr, size); + UnsafeNativeMethods.delete_array__PKc(strPtr); + Dispose(); + + return bytes; + } + } +} diff --git a/Mediapipe.Net/Framework/ValidatedGraphConfig/EdgeInfo.cs b/Mediapipe.Net/Framework/ValidatedGraphConfig/EdgeInfo.cs new file mode 100644 index 0000000..cfa09f8 --- /dev/null +++ b/Mediapipe.Net/Framework/ValidatedGraphConfig/EdgeInfo.cs @@ -0,0 +1,25 @@ +// 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.Runtime.InteropServices; + +namespace Mediapipe.Net.Framework.ValidatedGraphConfig +{ + [StructLayout(LayoutKind.Sequential)] + public readonly struct EdgeInfo + { + public readonly int Upstream; + public readonly NodeRef ParentNode; + public readonly string? Name; + public readonly bool BackEdge; + + internal EdgeInfo(int upstream, NodeRef parentNode, string? name, bool backEdge) + { + Upstream = upstream; + ParentNode = parentNode; + Name = name; + BackEdge = backEdge; + } + } +} diff --git a/Mediapipe.Net/Framework/ValidatedGraphConfig/EdgeInfoVector.cs b/Mediapipe.Net/Framework/ValidatedGraphConfig/EdgeInfoVector.cs new file mode 100644 index 0000000..f36929b --- /dev/null +++ b/Mediapipe.Net/Framework/ValidatedGraphConfig/EdgeInfoVector.cs @@ -0,0 +1,55 @@ +// 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.Collections.Generic; +using System.Runtime.InteropServices; +using Mediapipe.Net.Native; + +namespace Mediapipe.Net.Framework.ValidatedGraphConfig +{ + [StructLayout(LayoutKind.Sequential)] + internal unsafe readonly struct EdgeInfoVector + { + private readonly void* data; + private readonly int size; + + public void Dispose() => UnsafeNativeMethods.mp_api_EdgeInfoArray__delete(data, size); + + public List Copy() + { + var edgeInfos = new List(size); + + unsafe + { + var edgeInfoPtr = (EdgeInfoTmp*)data; + + for (int i = 0; i < size; i++) + { + EdgeInfoTmp edgeInfoTmp = Marshal.PtrToStructure((IntPtr)edgeInfoPtr++); + edgeInfos.Add(edgeInfoTmp.Copy()); + } + } + + return edgeInfos; + } + + [StructLayout(LayoutKind.Sequential)] + private readonly struct EdgeInfoTmp + { + private readonly int upstream; + private readonly NodeRef parentNode; + private readonly IntPtr name; + + [MarshalAs(UnmanagedType.U1)] + private readonly bool backEdge; + + public EdgeInfo Copy() + { + string? name = Marshal.PtrToStringAnsi(this.name); + return new EdgeInfo(upstream, parentNode, name, backEdge); + } + } + } +} diff --git a/Mediapipe.Net/Framework/ValidatedGraphConfig/NodeRef.cs b/Mediapipe.Net/Framework/ValidatedGraphConfig/NodeRef.cs new file mode 100644 index 0000000..9197ded --- /dev/null +++ b/Mediapipe.Net/Framework/ValidatedGraphConfig/NodeRef.cs @@ -0,0 +1,15 @@ +// 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.Runtime.InteropServices; + +namespace Mediapipe.Net.Framework.ValidatedGraphConfig +{ + [StructLayout(LayoutKind.Sequential)] + public readonly struct NodeRef + { + public readonly NodeType Type; + public readonly int Index; + } +} diff --git a/Mediapipe.Net/Framework/ValidatedGraphConfig/NodeType.cs b/Mediapipe.Net/Framework/ValidatedGraphConfig/NodeType.cs new file mode 100644 index 0000000..4828043 --- /dev/null +++ b/Mediapipe.Net/Framework/ValidatedGraphConfig/NodeType.cs @@ -0,0 +1,15 @@ +// 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. + +namespace Mediapipe.Net.Framework.ValidatedGraphConfig +{ + public enum NodeType : int + { + Unknown = 0, + Calculator = 1, + PacketGenerator = 2, + GraphInputStream = 3, + StatusHandler = 4, + }; +} diff --git a/Mediapipe.Net/Framework/ValidatedGraphConfig/ValidatedGraphConfig.cs b/Mediapipe.Net/Framework/ValidatedGraphConfig/ValidatedGraphConfig.cs new file mode 100644 index 0000000..62ad731 --- /dev/null +++ b/Mediapipe.Net/Framework/ValidatedGraphConfig/ValidatedGraphConfig.cs @@ -0,0 +1,139 @@ +// 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.Collections.Generic; +using Google.Protobuf; +using Mediapipe.Net.Core; +using Mediapipe.Net.Framework.Packets; +using Mediapipe.Net.Framework.Port; +using Mediapipe.Net.Framework.Protobuf; +using Mediapipe.Net.Native; + +namespace Mediapipe.Net.Framework.ValidatedGraphConfig +{ + public unsafe class ValidatedGraphConfig : MpResourceHandle + { + public ValidatedGraphConfig() : base() + { + UnsafeNativeMethods.mp_ValidatedGraphConfig__(out var ptr).Assert(); + Ptr = ptr; + } + + protected override void DeleteMpPtr() => UnsafeNativeMethods.mp_ValidatedGraphConfig__delete(Ptr); + + public Status Initialize(CalculatorGraphConfig config) + { + var bytes = config.ToByteArray(); + UnsafeNativeMethods.mp_ValidatedGraphConfig__Initialize__Rcgc(MpPtr, bytes, bytes.Length, out var statusPtr).Assert(); + + GC.KeepAlive(this); + return new Status(statusPtr); + } + + public Status Initialize(string graphType) + { + UnsafeNativeMethods.mp_ValidatedGraphConfig__Initialize__PKc(MpPtr, graphType, out var statusPtr).Assert(); + + GC.KeepAlive(this); + return new Status(statusPtr); + } + + public bool Initialized() => SafeNativeMethods.mp_ValidatedGraphConfig__Initialized(MpPtr) > 0; + + public Status ValidateRequiredSidePackets(SidePackets sidePackets) + { + UnsafeNativeMethods.mp_ValidatedGraphConfig__ValidateRequiredSidePackets__Rsp(MpPtr, sidePackets.MpPtr, out var statusPtr).Assert(); + + GC.KeepAlive(sidePackets); + GC.KeepAlive(this); + return new Status(statusPtr); + } + + public CalculatorGraphConfig Config(ExtensionRegistry? extensionRegistry = null) + { + UnsafeNativeMethods.mp_ValidatedGraphConfig__Config(MpPtr, out var serializedProto).Assert(); + GC.KeepAlive(this); + + var parser = extensionRegistry == null ? CalculatorGraphConfig.Parser : CalculatorGraphConfig.Parser.WithExtensionRegistry(extensionRegistry); + var config = serializedProto.Deserialize(parser); + serializedProto.Dispose(); + + return config; + } + + public List InputStreamInfos() + { + UnsafeNativeMethods.mp_ValidatedGraphConfig__InputStreamInfos(MpPtr, out var edgeInfoVector).Assert(); + GC.KeepAlive(this); + + var edgeInfos = edgeInfoVector.Copy(); + edgeInfoVector.Dispose(); + return edgeInfos; + } + + public List OutputStreamInfos() + { + UnsafeNativeMethods.mp_ValidatedGraphConfig__OutputStreamInfos(MpPtr, out var edgeInfoVector).Assert(); + GC.KeepAlive(this); + + var edgeInfos = edgeInfoVector.Copy(); + edgeInfoVector.Dispose(); + return edgeInfos; + } + + public List InputSidePacketInfos() + { + UnsafeNativeMethods.mp_ValidatedGraphConfig__InputSidePacketInfos(MpPtr, out var edgeInfoVector).Assert(); + GC.KeepAlive(this); + + var edgeInfos = edgeInfoVector.Copy(); + edgeInfoVector.Dispose(); + return edgeInfos; + } + + public List OutputSidePacketInfos() + { + UnsafeNativeMethods.mp_ValidatedGraphConfig__OutputSidePacketInfos(MpPtr, out var edgeInfoVector).Assert(); + GC.KeepAlive(this); + + var edgeInfos = edgeInfoVector.Copy(); + edgeInfoVector.Dispose(); + return edgeInfos; + } + + public int OutputStreamIndex(string name) + => SafeNativeMethods.mp_ValidatedGraphConfig__OutputStreamIndex__PKc(MpPtr, name); + + public int OutputSidePacketIndex(string name) + => SafeNativeMethods.mp_ValidatedGraphConfig__OutputSidePacketIndex__PKc(MpPtr, name); + + public int OutputStreamToNode(string name) + => SafeNativeMethods.mp_ValidatedGraphConfig__OutputStreamToNode__PKc(MpPtr, name); + + public StatusOrString RegisteredSidePacketTypeName(string name) + { + UnsafeNativeMethods.mp_ValidatedGraphConfig__RegisteredSidePacketTypeName(MpPtr, name, out var statusOrStringPtr).Assert(); + + GC.KeepAlive(this); + return new StatusOrString(statusOrStringPtr); + } + + public StatusOrString RegisteredStreamTypeName(string name) + { + UnsafeNativeMethods.mp_ValidatedGraphConfig__RegisteredStreamTypeName(MpPtr, name, out var statusOrStringPtr).Assert(); + + GC.KeepAlive(this); + return new StatusOrString(statusOrStringPtr); + } + + public string? Package() => MarshalStringFromNative(UnsafeNativeMethods.mp_ValidatedGraphConfig__Package); + + public static bool IsReservedExecutorName(string name) + => SafeNativeMethods.mp_ValidatedGraphConfig_IsReservedExecutorName(name) > 0; + + public bool IsExternalSidePacket(string name) + => SafeNativeMethods.mp_ValidatedGraphConfig__IsExternalSidePacket__PKc(MpPtr, name) > 0; + } +} diff --git a/Mediapipe.Net/Gpu/GlCalculatorHelper.cs b/Mediapipe.Net/Gpu/GlCalculatorHelper.cs index 8dac91e..8b87b74 100644 --- a/Mediapipe.Net/Gpu/GlCalculatorHelper.cs +++ b/Mediapipe.Net/Gpu/GlCalculatorHelper.cs @@ -3,7 +3,6 @@ // MediaPipe.NET is licensed under the MIT License. See LICENSE for details. using System; -using System.Runtime.InteropServices; using System.Runtime.Versioning; using Mediapipe.Net.Core; using Mediapipe.Net.Framework.Format; @@ -14,8 +13,8 @@ namespace Mediapipe.Net.Gpu { public unsafe class GlCalculatorHelper : MpResourceHandle { - public delegate void* NativeGlStatusFunction(); - public delegate Status GlStatusFunction(); + public delegate Status.StatusArgs NativeGlStatusFunction(); + public delegate void GlFunction(); public GlCalculatorHelper() : base() { @@ -45,34 +44,20 @@ namespace Mediapipe.Net.Gpu return new Status(statusPtr); } - public Status RunInGlContext(GlStatusFunction glStatusFunc) + public Status RunInGlContext(GlFunction glFunction) { - Status? tmpStatus = null; - - NativeGlStatusFunction nativeGlStatusFunc = () => + return RunInGlContext(() => { try { - tmpStatus = glStatusFunc(); + glFunction(); + return Status.StatusArgs.Ok(); } catch (Exception e) { - tmpStatus = Status.FailedPrecondition(e.ToString()); + return Status.StatusArgs.Internal(e.ToString()); } - return tmpStatus.MpPtr; - }; - - // Was previously `GCHandleType.Pinned`. It had to be changed because - // the `NativeGlStatusFunction` delegate type is non-blittable. - // Using `GCHandleType.Normal` should be fine as it seems that all we - // need to do is to make sure that the delegate doesn't get garbage-collected. - var nativeGlStatusFuncHandle = GCHandle.Alloc(nativeGlStatusFunc, GCHandleType.Normal); - var status = RunInGlContext(nativeGlStatusFunc); - nativeGlStatusFuncHandle.Free(); - - if (tmpStatus != null) - tmpStatus.Dispose(); - return status; + }); } public GlTexture CreateSourceTexture(ImageFrame imageFrame) diff --git a/Mediapipe.Net/Gpu/GlSyncPoint.cs b/Mediapipe.Net/Gpu/GlSyncPoint.cs index c6a0271..8c572fc 100644 --- a/Mediapipe.Net/Gpu/GlSyncPoint.cs +++ b/Mediapipe.Net/Gpu/GlSyncPoint.cs @@ -12,7 +12,7 @@ namespace Mediapipe.Net.Gpu { private SharedPtrHandle? sharedPtrHandle; - public GlSyncPoint(void* ptr) : base(ptr) + public GlSyncPoint(void* ptr) : base() { sharedPtrHandle = new SharedGlSyncPointPtr(ptr); Ptr = sharedPtrHandle.Get(); diff --git a/Mediapipe.Net/Gpu/GlTextureBuffer.cs b/Mediapipe.Net/Gpu/GlTextureBuffer.cs index c6e7eb1..d8a7154 100644 --- a/Mediapipe.Net/Gpu/GlTextureBuffer.cs +++ b/Mediapipe.Net/Gpu/GlTextureBuffer.cs @@ -29,7 +29,7 @@ namespace Mediapipe.Net.Gpu /// Make sure that this function doesn't throw exceptions and won't be GCed. /// public GlTextureBuffer(uint target, uint name, int width, int height, - GpuBufferFormat format, DeletionCallback callback, GlContext? glContext) + GpuBufferFormat format, DeletionCallback callback, GlContext? glContext) : base() { var sharedContextPtr = glContext == null ? null : glContext.SharedPtr; UnsafeNativeMethods.mp_SharedGlTextureBuffer__ui_ui_i_i_ui_PF_PSgc( diff --git a/Mediapipe.Net/Gpu/GpuBuffer.cs b/Mediapipe.Net/Gpu/GpuBuffer.cs index 67522a0..c73ce53 100644 --- a/Mediapipe.Net/Gpu/GpuBuffer.cs +++ b/Mediapipe.Net/Gpu/GpuBuffer.cs @@ -22,10 +22,6 @@ namespace Mediapipe.Net.Gpu protected override void DeleteMpPtr() => UnsafeNativeMethods.mp_GpuBuffer__delete(Ptr); - [SupportedOSPlatform("Linux"), SupportedOSPlatform("Android")] - public GlTextureBuffer GetGlTextureBuffer() - => new GlTextureBuffer(SafeNativeMethods.mp_GpuBuffer__GetGlTextureBufferSharedPtr(MpPtr), false); - public GpuBufferFormat Format => SafeNativeMethods.mp_GpuBuffer__format(MpPtr); public int Width => SafeNativeMethods.mp_GpuBuffer__width(MpPtr); diff --git a/Mediapipe.Net/Gpu/GpuBufferFormatExtension.cs b/Mediapipe.Net/Gpu/GpuBufferFormatExtension.cs index 536e647..63ebc10 100644 --- a/Mediapipe.Net/Gpu/GpuBufferFormatExtension.cs +++ b/Mediapipe.Net/Gpu/GpuBufferFormatExtension.cs @@ -2,14 +2,14 @@ // This file is part of MediaPipe.NET. // MediaPipe.NET is licensed under the MIT License. See LICENSE for details. -using Mediapipe.Net.Framework.Format; +using Mediapipe.Net.Framework.Protobuf; using Mediapipe.Net.Native; namespace Mediapipe.Net.Gpu { public static class GpuBufferFormatExtension { - public static ImageFormat ImageFormatFor(this GpuBufferFormat gpuBufferFormat) + public static ImageFormat.Types.Format ImageFormatFor(this GpuBufferFormat gpuBufferFormat) => SafeNativeMethods.mp__ImageFormatForGpuBufferFormat__ui(gpuBufferFormat); public static GlTextureInfo GlTextureInfoFor(this GpuBufferFormat gpuBufferFormat, int plane, GlVersion glVersion = GlVersion.KGles3) diff --git a/Mediapipe.Net/Mediapipe.Net.csproj b/Mediapipe.Net/Mediapipe.Net.csproj index b13894f..565484e 100644 --- a/Mediapipe.Net/Mediapipe.Net.csproj +++ b/Mediapipe.Net/Mediapipe.Net.csproj @@ -11,7 +11,7 @@ true Mediapipe.Net - 0.8.9.1 + 1.0.0-alpha2 homuler;Vignette Google;Mediapipe;Tracking;Media Analysis Mediapipe.Net @@ -22,8 +22,8 @@ - - + + diff --git a/Mediapipe.Net/Native/SafeNativeMethods/External/Stdlib.cs b/Mediapipe.Net/Native/SafeNativeMethods/External/Stdlib.cs new file mode 100644 index 0000000..b903ca4 --- /dev/null +++ b/Mediapipe.Net/Native/SafeNativeMethods/External/Stdlib.cs @@ -0,0 +1,15 @@ +// 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.Diagnostics.Contracts; +using System.Runtime.InteropServices; + +namespace Mediapipe.Net.Native +{ + internal unsafe partial class SafeNativeMethods + { + [Pure, DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] + public static extern byte mp_StatusOrString__ok(void* statusOrString); + } +} diff --git a/Mediapipe.Net/Native/SafeNativeMethods/Framework/Format/ImageFormat.cs b/Mediapipe.Net/Native/SafeNativeMethods/Framework/Format/ImageFormat.cs index 42b5d4d..cd353d7 100644 --- a/Mediapipe.Net/Native/SafeNativeMethods/Framework/Format/ImageFormat.cs +++ b/Mediapipe.Net/Native/SafeNativeMethods/Framework/Format/ImageFormat.cs @@ -4,7 +4,7 @@ using System.Diagnostics.Contracts; using System.Runtime.InteropServices; -using Mediapipe.Net.Framework.Format; +using Mediapipe.Net.Framework.Protobuf; namespace Mediapipe.Net.Native { @@ -21,7 +21,7 @@ namespace Mediapipe.Net.Native void* imageFrame, uint alignmentBoundary, out bool value); [Pure, DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] - public static extern ImageFormat mp_ImageFrame__Format(void* imageFrame); + public static extern ImageFormat.Types.Format mp_ImageFrame__Format(void* imageFrame); [Pure, DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] public static extern int mp_ImageFrame__Width(void* imageFrame); diff --git a/Mediapipe.Net/Native/SafeNativeMethods/Framework/ValidatedGraphConfig.cs b/Mediapipe.Net/Native/SafeNativeMethods/Framework/ValidatedGraphConfig.cs new file mode 100644 index 0000000..ae9e4a9 --- /dev/null +++ b/Mediapipe.Net/Native/SafeNativeMethods/Framework/ValidatedGraphConfig.cs @@ -0,0 +1,33 @@ +// 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.Diagnostics.Contracts; +using System.Runtime.InteropServices; + +namespace Mediapipe.Net.Native +{ + internal unsafe partial class SafeNativeMethods + { + [Pure, DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] + public static extern byte mp_ValidatedGraphConfig__Initialized(void* config); + +#pragma warning disable CA2101 + [Pure, DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true, CharSet = CharSet.Ansi)] + public static extern int mp_ValidatedGraphConfig__OutputStreamIndex__PKc(void* config, string name); + + [Pure, DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true, CharSet = CharSet.Ansi)] + public static extern int mp_ValidatedGraphConfig__OutputSidePacketIndex__PKc(void* config, string name); + + [Pure, DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true, CharSet = CharSet.Ansi)] + public static extern int mp_ValidatedGraphConfig__OutputStreamToNode__PKc(void* config, string name); + + + [Pure, DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true, CharSet = CharSet.Ansi)] + public static extern byte mp_ValidatedGraphConfig_IsReservedExecutorName(string name); + + [Pure, DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true, CharSet = CharSet.Ansi)] + public static extern byte mp_ValidatedGraphConfig__IsExternalSidePacket__PKc(void* config, string name); +#pragma warning restore CA2101 + } +} diff --git a/Mediapipe.Net/Native/SafeNativeMethods/Gpu/GpuBuffer.cs b/Mediapipe.Net/Native/SafeNativeMethods/Gpu/GpuBuffer.cs index b3071d2..1c0af37 100644 --- a/Mediapipe.Net/Native/SafeNativeMethods/Gpu/GpuBuffer.cs +++ b/Mediapipe.Net/Native/SafeNativeMethods/Gpu/GpuBuffer.cs @@ -4,17 +4,12 @@ using System.Diagnostics.Contracts; using System.Runtime.InteropServices; -using System.Runtime.Versioning; using Mediapipe.Net.Gpu; namespace Mediapipe.Net.Native { internal unsafe partial class SafeNativeMethods : NativeMethods { - [SupportedOSPlatform("Linux"), SupportedOSPlatform("Android")] - [Pure, DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] - public static extern void* mp_GpuBuffer__GetGlTextureBufferSharedPtr(void* gpuBuffer); - [Pure, DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] public static extern int mp_GpuBuffer__width(void* gpuBuffer); diff --git a/Mediapipe.Net/Native/SafeNativeMethods/Gpu/GpuBufferFormat.cs b/Mediapipe.Net/Native/SafeNativeMethods/Gpu/GpuBufferFormat.cs index 7800dd2..9bb4bec 100644 --- a/Mediapipe.Net/Native/SafeNativeMethods/Gpu/GpuBufferFormat.cs +++ b/Mediapipe.Net/Native/SafeNativeMethods/Gpu/GpuBufferFormat.cs @@ -4,7 +4,7 @@ using System.Diagnostics.Contracts; using System.Runtime.InteropServices; -using Mediapipe.Net.Framework.Format; +using Mediapipe.Net.Framework.Protobuf; using Mediapipe.Net.Gpu; namespace Mediapipe.Net.Native @@ -12,9 +12,9 @@ namespace Mediapipe.Net.Native internal unsafe partial class SafeNativeMethods : NativeMethods { [Pure, DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] - public static extern ImageFormat mp__ImageFormatForGpuBufferFormat__ui(GpuBufferFormat format); + public static extern ImageFormat.Types.Format mp__ImageFormatForGpuBufferFormat__ui(GpuBufferFormat format); [Pure, DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] - public static extern ImageFormat mp__GpuBufferFormatForImageFormat__ui(ImageFormat format); + public static extern ImageFormat.Types.Format mp__GpuBufferFormatForImageFormat__ui(ImageFormat.Types.Format format); } } diff --git a/Mediapipe.Net/Native/UnsafeNativeMethods/External/Protobuf.cs b/Mediapipe.Net/Native/UnsafeNativeMethods/External/Protobuf.cs index 1f14af1..e33b633 100644 --- a/Mediapipe.Net/Native/UnsafeNativeMethods/External/Protobuf.cs +++ b/Mediapipe.Net/Native/UnsafeNativeMethods/External/Protobuf.cs @@ -10,11 +10,13 @@ namespace Mediapipe.Net.Native internal unsafe partial class UnsafeNativeMethods : NativeMethods { [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] - public static extern MpReturnCode google_protobuf__SetLogHandler__PF( - Protobuf.ProtobufLogHandler logHandler); + public static extern MpReturnCode google_protobuf__SetLogHandler__PF(Protobuf.LogHandler logHandler); [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] - public static extern void mp_api_SerializedProtoArray__delete(void* serializedProtoVectorData); + public static extern MpReturnCode google_protobuf__ResetLogHandler(); + + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] + public static extern void mp_api_SerializedProtoArray__delete(void* serializedProtoVectorData, int size); #region MessageProto [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] diff --git a/Mediapipe.Net/Native/UnsafeNativeMethods/External/Stdlib.cs b/Mediapipe.Net/Native/UnsafeNativeMethods/External/Stdlib.cs index 262f105..c91f9fd 100644 --- a/Mediapipe.Net/Native/UnsafeNativeMethods/External/Stdlib.cs +++ b/Mediapipe.Net/Native/UnsafeNativeMethods/External/Stdlib.cs @@ -21,5 +21,19 @@ namespace Mediapipe.Net.Native [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] public static extern void std_string__swap__Rstr(void* src, void* dst); #endregion + + #region StatusOrString + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] + public static extern void mp_StatusOrString__delete(void* statusOrString); + + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] + public static extern MpReturnCode mp_StatusOrString__status(void* statusOrString, out void* status); + + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] + public static extern MpReturnCode mp_StatusOrString__value(void* statusOrString, out sbyte* value); + + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] + public static extern MpReturnCode mp_StatusOrString__bytearray(void* statusOrString, out sbyte* value, out int size); + #endregion } } diff --git a/Mediapipe.Net/Native/UnsafeNativeMethods/Framework/CalculatorGraph.cs b/Mediapipe.Net/Native/UnsafeNativeMethods/Framework/CalculatorGraph.cs index c82f232..8a746bb 100644 --- a/Mediapipe.Net/Native/UnsafeNativeMethods/Framework/CalculatorGraph.cs +++ b/Mediapipe.Net/Native/UnsafeNativeMethods/Framework/CalculatorGraph.cs @@ -14,9 +14,6 @@ namespace Mediapipe.Net.Native [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] public static extern MpReturnCode mp_CalculatorGraph__(out void* graph); - [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true, CharSet = CharSet.Ansi)] - public static extern MpReturnCode mp_CalculatorGraph__PKc(string textFormatConfig, out void* graph); - [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] public static extern MpReturnCode mp_CalculatorGraph__PKc_i(byte[] serializedConfig, int size, out void* graph); @@ -35,7 +32,7 @@ namespace Mediapipe.Net.Native [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true, CharSet = CharSet.Ansi)] public static extern MpReturnCode mp_CalculatorGraph__ObserveOutputStream__PKc_PF_b(void* graph, string streamName, - CalculatorGraph.NativePacketCallback packetCallback, + int streamId, CalculatorGraph.NativePacketCallback packetCallback, bool observeTimestampBounds, out void* status); [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true, CharSet = CharSet.Ansi)] diff --git a/Mediapipe.Net/Native/UnsafeNativeMethods/Framework/Format/ImageFormat.cs b/Mediapipe.Net/Native/UnsafeNativeMethods/Framework/Format/ImageFormat.cs index 0b37c2f..5e311f4 100644 --- a/Mediapipe.Net/Native/UnsafeNativeMethods/Framework/Format/ImageFormat.cs +++ b/Mediapipe.Net/Native/UnsafeNativeMethods/Framework/Format/ImageFormat.cs @@ -3,7 +3,7 @@ // MediaPipe.NET is licensed under the MIT License. See LICENSE for details. using System.Runtime.InteropServices; -using Mediapipe.Net.Framework.Format; +using Mediapipe.Net.Framework.Protobuf; namespace Mediapipe.Net.Native { @@ -14,11 +14,11 @@ namespace Mediapipe.Net.Native [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] public static extern MpReturnCode mp_ImageFrame__ui_i_i_ui( - ImageFormat format, int width, int height, uint alignmentBoundary, out void* imageFrame); + ImageFormat.Types.Format format, int width, int height, uint alignmentBoundary, out void* imageFrame); [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] public static extern MpReturnCode mp_ImageFrame__ui_i_i_i_Pui8( - ImageFormat format, int width, int height, int widthStep, byte* pixelData, + ImageFormat.Types.Format format, int width, int height, int widthStep, byte* pixelData, out void* imageFrame); [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] diff --git a/Mediapipe.Net/Native/UnsafeNativeMethods/Framework/Packet.cs b/Mediapipe.Net/Native/UnsafeNativeMethods/Framework/Packet.cs index 9754e72..6782625 100644 --- a/Mediapipe.Net/Native/UnsafeNativeMethods/Framework/Packet.cs +++ b/Mediapipe.Net/Native/UnsafeNativeMethods/Framework/Packet.cs @@ -31,6 +31,9 @@ namespace Mediapipe.Net.Native [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] public static extern MpReturnCode mp_Packet__RegisteredTypeName(void* packet, out sbyte* str); + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] + public static extern MpReturnCode mp_Packet__ConsumeString(void* packet, out void* statusOrValue); + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] public static extern MpReturnCode mp_Packet__DebugTypeName(void* packet, out sbyte* str); #endregion diff --git a/Mediapipe.Net/Native/UnsafeNativeMethods/Framework/ValidatedGraphConfig.cs b/Mediapipe.Net/Native/UnsafeNativeMethods/Framework/ValidatedGraphConfig.cs new file mode 100644 index 0000000..2c828e7 --- /dev/null +++ b/Mediapipe.Net/Native/UnsafeNativeMethods/Framework/ValidatedGraphConfig.cs @@ -0,0 +1,59 @@ +// 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.Runtime.InteropServices; +using Mediapipe.Net.External; +using Mediapipe.Net.Framework.ValidatedGraphConfig; + +namespace Mediapipe.Net.Native +{ + internal unsafe partial class UnsafeNativeMethods + { + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] + public static extern MpReturnCode mp_ValidatedGraphConfig__(out void* config); + + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] + public static extern void mp_ValidatedGraphConfig__delete(void* config); + + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] + public static extern MpReturnCode mp_ValidatedGraphConfig__Initialize__Rcgc(void* config, byte[] serializedConfig, int size, out void* status); + +#pragma warning disable CA2101 + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true, CharSet = CharSet.Ansi)] + public static extern MpReturnCode mp_ValidatedGraphConfig__Initialize__PKc(void* config, string graphType, out void* status); +#pragma warning restore CA2101 + + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] + public static extern MpReturnCode mp_ValidatedGraphConfig__ValidateRequiredSidePackets__Rsp(void* config, void* sidePackets, out void* status); + + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] + public static extern MpReturnCode mp_ValidatedGraphConfig__Config(void* config, out SerializedProto serializedProto); + + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] + public static extern MpReturnCode mp_ValidatedGraphConfig__InputStreamInfos(void* config, out EdgeInfoVector edgeInfoVector); + + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] + public static extern MpReturnCode mp_ValidatedGraphConfig__OutputStreamInfos(void* config, out EdgeInfoVector edgeInfoVector); + + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] + public static extern MpReturnCode mp_ValidatedGraphConfig__InputSidePacketInfos(void* config, out EdgeInfoVector edgeInfoVector); + + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] + public static extern MpReturnCode mp_ValidatedGraphConfig__OutputSidePacketInfos(void* config, out EdgeInfoVector edgeInfoVector); + +#pragma warning disable CA2101 + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true, CharSet = CharSet.Ansi)] + public static extern MpReturnCode mp_ValidatedGraphConfig__RegisteredSidePacketTypeName(void* config, string name, out void* statusOrString); + + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true, CharSet = CharSet.Ansi)] + public static extern MpReturnCode mp_ValidatedGraphConfig__RegisteredStreamTypeName(void* config, string name, out void* statusOrString); +#pragma warning restore CA2101 + + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] + public static extern MpReturnCode mp_ValidatedGraphConfig__Package(void* config, out sbyte* str); + + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] + public static extern void mp_api_EdgeInfoArray__delete(void* data, int size); + } +} diff --git a/Mediapipe.Net/Native/UnsafeNativeMethods/FreeHGlobal.cs b/Mediapipe.Net/Native/UnsafeNativeMethods/FreeHGlobal.cs new file mode 100644 index 0000000..9a1673f --- /dev/null +++ b/Mediapipe.Net/Native/UnsafeNativeMethods/FreeHGlobal.cs @@ -0,0 +1,31 @@ +// 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.Runtime.InteropServices; + +namespace Mediapipe.Net.Native +{ + internal unsafe partial class UnsafeNativeMethods : NativeMethods + { + // This is required to be a field in order to bypass the GC. + private static readonly FreeHGlobalDelegate freeHGlobalDelegate; + + static UnsafeNativeMethods() + { + freeHGlobalDelegate = new FreeHGlobalDelegate(freeHGlobal); + mp_api__SetFreeHGlobal(freeHGlobalDelegate); + } + + private delegate void FreeHGlobalDelegate(IntPtr hglobal); + + private static void freeHGlobal(IntPtr hglobal) + { + Marshal.FreeHGlobal(hglobal); + } + + [DllImport(MEDIAPIPE_LIBRARY, ExactSpelling = true)] + private static extern void mp_api__SetFreeHGlobal(FreeHGlobalDelegate freeHGlobal); + } +} diff --git a/Mediapipe.Net/Solutions/GpuSolution.cs b/Mediapipe.Net/Solutions/GpuSolution.cs index 9aab459..c8b917f 100644 --- a/Mediapipe.Net/Solutions/GpuSolution.cs +++ b/Mediapipe.Net/Solutions/GpuSolution.cs @@ -8,7 +8,6 @@ using Mediapipe.Net.Core; using Mediapipe.Net.Framework; using Mediapipe.Net.Framework.Format; using Mediapipe.Net.Framework.Packets; -using Mediapipe.Net.Framework.Port; using Mediapipe.Net.Gpu; namespace Mediapipe.Net.Solutions @@ -50,8 +49,6 @@ namespace Mediapipe.Net.Solutions Timestamp timestamp = new Timestamp(SimulatedTimestamp); Packet packet = PacketFactory.GpuBufferPacket(gpuBuffer, timestamp); inputs.Add(GpuBufferInput, packet); - - return Status.Ok(); }).AssertOk(); return Process(inputs); @@ -74,8 +71,6 @@ namespace Mediapipe.Net.Solutions } Gl.Flush(); texture.Release(); - - return Status.Ok(); }).AssertOk(); if (outFrame == null) diff --git a/Mediapipe.Net/Solutions/Solution.cs b/Mediapipe.Net/Solutions/Solution.cs index 3a5da7f..3e91f30 100644 --- a/Mediapipe.Net/Solutions/Solution.cs +++ b/Mediapipe.Net/Solutions/Solution.cs @@ -10,7 +10,6 @@ using Mediapipe.Net.Core; using Mediapipe.Net.Framework; using Mediapipe.Net.Framework.Format; using Mediapipe.Net.Framework.Packets; -using Mediapipe.Net.Framework.Port; namespace Mediapipe.Net.Solutions { @@ -49,7 +48,6 @@ namespace Mediapipe.Net.Solutions packet.PacketType = packetType; lock (GraphOutputs) GraphOutputs.Add(output, packet.Get()); - return Status.Ok(); }, out GCHandle handle).AssertOk(); observeStreamHandles.Add(output, handle); }