### FaceMesh Script Example Source: https://github.com/homuler/mediapipeunityplugin/wiki/Getting-Started A complete C# script for Unity that demonstrates setting up a webcam, initializing the Mediapipe graph, preparing necessary assets using `LocalResourceManager`, and processing video frames for face mesh detection. It includes setup for input textures, display, and proper cleanup. ```csharp using System.Collections; using UnityEngine; using UnityEngine.UI; namespace Mediapipe.Unity.Tutorial { public class FaceMesh : MonoBehaviour { [SerializeField] private TextAsset _configAsset; [SerializeField] private RawImage _screen; [SerializeField] private int _width; [SerializeField] private int _height; [SerializeField] private int _fps; private CalculatorGraph _graph; private ResourceManager _resourceManager; private WebCamTexture _webCamTexture; private Texture2D _inputTexture; private Color32[] _pixelData; private IEnumerator Start() { if (WebCamTexture.devices.Length == 0) { throw new System.Exception("Web Camera devices are not found"); } var webCamDevice = WebCamTexture.devices[0]; _webCamTexture = new WebCamTexture(webCamDevice.name, _width, _height, _fps); _webCamTexture.Play(); yield return new WaitUntil(() => _webCamTexture.width > 16); _screen.rectTransform.sizeDelta = new Vector2(_width, _height); _screen.texture = _webCamTexture; _inputTexture = new Texture2D(_width, _height, TextureFormat.RGBA32, false); _pixelData = new Color32[_width * _height]; _resourceManager = new LocalResourceManager(); yield return _resourceManager.PrepareAssetAsync("face_detection_short_range.bytes"); yield return _resourceManager.PrepareAssetAsync("face_landmark_with_attention.bytes"); _graph = new CalculatorGraph(_configAsset.text); _graph.StartRun(); while (true) { _inputTexture.SetPixels32(_webCamTexture.GetPixels32(_pixelData)); var imageFrame = new ImageFrame(ImageFormat.Types.Format.Srgba, _width, _height, _width * 4, _inputTexture.GetRawTextureData()); _graph.AddPacketToInputStream("input_video", Packet.CreateImageFrame(imageFrame)); yield return new WaitForEndOfFrame(); } } private void OnDestroy() { if (_webCamTexture != null) { _webCamTexture.Stop(); } if (_graph != null) { try { _graph.CloseInputStream("input_video"); _graph.WaitUntilDone(); } finally { _graph.Dispose(); _graph = null; } } } } } ``` -------------------------------- ### Complete HelloWorld Example in C# Source: https://github.com/homuler/mediapipeunityplugin/wiki/Getting-Started A complete example demonstrating the initialization, execution, input sending, and cleanup of a MediaPipe CalculatorGraph within a Unity MonoBehaviour. This script can be attached to a GameObject to run the MediaPipe tutorial. ```csharp using UnityEngine; namespace Mediapipe.Unity.Tutorial { public class HelloWorld : MonoBehaviour { private void Start() { var configText = @" input_stream: \"in\"\noutput_stream: \"out\"\nnode {\n calculator: \"PassThroughCalculator\"\n input_stream: \"in\"\n output_stream: \"out1\"\n}\nnode {\n calculator: \"PassThroughCalculator\"\n input_stream: \"out1\"\n output_stream: \"out\"\n} "; var graph = new CalculatorGraph(configText); graph.StartRun(); for (var i = 0; i < 10; i++) { var input = Packet.CreateStringAt("Hello World!", i); graph.AddPacketToInputStream("in", input); } graph.CloseInputStream("in"); graph.WaitUntilDone(); graph.Dispose(); Debug.Log("Done"); } } } ``` -------------------------------- ### Initialize and Run Face Mesh Detection in Unity Source: https://github.com/homuler/mediapipeunityplugin/wiki/Getting-Started This C# script initializes the Mediapipe Face Mesh solution within a Unity application. It sets up the webcam, configures the calculator graph, and starts processing video frames to detect face landmarks. The script includes setup for input textures, resource management, and output stream handling for face landmarks. ```csharp using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using Stopwatch = System.Diagnostics.Stopwatch; namespace Mediapipe.Unity.Tutorial { public class FaceMesh : MonoBehaviour { [SerializeField] private TextAsset _configAsset; [SerializeField] private RawImage _screen; [SerializeField] private int _width; [SerializeField] private int _height; [SerializeField] private int _fps; [SerializeField] private MultiFaceLandmarkListAnnotationController _multiFaceLandmarksAnnotationController; private CalculatorGraph _graph; private OutputStream> _multiFaceLandmarksStream; private ResourceManager _resourceManager; private WebCamTexture _webCamTexture; private Texture2D _inputTexture; private Color32[] _inputPixelData; private IEnumerator Start() { if (WebCamTexture.devices.Length == 0) { throw new System.Exception("Web Camera devices are not found"); } var webCamDevice = WebCamTexture.devices[0]; _webCamTexture = new WebCamTexture(webCamDevice.name, _width, _height, _fps); _webCamTexture.Play(); yield return new WaitUntil(() => _webCamTexture.width > 16); _screen.rectTransform.sizeDelta = new Vector2(_width, _height); _inputTexture = new Texture2D(_width, _height, TextureFormat.RGBA32, false); _inputPixelData = new Color32[_width * _height]; _screen.texture = _webCamTexture; _resourceManager = new LocalResourceManager(); yield return _resourceManager.PrepareAssetAsync("face_detection_short_range.bytes"); yield return _resourceManager.PrepareAssetAsync("face_landmark_with_attention.bytes"); var stopwatch = new Stopwatch(); _graph = new CalculatorGraph(_configAsset.text); _multiFaceLandmarksStream = new OutputStream>(_graph, "multi_face_landmarks"); _multiFaceLandmarksStream.StartPolling(); _graph.StartRun(); stopwatch.Start(); var screenRect = _screen.GetComponent().rect; while (true) { _inputTexture.SetPixels32(_webCamTexture.GetPixels32(_inputPixelData)); var imageFrame = new ImageFrame(ImageFormat.Types.Format.Srgba, _width, _height, _width * 4, _inputTexture.GetRawTextureData()); var currentTimestamp = stopwatch.ElapsedTicks / (System.TimeSpan.TicksPerMillisecond / 1000); _graph.AddPacketToInputStream("input_video", Packet.CreateImageFrameAt(imageFrame, currentTimestamp)); var task = _multiFaceLandmarksStream.WaitNextAsync(); yield return new WaitUntil(() => task.IsCompleted); var result = task.Result; if (!result.ok) { throw new Exception("Something went wrong"); } var multiFaceLandmarksPacket = result.packet; if (multiFaceLandmarksPacket != null) { var multiFaceLandmarks = multiFaceLandmarksPacket.Get(NormalizedLandmarkList.Parser); _multiFaceLandmarksAnnotationController.DrawNow(multiFaceLandmarks); ``` -------------------------------- ### Full HelloWorld Implementation Source: https://github.com/homuler/mediapipeunityplugin/blob/master/docs/Tutorial-Hello-World.md A complete Unity MonoBehaviour example that initializes a MediaPipe graph, sends string packets to an input stream, and polls the output stream for results. ```csharp using UnityEngine; namespace Mediapipe.Unity.Tutorial { public class HelloWorld : MonoBehaviour { private void Start() { var configText = @"input_stream: ""in"" output_stream: ""out"" node { calculator: ""PassThroughCalculator"" input_stream: ""in"" output_stream: ""out1"" } node { calculator: ""PassThroughCalculator"" input_stream: ""out1"" output_stream: ""out"" }"; using var graph = new CalculatorGraph(configText); using var poller = graph.AddOutputStreamPoller("out"); graph.StartRun(); for (var i = 0; i < 10; i++) { var input = Packet.CreateStringAt("Hello World!", i); graph.AddPacketToInputStream("in", input); } graph.CloseInputStream("in"); using var output = new Packet(); while (poller.Next(output)) { Debug.Log(output.Get()); } graph.WaitUntilDone(); Debug.Log("Done"); } } } ``` -------------------------------- ### Full FaceMesh Implementation Example Source: https://github.com/homuler/mediapipeunityplugin/wiki/Getting-Started A complete MonoBehaviour implementation that captures webcam input, processes it through a MediaPipe graph, and renders the output to a UI RawImage. ```csharp using System.Collections; using UnityEngine; using UnityEngine.UI; using Stopwatch = System.Diagnostics.Stopwatch; namespace Mediapipe.Unity.Tutorial { public class FaceMesh : MonoBehaviour { [SerializeField] private TextAsset _configAsset; [SerializeField] private RawImage _screen; [SerializeField] private int _width; [SerializeField] private int _height; [SerializeField] private int _fps; private CalculatorGraph _graph; private OutputStream _outputVideoStream; private IEnumerator Start() { _graph = new CalculatorGraph(_configAsset.text); _outputVideoStream = new OutputStream(_graph, "output_video"); _outputVideoStream.StartPolling(); _graph.StartRun(); while (true) { // Capture and process logic omitted for brevity var task = _outputVideoStream.WaitNextAsync(); yield return new WaitUntil(() => task.IsCompleted); // Handle output packet... } } } } ``` -------------------------------- ### Initialize and Run MediaPipe CalculatorGraph in C# Source: https://github.com/homuler/mediapipeunityplugin/blob/master/README.md Demonstrates how to instantiate a CalculatorGraph with a configuration string, add input packets, and poll output packets. This example shows the basic lifecycle of a MediaPipe graph execution within a Unity MonoBehaviour. ```csharp using Mediapipe; using UnityEngine; public sealed class HelloWorld : MonoBehaviour { private const string _ConfigText = @"input_stream: ""in"" output_stream: ""out"" node { calculator: ""PassThroughCalculator"" input_stream: ""in"" output_stream: ""out1"" } node { calculator: ""PassThroughCalculator"" input_stream: ""out1"" output_stream: ""out"" } "; private void Start() { using var graph = new CalculatorGraph(_ConfigText); using var poller = graph.AddOutputStreamPoller("out"); graph.StartRun(); for (var i = 0; i < 10; i++) { graph.AddPacketToInputStream("in", Packet.CreateStringAt("Hello World!", i)); } graph.CloseInputStream("in"); var packet = new Packet(); while (poller.Next(packet)) { Debug.Log(packet.Get()); } graph.WaitUntilDone(); } } ``` -------------------------------- ### Complete Face Mesh Setup in Unity Source: https://github.com/homuler/mediapipeunityplugin/blob/master/docs/Tutorial-Custom-Graph.md A comprehensive Unity C# script demonstrating the initialization of a webcam, loading Mediapipe model files (`.bytes` extension), setting up the `CalculatorGraph`, and processing video frames. It includes error handling for missing webcams and proper disposal of resources. ```csharp using System.Collections; using UnityEngine; using UnityEngine.UI; namespace Mediapipe.Unity.Tutorial { public class FaceMeshLegacy : MonoBehaviour { [SerializeField] private TextAsset configAsset; [SerializeField] private RawImage screen; [SerializeField] private int width; [SerializeField] private int height; [SerializeField] private int fps; private CalculatorGraph graph; private WebCamTexture webCamTexture; private IEnumerator Start() { if (WebCamTexture.devices.Length == 0) { throw new System.Exception("Web Camera devices are not found"); } var webCamDevice = WebCamTexture.devices[0]; webCamTexture = new WebCamTexture(webCamDevice.name, width, height, fps); webCamTexture.Play(); yield return new WaitUntil(() => webCamTexture.width > 16); screen.rectTransform.sizeDelta = new Vector2(width, height); screen.texture = webCamTexture; IResourceManager resourceManager = new LocalResourceManager(); yield return resourceManager.PrepareAssetAsync("face_detection_short_range.bytes"); yield return resourceManager.PrepareAssetAsync("face_landmark_with_attention.bytes"); graph = new CalculatorGraph(configAsset.text); graph.StartRun(); using var textureFrame = new Experimental.TextureFrame(webCamTexture.width, webCamTexture.height, TextureFormat.RGBA32); while (true) { textureFrame.ReadTextureOnCPU(webCamTexture, flipHorizontally: false, flipVertically: true); using var imageFrame = textureFrame.BuildImageFrame(); graph.AddPacketToInputStream("input_video", Packet.CreateImageFrame(imageFrame)); yield return new WaitForEndOfFrame(); } } private void OnDestroy() { if (webCamTexture != null) { webCamTexture.Stop(); } if (graph != null) { try { graph.CloseInputStream("input_video"); graph.WaitUntilDone(); } finally { graph.Dispose(); graph = null; } } } } } ``` -------------------------------- ### Complete FaceMeshLegacy Example with ImageFrame Processing Source: https://github.com/homuler/mediapipeunityplugin/blob/master/docs/Tutorial-Custom-Graph.md A complete Unity MonoBehaviour script demonstrating the initialization of Mediapipe, capturing webcam input, processing it through a CalculatorGraph, and displaying the output ImageFrame on a RawImage. It handles resource management and asynchronous operations. ```cs using System.Collections; using UnityEngine; using UnityEngine.UI; using Stopwatch = System.Diagnostics.Stopwatch; namespace Mediapipe.Unity.Tutorial { public class FaceMeshLegacy : MonoBehaviour { [SerializeField] private TextAsset configAsset; [SerializeField] private RawImage screen; [SerializeField] private int width; [SerializeField] private int height; [SerializeField] private int fps; private CalculatorGraph graph; private OutputStream outputVideoStream; private WebCamTexture webCamTexture; private IEnumerator Start() { if (WebCamTexture.devices.Length == 0) { throw new System.Exception("Web Camera devices are not found"); } var webCamDevice = WebCamTexture.devices[0]; webCamTexture = new WebCamTexture(webCamDevice.name, width, height, fps); webCamTexture.Play(); yield return new WaitUntil(() => webCamTexture.width > 16); var outputTexture = new Texture2D(width, height, TextureFormat.RGBA32, false); var outputPixelData = new Color32[width * height]; screen.rectTransform.sizeDelta = new Vector2(width, height); screen.texture = outputTexture; IResourceManager resourceManager = new LocalResourceManager(); yield return resourceManager.PrepareAssetAsync("face_detection_short_range.bytes"); yield return resourceManager.PrepareAssetAsync("face_landmark_with_attention.bytes"); graph = new CalculatorGraph(configAsset.text); outputVideoStream = new OutputStream(graph, "output_video"); outputVideoStream.StartPolling(); graph.StartRun(); var stopwatch = new Stopwatch(); stopwatch.Start(); using var textureFrame = new Experimental.TextureFrame(webCamTexture.width, webCamTexture.height, TextureFormat.RGBA32); while (true) { textureFrame.ReadTextureOnCPU(webCamTexture, flipHorizontally: false, flipVertically: true); using var imageFrame = textureFrame.BuildImageFrame(); var currentTimestamp = stopwatch.ElapsedTicks / ((double)System.TimeSpan.TicksPerMillisecond / 1000); graph.AddPacketToInputStream("input_video", Packet.CreateImageFrameAt(imageFrame, (long)currentTimestamp)); var task = outputVideoStream.WaitNextAsync(); yield return new WaitUntil(() => task.IsCompleted); if (!task.Result.ok) { throw new System.Exception("Something went wrong"); } var outputPacket = task.Result.packet; if (outputPacket != null) { var outputVideo = outputPacket.Get(); if (outputVideo.TryReadPixelData(outputPixelData)) { outputTexture.SetPixels32(outputPixelData); outputTexture.Apply(); } } } } private void OnDestroy() { if (webCamTexture != null) { webCamTexture.Stop(); } outputVideoStream?.Dispose(); outputVideoStream = null; if (graph != null) { try { graph.CloseInputStream("input_video"); graph.WaitUntilDone(); } finally { graph.Dispose(); graph = null; } } } } } ``` -------------------------------- ### Setup and Display WebCamTexture in Unity Source: https://github.com/homuler/mediapipeunityplugin/wiki/Getting-Started This C# script initializes a WebCamTexture from the first available camera device, sets its resolution and frame rate, and displays the feed on a UI RawImage. It includes a coroutine to wait until the texture is ready and handles cleanup by stopping the texture when the object is destroyed. ```csharp using System.Collections; using UnityEngine; using UnityEngine.UI; namespace Mediapipe.Unity.Tutorial { public class FaceMesh : MonoBehaviour { [SerializeField] private TextAsset _configAsset; [SerializeField] private RawImage _screen; [SerializeField] private int _width; [SerializeField] private int _height; [SerializeField] private int _fps; private WebCamTexture _webCamTexture; private IEnumerator Start() { if (WebCamTexture.devices.Length == 0) { throw new System.Exception("Web Camera devices are not found"); } var webCamDevice = WebCamTexture.devices[0]; _webCamTexture = new WebCamTexture(webCamDevice.name, _width, _height, _fps); _webCamTexture.Play(); yield return new WaitUntil(() => _webCamTexture.width > 16); _screen.rectTransform.sizeDelta = new Vector2(_width, _height); _screen.texture = _webCamTexture; } private void OnDestroy() { if (_webCamTexture != null) { _webCamTexture.Stop(); } } } } ``` -------------------------------- ### Configure Face Landmarker Options Source: https://github.com/homuler/mediapipeunityplugin/blob/master/docs/Tutorial-Task-API.md Shows how to instantiate FaceLandmarkerOptions with a specific model path and running mode. This setup is essential for defining how the task processes input data. ```csharp var options = new FaceLandmarkerOptions( baseOptions: new Tasks.Core.BaseOptions( Tasks.Core.BaseOptions.Delegate.CPU, modelAssetPath: "face_landmarker_v2_with_blendshapes.bytes" ), runningMode: Tasks.Vision.Core.RunningMode.VIDEO ); ``` -------------------------------- ### Execute Build Commands Source: https://github.com/homuler/mediapipeunityplugin/blob/master/docs/Build.md Provides various examples of using the build.py script to compile for different platforms, including desktop, Android, and iOS, with options for GPU support and debug symbols. ```sh python build.py build --desktop gpu --opencv cmake -vv python build.py build --desktop cpu --opencv cmake -vv python build.py build --desktop cpu --opencv cmake --macos_universal -vv python build.py build --desktop cpu --android arm64 --ios arm64 --opencv cmake -vv python build.py build --android arm64 --android_ndk_api_level 21 -vv python build.py build --desktop gpu --opencv cmake --solutions face_mesh hands pose -vv python build.py build -c dbg --android arm64 -vv python build.py build --android arm64 --linkopt=-s -vv ``` -------------------------------- ### LIVE_STREAM Mode - Asynchronous Detection with Callbacks Source: https://context7.com/homuler/mediapipeunityplugin/llms.txt This example demonstrates how to set up and use the FaceLandmarker in Unity's LIVE_STREAM mode. It configures the landmarker for asynchronous detection and processes results via a callback function, ensuring the main thread remains responsive. ```APIDOC ## LIVE_STREAM Mode - Asynchronous Detection with Callbacks LIVE_STREAM mode enables non-blocking inference with results delivered via callbacks, ideal for real-time applications where you don't want to block the main thread. ### Method ```csharp // This is a C# example demonstrating the usage within a Unity script. ``` ### Endpoint ``` N/A - This is a client-side implementation within Unity. ``` ### Parameters #### Base Options - **modelAsset** (TextAsset) - Required - The model asset file for the FaceLandmarker. #### FaceLandmarker Options - **runningMode** (RunningMode) - Required - Set to `Tasks.Vision.Core.RunningMode.LIVE_STREAM` for asynchronous operation. - **numFaces** (int) - Optional - The maximum number of faces to detect. - **resultCallback** (Action) - Required - A callback function to receive detection results asynchronously. ### Request Example ```csharp // Configuration within a Unity MonoBehaviour script: var options = new FaceLandmarkerOptions( baseOptions: new Tasks.Core.BaseOptions( Tasks.Core.BaseOptions.Delegate.CPU, modelAssetBuffer: modelAsset.bytes ), runningMode: Tasks.Vision.Core.RunningMode.LIVE_STREAM, numFaces: 1, resultCallback: OnFaceLandmarkerResult // Assign your callback method here ); using var faceLandmarker = FaceLandmarker.CreateFromOptions(options); // To perform detection: // faceLandmarker.DetectAsync(image, timestamp); ``` ### Response #### Success Response (Callback) - **FaceLandmarkerResult** - The result object containing detected face landmarks. - **Image** - The input image frame used for detection. - **long** - The timestamp of the image frame. #### Response Example (Callback) ```csharp // This method is called from a background thread, so Unity API calls are not permitted. private void OnFaceLandmarkerResult(FaceLandmarkerResult result, Image image, long timestamp) { // Process the 'result' object here. // Example: Accessing landmarks if (result.faceLandmarks != null && result.faceLandmarks.Count > 0) { Debug.Log($"Detected {result.faceLandmarks[0].landmarks.Count} landmarks."); } } ``` ### Error Handling - **Exceptions** may occur during `FaceLandmarker.CreateFromOptions` if the model asset is invalid or missing. - **Callback Threading**: Ensure thread-safe access to shared data when processing results from the callback on the main thread. ``` -------------------------------- ### Start Polling and Running the CalculatorGraph Source: https://github.com/homuler/mediapipeunityplugin/blob/master/docs/Tutorial-Custom-Graph.md Starts the polling for the output stream and then runs the CalculatorGraph. This is a prerequisite for retrieving any output packets. ```cs outputVideoStream.StartPolling(); graph.StartRun(); ``` -------------------------------- ### Full FaceMesh Implementation in Unity Source: https://github.com/homuler/mediapipeunityplugin/wiki/Getting-Started A complete Unity C# script demonstrating the initialization of a WebCamTexture, setup of a CalculatorGraph, continuous capture and processing of video frames as ImageFrames, and proper cleanup. ```csharp using System.Collections; using UnityEngine; using UnityEngine.UI; namespace Mediapipe.Unity.Tutorial { public class FaceMesh : MonoBehaviour { [SerializeField] private TextAsset _configAsset; [SerializeField] private RawImage _screen; [SerializeField] private int _width; [SerializeField] private int _height; [SerializeField] private int _fps; private CalculatorGraph _graph; private WebCamTexture _webCamTexture; private Texture2D _inputTexture; private Color32[] _pixelData; private IEnumerator Start() { if (WebCamTexture.devices.Length == 0) { throw new System.Exception("Web Camera devices are not found"); } var webCamDevice = WebCamTexture.devices[0]; _webCamTexture = new WebCamTexture(webCamDevice.name, _width, _height, _fps); _webCamTexture.Play(); yield return new WaitUntil(() => _webCamTexture.width > 16); _screen.rectTransform.sizeDelta = new Vector2(_width, _height); _screen.texture = _webCamTexture; _inputTexture = new Texture2D(_width, _height, TextureFormat.RGBA32, false); _pixelData = new Color32[_width * _height]; _graph = new CalculatorGraph(_configAsset.text); _graph.StartRun(); while (true) { _inputTexture.SetPixels32(_webCamTexture.GetPixels32(_pixelData)); var imageFrame = new ImageFrame(ImageFormat.Types.Format.Srgba, _width, _height, _width * 4, _inputTexture.GetRawTextureData()); _graph.AddPacketToInputStream("input_video", Packet.CreateImageFrame(imageFrame)); yield return new WaitForEndOfFrame(); } } private void OnDestroy() { if (_webCamTexture != null) { _webCamTexture.Stop(); } if (_graph != null) { try { _graph.CloseInputStream("input_video"); _graph.WaitUntilDone(); } finally { _graph.Dispose(); _graph = null; } } } } } ``` -------------------------------- ### Configure FaceLandmarker Task Source: https://github.com/homuler/mediapipeunityplugin/blob/master/docs/Tutorial-Task-API.md Creates a FaceLandmarker instance by defining BaseOptions and the desired RunningMode. This is the core setup required to initialize the MediaPipe vision task. ```csharp using Mediapipe.Tasks.Vision.FaceLandmarker; TextAsset modelAsset; var options = new FaceLandmarkerOptions( baseOptions: new Tasks.Core.BaseOptions( Tasks.Core.BaseOptions.Delegate.CPU, modelAssetBuffer: modelAsset.bytes ), runningMode: Tasks.Vision.Core.RunningMode.VIDEO ); using var faceLandmarker = FaceLandmarker.CreateFromOptions(options); ``` -------------------------------- ### Full Face Mesh Setup and Processing Loop in C# Source: https://github.com/homuler/mediapipeunityplugin/wiki/Getting-Started This is the main component for the Face Mesh tutorial. It initializes the webcam, Mediapipe graph, and necessary streams. The core loop continuously captures frames from the webcam, sends them to the graph, and displays the processed output video while also retrieving face landmarks. ```csharp using System.Collections; using System.Collections.Generic; using System.Threading.Tasks; using UnityEngine; using UnityEngine.UI; using Mediapipe.Unity.CoordinateSystem; using Stopwatch = System.Diagnostics.Stopwatch; namespace Mediapipe.Unity.Tutorial { public class FaceMesh : MonoBehaviour { [SerializeField] private TextAsset _configAsset; [SerializeField] private RawImage _screen; [SerializeField] private int _width; [SerializeField] private int _height; [SerializeField] private int _fps; private CalculatorGraph _graph; private OutputStream _outputVideoStream; private OutputStream> _multiFaceLandmarksStream; private ResourceManager _resourceManager; private WebCamTexture _webCamTexture; private Texture2D _inputTexture; private Color32[] _inputPixelData; private Texture2D _outputTexture; private Color32[] _outputPixelData; private IEnumerator Start() { if (WebCamTexture.devices.Length == 0) { throw new System.Exception("Web Camera devices are not found"); } var webCamDevice = WebCamTexture.devices[0]; _webCamTexture = new WebCamTexture(webCamDevice.name, _width, _height, _fps); _webCamTexture.Play(); yield return new WaitUntil(() => _webCamTexture.width > 16); _screen.rectTransform.sizeDelta = new Vector2(_width, _height); _inputTexture = new Texture2D(_width, _height, TextureFormat.RGBA32, false); _inputPixelData = new Color32[_width * _height]; _outputTexture = new Texture2D(_width, _height, TextureFormat.RGBA32, false); _outputPixelData = new Color32[_width * _height]; _screen.texture = _outputTexture; _resourceManager = new LocalResourceManager(); yield return _resourceManager.PrepareAssetAsync("face_detection_short_range.bytes"); yield return _resourceManager.PrepareAssetAsync("face_landmark_with_attention.bytes"); var stopwatch = new Stopwatch(); _graph = new CalculatorGraph(_configAsset.text); _outputVideoStream = new OutputStream(_graph, "output_video"); _multiFaceLandmarksStream = new OutputStream>(_graph, "multi_face_landmarks"); _outputVideoStream.StartPolling(); _multiFaceLandmarksStream.StartPolling(); _graph.StartRun(); stopwatch.Start(); var screenRect = _screen.GetComponent().rect; while (true) { _inputTexture.SetPixels32(_webCamTexture.GetPixels32(_inputPixelData)); var imageFrame = new ImageFrame(ImageFormat.Types.Format.Srgba, _width, _height, _width * 4, _inputTexture.GetRawTextureData()); var currentTimestamp = stopwatch.ElapsedTicks / (System.TimeSpan.TicksPerMillisecond / 1000); _graph.AddPacketToInputStream("input_video", Packet.CreateImageFrameAt(imageFrame, currentTimestamp)); var task1 = _outputVideoStream.WaitNextAsync(); var task2 = _multiFaceLandmarksStream.WaitNextAsync(); var task = Task.WhenAll(task1, task2); yield return new WaitUntil(() => task.IsCompleted); if (!task1.Result.ok || !task2.Result.ok) { throw new System.Exception("Something went wrong"); } var outputVideoPacket = task1.Result.packet; if (outputVideoPacket != null) { var outputVideo = outputVideoPacket.Get(); if (outputVideo.TryReadPixelData(_outputPixelData)) { _outputTexture.SetPixels32(_outputPixelData); _outputTexture.Apply(); } } var multiFaceLandmarksPacket = task2.Result.packet; if (multiFaceLandmarksPacket != null) { // Landmark processing would go here } } } } } ``` -------------------------------- ### Configure Build Environment on macOS Source: https://github.com/homuler/mediapipeunityplugin/blob/master/docs/Build.md Commands to install Python, Bazelisk, NuGet, and Xcode command line tools on macOS using Homebrew. ```shell brew install python export PATH=$PATH:"$(brew --prefix)/opt/python/libexec/bin" pip3 install --user six numpy brew install bazelisk brew install nuget sudo xcodebuild -license sudo xcode-select -s /Applications/Xcode.app xcode-select --install ``` -------------------------------- ### Face Mesh Live GPU Sample Source: https://github.com/homuler/mediapipeunityplugin/wiki/Getting-Started A comprehensive C# script demonstrating a live face mesh detection using GPU acceleration. It covers webcam setup, texture handling, MediaPipe graph execution, and output rendering. ```APIDOC ## Face Mesh Live GPU Sample ### Description This script implements a live face mesh detection pipeline using GPU acceleration in Unity. It captures video from a webcam, processes it with a MediaPipe graph configured for GPU, and displays the resulting face mesh on a UI RawImage. ### Method N/A (Code example) ### Endpoint N/A ### Parameters #### Path Parameters N/A #### Query Parameters N/A #### Request Body N/A ### Request Example ```cs using System.Collections; using UnityEngine; using UnityEngine.UI; using Stopwatch = System.Diagnostics.Stopwatch; namespace Mediapipe.Unity.Tutorial { public class FaceMesh : MonoBehaviour { [SerializeField] private TextAsset _configAsset; [SerializeField] private RawImage _screen; [SerializeField] private int _width; [SerializeField] private int _height; [SerializeField] private int _fps; private CalculatorGraph _graph; private OutputStream _outputVideoStream; private ResourceManager _resourceManager; private WebCamTexture _webCamTexture; private Texture2D _inputTexture; private Color32[] _inputPixelData; private Texture2D _outputTexture; private Color32[] _outputPixelData; private IEnumerator Start() { // Initialize Web Camera if (WebCamTexture.devices.Length == 0) { throw new System.Exception("Web Camera devices are not found"); } var webCamDevice = WebCamTexture.devices[0]; _webCamTexture = new WebCamTexture(webCamDevice.name, _width, _height, _fps); _webCamTexture.Play(); yield return new WaitUntil(() => _webCamTexture.width > 16); // Initialize GPU resources yield return GpuManager.Initialize(); if (!GpuManager.IsInitialized) { throw new System.Exception("Failed to initialize GPU resources"); } // Setup UI and Textures _screen.rectTransform.sizeDelta = new Vector2(_width, _height); _inputTexture = new Texture2D(_width, _height, TextureFormat.RGBA32, false); _inputPixelData = new Color32[_width * _height]; _outputTexture = new Texture2D(_width, _height, TextureFormat.RGBA32, false); _outputPixelData = new Color32[_width * _height]; _screen.texture = _outputTexture; // Prepare MediaPipe assets _resourceManager = new LocalResourceManager(); yield return _resourceManager.PrepareAssetAsync("face_detection_short_range.bytes"); yield return _resourceManager.PrepareAssetAsync("face_landmark_with_attention.bytes"); // Configure and start MediaPipe graph var stopwatch = new Stopwatch(); _graph = new CalculatorGraph(_configAsset.text); _graph.SetGpuResources(GpuManager.GpuResources); _outputVideoStream = new OutputStream(_graph, "output_video"); _outputVideoStream.StartPolling(); _graph.StartRun(); stopwatch.Start(); // Processing loop while (true) { // Read webcam frame and convert to ImageFrame _inputTexture.SetPixels32(_webCamTexture.GetPixels32(_inputPixelData)); var imageFrame = new ImageFrame(ImageFormat.Types.Format.Srgba, _width, _height, _width * 4, _inputTexture.GetRawTextureData()); var currentTimestamp = stopwatch.ElapsedTicks / (System.TimeSpan.TicksPerMillisecond / 1000); _graph.AddPacketToInputStream("input_video", Packet.CreateImageFrameAt(imageFrame, currentTimestamp)); // Wait for output frame var task = _outputVideoStream.WaitNextAsync(); yield return new WaitUntil(() => task.IsCompleted); if (!task.Result.ok) { throw new System.Exception("Something went wrong"); } // Process and render output frame var outputPacket = task.Result.packet; if (outputPacket != null) { var outputVideo = outputPacket.Get(); if (outputVideo.TryReadPixelData(outputPixelData)) { _outputTexture.SetPixels32(outputPixelData); _outputTexture.Apply(); } } } } private void OnDestroy() { if (_webCamTexture != null) { _webCamTexture.Stop(); } _outputVideoStream?.Dispose(); _graph?.Dispose(); GpuManager.Shutdown(); } } } ``` ### Response #### Success Response (200) N/A (This is a client-side script) #### Response Example N/A ``` -------------------------------- ### Initialize and Run a Basic CalculatorGraph in C# Source: https://context7.com/homuler/mediapipeunityplugin/llms.txt This snippet demonstrates how to define a graph configuration, initialize a CalculatorGraph, and process data packets. It covers the lifecycle of a graph including starting the run, feeding input streams, and polling output streams. ```csharp using Mediapipe; using UnityEngine; public class HelloWorld : MonoBehaviour { private const string _ConfigText = @" input_stream: ""in"" output_stream: ""out"" node { calculator: ""PassThroughCalculator"" input_stream: ""in"" output_stream: ""out1"" } node { calculator: ""PassThroughCalculator"" input_stream: ""out1"" output_stream: ""out"" } "; private void Start() { using var graph = new CalculatorGraph(_ConfigText); using var poller = graph.AddOutputStreamPoller("out"); graph.StartRun(); for (var i = 0; i < 10; i++) { graph.AddPacketToInputStream("in", Packet.CreateStringAt("Hello World!", i)); } graph.CloseInputStream("in"); var packet = new Packet(); while (poller.Next(packet)) { Debug.Log(packet.Get()); } graph.WaitUntilDone(); } } ``` -------------------------------- ### HandLandmarker - Detect Hand Landmarks and Handedness Source: https://context7.com/homuler/mediapipeunityplugin/llms.txt This example demonstrates how to initialize and use the HandLandmarker to detect hand landmarks from a webcam feed in Unity. It covers setting up options, processing video frames, and accessing landmark and handedness data. ```APIDOC ## HandLandmarker - Detect Hand Landmarks and Handedness ### Description The HandLandmarker detects 21 3D hand landmarks per hand, determines handedness (left/right), and provides world coordinates for 3D hand positioning in virtual environments. ### Method This documentation describes the usage of the `HandLandmarker` class within the Mediapipe Unity Plugin, primarily focusing on its initialization and detection capabilities for video streams. ### Endpoint N/A (This is a client-side Unity script, not a server endpoint) ### Parameters #### Path Parameters N/A #### Query Parameters N/A #### Request Body N/A ### Request Example ```csharp // Initialization and detection logic within a Unity MonoBehaviour script using System.Collections; using UnityEngine; using Mediapipe.Tasks.Vision.HandLandmarker; using Stopwatch = System.Diagnostics.Stopwatch; public class HandLandmarkerRunner : MonoBehaviour { [SerializeField] private TextAsset modelAsset; // e.g., "hand_landmarker.bytes" private IEnumerator Start() { var webCamTexture = new WebCamTexture(1280, 720, 30); webCamTexture.Play(); yield return new WaitUntil(() => webCamTexture.width > 16); // Create HandLandmarker with options var options = new HandLandmarkerOptions( baseOptions: new Tasks.Core.BaseOptions( Tasks.Core.BaseOptions.Delegate.CPU, modelAssetBuffer: modelAsset.bytes ), runningMode: Tasks.Vision.Core.RunningMode.VIDEO, numHands: 2 ); using var handLandmarker = HandLandmarker.CreateFromOptions(options); var stopwatch = new Stopwatch(); stopwatch.Start(); using var textureFrame = new Experimental.TextureFrame(webCamTexture.width, webCamTexture.height, TextureFormat.RGBA32); var result = default(HandLandmarkerResult); while (true) { textureFrame.ReadTextureOnCPU(webCamTexture, flipHorizontally: false, flipVertically: true); using var image = textureFrame.BuildCPUImage(); // Use TryDetectForVideo to avoid allocations if (handLandmarker.TryDetectForVideo(image, stopwatch.ElapsedMilliseconds, null, ref result)) { // Process results... } yield return new WaitForEndOfFrame(); } } } ``` ### Response #### Success Response (200) When `TryDetectForVideo` returns true, the `result` parameter (of type `HandLandmarkerResult`) will be populated with the detection data. This includes: - **handLandmarks** (List): A list where each element contains the 21 normalized landmarks for a detected hand. - **handedness** (List): A list where each element contains classification information (e.g., 'Left' or 'Right' hand) for a detected hand. - **handWorldLandmarks** (List): A list where each element contains the 21 landmarks in world coordinates for a detected hand. #### Response Example ```json { "handLandmarks": [ { "landmarks": [ { "x": 0.1, "y": 0.2, "z": 0.01 }, // Example landmark data // ... 20 more landmarks ] } // ... more hands ], "handedness": [ { "categories": [ { "categoryName": "Left", "score": 0.95 }, { "categoryName": "Right", "score": 0.05 } ] } // ... more hands ], "handWorldLandmarks": [ { "landmarks": [ { "x": 1.5, "y": -0.8, "z": 0.2 }, // Example world landmark data // ... 20 more landmarks ] } // ... more hands ] } ``` ### Error Handling - If no hands are detected, `result.handLandmarks` will be an empty list. - Ensure the `modelAsset` is correctly assigned in the Unity Inspector. - Check `WebCamTexture` initialization and permissions. ``` -------------------------------- ### Convert ImageFrame to GpuBuffer using GlCalculatorHelper Source: https://github.com/homuler/mediapipeunityplugin/wiki/API-Overview This example demonstrates how to convert an ImageFrame to a GpuBuffer using GlCalculatorHelper. It involves creating a source texture, getting the GpuBuffer, and then adding it to a CalculatorGraph. Proper resource management (releasing the texture) is important. ```cs glCalculatorHelper.RunInGlContext(() => { var texture = glCalculatorHelper.CreateSourceTexture(imageFrame); var gpuBuffer = texture.GetGpuBufferFrame(); Gl.Flush(); texture.Release(); return calculatorGraph.AddPacketToInputStream("in", Packet.CreateGpuBufferAt(gpuBuffer, 0)); }); ``` -------------------------------- ### Start CalculatorGraph Execution in C# Source: https://github.com/homuler/mediapipeunityplugin/wiki/Getting-Started Starts the execution of an initialized CalculatorGraph. The StartRun method should be called before adding any packets. It throws an exception if the graph cannot be started. ```csharp graph.StartRun(); ``` -------------------------------- ### Install NumPy Dependency Source: https://github.com/homuler/mediapipeunityplugin/blob/master/docs/Build.md Installs the NumPy library required for the build scripts using pip. ```bat pip install numpy --user ``` -------------------------------- ### Start CalculatorGraph Execution Source: https://github.com/homuler/mediapipeunityplugin/wiki/API-Overview Starts the graph execution, optionally providing side packets for configuration parameters. ```csharp calculatorGraph.StartRun(); var sidePacket = new PacketMap(); sidePacket.Emplace("num_faces", Packet.CreateInt(2)); calculatorGraph.StartRun(sidePacket); ``` -------------------------------- ### Install Dependencies on Arch Linux Source: https://github.com/homuler/mediapipeunityplugin/blob/master/docs/Build.md Commands to install system packages and Python dependencies required for building the plugin on Arch Linux. ```shell pacman -Sy unzip mesa pip install numpy --user ``` -------------------------------- ### Initialize MediaPipe Packets Source: https://github.com/homuler/mediapipeunityplugin/wiki/API-Overview Demonstrates how to create empty packets, packets with data, and packets with specific timestamps using C#. ```csharp var stringPacket = new Packet(); var stringPacket = Packet.CreateString("data"); var intPacket = Packet.CreateInt(0); var stringPacket = Packet.CreateStringAt("data", 0); var intPacket = Packet.CreateIntAt(0, 1); var stringPacket = Packet.CreateString("data"); stringPacket = stringPacket.At(0); var stringPacket = Packet.CreateForReference(packetPtr); ``` -------------------------------- ### Install Build Dependencies via MSYS2 Source: https://github.com/homuler/mediapipeunityplugin/blob/master/docs/Build.md Installs essential build tools including git, patch, and unzip using the pacman package manager on Windows. ```sh pacman -S git patch unzip ``` -------------------------------- ### Initialize GpuResources in Unity Source: https://github.com/homuler/mediapipeunityplugin/wiki/API-Overview Demonstrates how to create and initialize GpuResources in a Unity environment. It includes a method for sharing the OpenGL ES context with MediaPipe, which is crucial for certain GPU operations. ```cs var gpuResources = GpuResources.Create(); // throws if GPU computing is not supported. ``` ```cs using Mediapipe; using System.Collections; using UnityEngine; class GpuInitializer { private static IntPtr _CurrentContext = IntPtr.Zero; private static bool _IsContextInitialized = false; private delegate void PluginCallback(int eventId); [AOT.MonoPInvokeCallback(typeof(PluginCallback))] private static void GetCurrentContext(int eventId) { _CurrentContext = Egl.GetCurrentContext(); // This API is ported by this plugin. _IsContextInitialized = true; } public IEnumerator Initialize() { // You need to get the current context first. PluginCallback callback = GetCurrentContext; var fp = Marshal.GetFunctionPointerForDelegate(callback); GL.IssuePluginEvent(fp, 1); yield return new WaitUntil(() => _IsContextInitialized); // Call `GpuResources.Create` with the current context. var gpuResources = GpuResources.Create(_CurrentContext); // ... } } ``` -------------------------------- ### Initialize and Poll MediaPipe OutputStream Source: https://github.com/homuler/mediapipeunityplugin/wiki/Getting-Started Demonstrates how to initialize an OutputStream for ImageFrame data and start the polling process before running the CalculatorGraph. ```csharp var graph = new CalculatorGraph(_configAsset.text); var outputVideoStream = new OutputStream(graph, "output_video"); outputVideoStream.StartPolling(); _graph.StartRun(); ``` -------------------------------- ### Enable GPU Compute in C# Source: https://github.com/homuler/mediapipeunityplugin/wiki/API-Overview Explains how to enable GPU compute for MediaPipe. This involves creating GpuResources and setting them on the CalculatorGraph before starting its execution. ```csharp var gpuResources = GpuResources.Create(); calculatorGraph.SetGpuResources(gpuResources); // `SetGpuResources` must be called before `StartRun`. calculatorGraph.StartRun(); ```