# Camera capture in Unity

**URL:** <https://forum.magicleap.cloud/t/camera-capture-in-unity/2718>\
**Category:** Unity Development\
**Tags:** development, camera-api, camera\
**Created:** [October 13, 2023, 1:04am UTC](https://forum.magicleap.cloud/t/camera-capture-in-unity/2718 "2023-10-13T01:04:10Z")\
**Posts on this page:** 1\
**Showing post:** 14

<div class="post-metadata">

**Author:** ![kbabilinski](https://sea1.discourse-cdn.com/flex019/user_avatar/forum.magicleap.cloud/kbabilinski/32/19_2.png) [@kbabilinski](https://forum.magicleap.cloud/u/kbabilinski)\
**Post date:** [November 5, 2023, 7:15pm UTC](https://forum.magicleap.cloud/t/camera-capture-in-unity/2718/14 "2023-11-05T19:15:59Z")

</div>

I wanted for follow up on this. The bug is part of the Image Capture Example script. To fix the ghosting issue, call `PreCaptureAEAWB()` before `CaptureImage()`, then make sure that AWAWB is complete using the meta data . I have linked the updated script below incase anyone references this thread in the future

**Async Camera Capture Script**

```csharp
using System.Collections;
using UnityEngine;
using UnityEngine.XR.MagicLeap;
using static UnityEngine.XR.MagicLeap.MLCameraBase.Metadata;
using Debug = UnityEngine.Debug;

/// <summary>
/// This script provides an example of capturing images using the Magic Leap 2's Main Camera stream and Magic Leap 2 Camera APIs
/// It handles permissions, connects to the camera, captures images at regular intervals, and sends the result data to the Camera Capture visualizer.
/// </summary>
public class ImageCaptureExample : MonoBehaviour
{
    [SerializeField, Tooltip("The renderer to show the camera capture on JPEG format")]
    private Renderer _screenRendererJPEG = null;

    // Indicates if the camera is connected
    private bool isCameraConnected;
    // Reference to the MLCamera object that will access the device's camera
    private MLCamera colorCamera;
    // Indicates if the camera device is available
    private bool cameraDeviceAvailable;

    // Indicates if an image is currently being captured
    private bool isCapturingImage;

    // Reference to the MLPermissions.Callbacks object that will handle the permission requests and responses
    private readonly MLPermissions.Callbacks permissionCallbacks = new MLPermissions.Callbacks();

    // Used to display the JPEG image.
    private Texture2D imageTexture;

    // Register the permission callbacks in the Awake method
    private void Awake()
    {
        permissionCallbacks.OnPermissionGranted += OnPermissionGranted;
        permissionCallbacks.OnPermissionDenied += OnPermissionDenied;
        permissionCallbacks.OnPermissionDeniedAndDontAskAgain += OnPermissionDenied;
    }

    // Request the camera permission in the Start method
    void Start()
    {
        MLResult result = MLPermissions.RequestPermission(MLPermission.Camera, permissionCallbacks);
        if (!result.IsOk)
        {
            Debug.LogErrorFormat("Error: ImageCaptureExample failed to get requested permissions, disabling script. Reason: {0}", result);
            enabled = false;
        }

    }

    /// <summary>
    /// Stop the camera, unregister callbacks, and stop input and permissions APIs.
    /// </summary>
    void OnDisable()
    {
        permissionCallbacks.OnPermissionGranted -= OnPermissionGranted;
        permissionCallbacks.OnPermissionDenied -= OnPermissionDenied;
        permissionCallbacks.OnPermissionDeniedAndDontAskAgain -= OnPermissionDenied;

        if (colorCamera != null && isCameraConnected)
        {
            DisableMLCamera();
        }
    }

    // Handle the permission denied event by logging an error message
    private void OnPermissionDenied(string permission)
    {
        MLPluginLog.Error($"{permission} denied, test won't function.");
    }

    // Handle the permission granted event by starting two coroutines:
    // one to enable the camera and one to capture images in a loop
    private void OnPermissionGranted(string permission)
    {
        StartCoroutine(EnableMLCamera());
        StartCoroutine(CaptureImagesLoop());
    }

    // Define a coroutine that will enable the camera by checking its availability,
    // creating and connecting it, and preparing it for capture
    private IEnumerator EnableMLCamera()
    {
        // Loop until the camera device is available
        while (!cameraDeviceAvailable)
        {
            MLResult result = MLCamera.GetDeviceAvailabilityStatus(MLCamera.Identifier.Main, out cameraDeviceAvailable);
            if (!(result.IsOk && cameraDeviceAvailable))
            {
                // Wait until camera device is available
                yield return new WaitForSeconds(1.0f);
            }
        }

        Debug.Log("Camera device available.");

        // Create and connect the camera with a context that enables video stabilization and camera only capture
        ConnectCamera();

        // Wait until the camera is connected since this script uses the async "CreateAndConnectAsync" Method to connect to the camera.
        while (!isCameraConnected)
        {
            yield return null;
        }

        Debug.Log("Camera device connected.");

        // Prepare the camera for capture with a configuration that specifies JPEG output format, frame rate, and resolution
        ConfigureAndPrepareCapture();
    }

    // Define a coroutine that will capture images every 3 seconds if the camera is connected and supports image capture type. 
    // The image is then captured async
    private IEnumerator CaptureImagesLoop()
    {
        while (true)
        {
            if (isCameraConnected && !isCapturingImage)
            {
                if (MLCamera.IsCaptureTypeSupported(colorCamera, MLCamera.CaptureType.Image))
                {
                    CaptureImage();
                }
            }
            yield return new WaitForSeconds(3.0f);
        }
    }

    // Define an async method that will create and connect the camera with a context that enables video stabilization and Video only capture
    private async void ConnectCamera()
    {
        MLCamera.ConnectContext context = MLCamera.ConnectContext.Create();
        context.EnableVideoStabilization = false;
        context.Flags = MLCameraBase.ConnectFlag.CamOnly;

        // Use the CreateAndConnectAsync method to create and connect the camera asynchronously
        colorCamera = await MLCamera.CreateAndConnectAsync(context);

        if (colorCamera != null)
        {
            // Register a callback for when a raw image is available after capture
            colorCamera.OnRawImageAvailable += OnCaptureRawImageComplete;
            isCameraConnected = true;
        }
    }

    // Define an async method that will prepare the camera for capture with a configuration that specifies
    // JPEG output format, frame rate, and resolution
    private async void ConfigureAndPrepareCapture()
    {
        MLCamera.CaptureStreamConfig[] imageConfig = new MLCamera.CaptureStreamConfig[1]
        {
            new MLCamera.CaptureStreamConfig()
            {
                OutputFormat = MLCamera.OutputFormat.JPEG,
                CaptureType = MLCamera.CaptureType.Image,
                Width = 1920,
                Height = 1080
            }
        };

        MLCamera.CaptureConfig captureConfig = new MLCamera.CaptureConfig()
        {
            StreamConfigs = imageConfig,
            CaptureFrameRate = MLCamera.CaptureFrameRate._30FPS
        };

        // Use the PrepareCapture method to set the capture configuration and get the metadata handle
        MLResult prepareCaptureResult = colorCamera.PrepareCapture(captureConfig, out MLCamera.Metadata _);

        if (!prepareCaptureResult.IsOk)
        {
            return;
        }

    }

    /// <summary>
    /// Disconnects the MLCamera if it was ever created or connected.
    /// </summary>
    private void DisableMLCamera()
    {
        if (colorCamera != null)
        {
            colorCamera.Disconnect();

            // Explicitly set to false here as the disconnect was attempted.
            isCameraConnected = false;
        }
    }

    /// <summary>
    /// Takes a picture async with the device's camera using the camera's CaptureImageAsync method.
    /// </summary>
    private async void CaptureImage()
    {
        // Set the flag to indicate that an image capture is in progress
        isCapturingImage = true;

        var aeawbResult = await colorCameraPreCaptureAE.AWBAsync();
        if (!aeawbResult.IsOk)
        {
            Debug.LogError("Image capture failed!");
        }
        else
        {
            var result = await colorCamera.CaptureImageAsync(1);
            if (!result.IsOk)
            {
                Debug.LogError("Image capture failed!");
            }
        }

        // Reset the flag to indicate that image capture is complete
        isCapturingImage = false;
    }

    /// <summary>
    /// Handles the event of a new image getting captured and visualizes it with the Image Visualizer
    /// </summary>
    /// <param name="capturedImage">Captured frame.</param>
    /// <param name="resultExtras">Results Extras.</param>
    private void OnCaptureRawImageComplete(MLCamera.CameraOutput capturedImage, MLCamera.ResultExtras resultExtras, MLCamera.Metadata metadataHandle)
    {
        MLResult aeStateResult = metadataHandle.GetControlAEStateResultMetadata(out ControlAEState controlAEState);
        MLResult awbStateResult = metadataHandle.GetControlAWBStateResultMetadata(out ControlAWBState controlAWBState);

        if (aeStateResult.IsOk && awbStateResult.IsOk)
        {
            bool autoExposureComplete = controlAEState == MLCameraBase.Metadata.ControlAEState.Converged || controlAEState == MLCameraBase.Metadata.ControlAEState.Locked;
            bool autoWhiteBalanceComplete = controlAWBState == MLCameraBase.Metadata.ControlAWBState.Converged || controlAWBState == MLCameraBase.Metadata.ControlAWBState.Locked;

            if (autoExposureComplete && autoWhiteBalanceComplete)
            {
                // This example is configured to render JPEG images only.
                if(capturedImage.Format == MLCameraBase.OutputFormat.JPEG)
                {
                    UpdateJPGTexture(capturedImage.Planes[0], _screenRendererJPEG);
                }
            }
        }
    }

    private void UpdateJPGTexture(MLCamera.PlaneInfo imagePlane, Renderer renderer)
    {
        if (imageTexture != null)
        {
            Destroy(imageTexture);
        }

        imageTexture = new Texture2D(8, 8);
        bool status = imageTexture.LoadImage(imagePlane.Data);

        if (status && (imageTexture.width != 8 && imageTexture.height != 8))
        {
            renderer.material.mainTexture = imageTexture;
        }
    }
}

```

---

_[View the full topic](https://forum.magicleap.cloud/t/camera-capture-in-unity/2718)._
