Exemplo n.º 1
0
        public Capture GetCapture(int timeoutInMS = -1)
        {
            lock (this)
            {
                if (disposedValue)
                {
                    throw new ObjectDisposedException(nameof(Device));
                }

                NativeMethods.k4a_wait_result_t result = NativeMethods.k4a_device_get_capture(handle, out NativeMethods.k4a_capture_t capture, timeoutInMS);

                if (result == NativeMethods.k4a_wait_result_t.K4A_WAIT_RESULT_TIMEOUT)
                {
                    throw new TimeoutException("Timed out waiting for capture");
                }

                AzureKinectException.ThrowIfNotSuccess(result);

                if (capture.IsInvalid)
                {
                    throw new Microsoft.Azure.Kinect.Sensor.AzureKinectException("k4a_device_get_capture did not return a valid capture handle");
                }

                return(new Capture(capture));
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Reads a sensor capture.
        /// </summary>
        /// <param name="timeout">Time to wait for a capture.</param>
        /// <returns>A Capture object holding image data.</returns>
        /// <remarks>
        /// Gets the next capture in the streamed sequence of captures from the camera.
        /// If a new capture is not currently available, this function will block until the timeout is reached.
        /// The SDK will buffer at least two captures worth of data before dropping the oldest capture.
        /// Callers needing to capture all data need to ensure they read the data as fast as the data is being produced on average.
        /// </remarks>
        public Capture GetCapture(TimeSpan timeout)
        {
            lock (this)
            {
                if (this.disposedValue)
                {
                    throw new ObjectDisposedException(nameof(Device));
                }

                using (LoggingTracer tracer = new LoggingTracer())
                {
                    NativeMethods.k4a_wait_result_t result = NativeMethods.k4a_device_get_capture(this.handle, out NativeMethods.k4a_capture_t capture, (int)timeout.TotalMilliseconds);

                    if (result == NativeMethods.k4a_wait_result_t.K4A_WAIT_RESULT_TIMEOUT)
                    {
                        throw new TimeoutException("Timed out waiting for capture");
                    }

                    AzureKinectException.ThrowIfNotSuccess(tracer, result);

                    if (capture.IsInvalid)
                    {
                        throw new AzureKinectException("k4a_device_get_capture did not return a valid capture handle");
                    }

                    return(new Capture(capture));
                }
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Gets the device raw calibration data.
        /// </summary>
        /// <returns>The raw data can be stored off-line for future use.</returns>
        public byte[] GetRawCalibration()
        {
            lock (this)
            {
                if (this.disposedValue)
                {
                    throw new ObjectDisposedException(nameof(Device));
                }

                // Determine the required calibration size
                UIntPtr size = new UIntPtr(0);
                if (NativeMethods.k4a_device_get_raw_calibration(this.handle, null, ref size) != NativeMethods.k4a_buffer_result_t.K4A_BUFFER_RESULT_TOO_SMALL)
                {
                    throw new AzureKinectException($"Unexpected result calling {nameof(NativeMethods.k4a_device_get_raw_calibration)}");
                }

                // Allocate a string buffer
                byte[] raw = new byte[size.ToUInt32()];

                // Get the raw calibration
                AzureKinectException.ThrowIfNotSuccess(() => NativeMethods.k4a_device_get_raw_calibration(this.handle, raw, ref size));

                return(raw);
            }
        }
Exemplo n.º 4
0
        /// <summary>
        /// Reads an IMU sample from the device.
        /// </summary>
        /// <param name="timeout">Time to wait for an IMU sample.</param>
        /// <returns>The next unread IMU sample from the device.</returns>
        /// <remarks>Gets the next sample in the streamed sequence of IMU samples from the device.
        /// If a new sample is not currently available, this function will block until the timeout is reached.
        /// The API will buffer at least two camera capture intervals worth of samples before dropping the oldest sample. Callers needing to capture all data need to ensure they read the data as fast as the data is being produced on average.
        /// </remarks>
        public ImuSample GetImuSample(TimeSpan timeout)
        {
            lock (this)
            {
                if (this.disposedValue)
                {
                    throw new ObjectDisposedException(nameof(Device));
                }

                using (LoggingTracer tracer = new LoggingTracer())
                {
                    NativeMethods.k4a_imu_sample_t  sample = new NativeMethods.k4a_imu_sample_t();
                    NativeMethods.k4a_wait_result_t result = NativeMethods.k4a_device_get_imu_sample(this.handle, sample, (int)timeout.TotalMilliseconds);

                    if (result == NativeMethods.k4a_wait_result_t.K4A_WAIT_RESULT_TIMEOUT)
                    {
                        throw new TimeoutException("Timed out waiting for IMU sample");
                    }

                    AzureKinectException.ThrowIfNotSuccess(tracer, result);

                    return(sample.ToImuSample());
                }
            }
        }
Exemplo n.º 5
0
        private bool disposedValue = false; // To detect redundant calls

        /// <summary>
        /// Initializes a new instance of the <see cref="Capture"/> class.
        /// </summary>
        public Capture()
        {
            AzureKinectException.ThrowIfNotSuccess(() => NativeMethods.k4a_capture_create(out this.handle));

            // Hook the native allocator and register this object.
            // .Dispose() will be called on this object when the allocator is shut down.
            Allocator.Singleton.RegisterForDisposal(this);
        }
Exemplo n.º 6
0
 /// <summary>
 /// Throws an <see cref="AzureKinectOpenDeviceException"/> if the result of the function
 /// is not a success.
 /// </summary>
 /// <param name="tracer">The tracer is that is capturing logging messages.</param>
 /// <param name="result">The result native function to call.</param>
 /// <typeparam name="T">The type of result to expect from the function call.</typeparam>
 internal static new void ThrowIfNotSuccess <T>(LoggingTracer tracer, T result)
     where T : System.Enum
 {
     if (!AzureKinectException.IsSuccess(result))
     {
         throw new AzureKinectOpenDeviceException($"result = {result}", tracer.LogMessages);
     }
 }
Exemplo n.º 7
0
        public void StartImu()
        {
            lock (this)
            {
                if (disposedValue)
                {
                    throw new ObjectDisposedException(nameof(Device));
                }

                AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_start_imu(handle));
            }
        }
Exemplo n.º 8
0
        /// <summary>
        /// Sets the Azure Kinect color sensor control value.
        /// </summary>
        /// <param name="command">Color sensor control command.</param>
        /// <param name="mode">The mode of the color control option.</param>
        /// <param name="value">The value of the color control option.</param>
        public void SetColorControl(ColorControlCommand command, ColorControlMode mode, int value)
        {
            lock (this)
            {
                if (this.disposedValue)
                {
                    throw new ObjectDisposedException(nameof(Device));
                }

                AzureKinectException.ThrowIfNotSuccess(() => NativeMethods.k4a_device_set_color_control(this.handle, command, mode, value));
            }
        }
Exemplo n.º 9
0
 /// <summary>
 /// Throws an <see cref="AzureKinectOpenDeviceException"/> if the result of the function
 /// is not a success.
 /// </summary>
 /// <param name="function">The native function to call.</param>
 /// <typeparam name="T">The type of result to expect from the function call.</typeparam>
 internal static new void ThrowIfNotSuccess <T>(Func <T> function)
     where T : System.Enum
 {
     using (LoggingTracer tracer = new LoggingTracer())
     {
         T result = function();
         if (!AzureKinectException.IsSuccess(result))
         {
             throw new AzureKinectOpenDeviceException($"result = {result}", tracer.LogMessages);
         }
     }
 }
Exemplo n.º 10
0
        /// <summary>
        /// Get the camera calibration for a device from a raw calibration blob.
        /// </summary>
        /// <param name="raw">Raw calibration blob obtained from a device or recording.</param>
        /// <param name="depthMode">Mode in which depth camera is operated.</param>
        /// <param name="colorResolution">Resolution in which the color camera is operated.</param>
        /// <returns>Calibration object.</returns>
        public static Calibration GetFromRaw(byte[] raw, DepthMode depthMode, ColorResolution colorResolution)
        {
            Calibration calibration = default;

            AzureKinectException.ThrowIfNotSuccess(() => NativeMethods.k4a_calibration_get_from_raw(
                                                       raw,
                                                       (UIntPtr)raw.Length,
                                                       depthMode,
                                                       colorResolution,
                                                       out calibration));

            return(calibration);
        }
Exemplo n.º 11
0
        /// <summary>
        /// Initializes a new instance of the <see cref="Image"/> class.
        /// </summary>
        /// <param name="format">The pixel format of the image. Must be a format with a constant pixel size.</param>
        /// <param name="widthPixels">Width of the image in pixels.</param>
        /// <param name="heightPixels">Height of the image in pixels.</param>
        /// <param name="strideBytes">Stride of the image in bytes. Must be as large as the width times the size of a pixel. Set to zero for the default if available for that format.</param>
        public Image(ImageFormat format, int widthPixels, int heightPixels, int strideBytes)
        {
            // Hook the native allocator and register this object.
            // .Dispose() will be called on this object when the allocator is shut down.
            Allocator.Singleton.RegisterForDisposal(this);

            AzureKinectException.ThrowIfNotSuccess(() => NativeMethods.k4a_image_create(
                                                       format,
                                                       widthPixels,
                                                       heightPixels,
                                                       strideBytes,
                                                       out this.handle));
        }
Exemplo n.º 12
0
        /// <summary>
        /// Transform a 3D point of a source coordinate system into a 3D point of the target coordinate system.
        /// </summary>
        /// <param name="sourcePoint3D">The 3D coordinates in millimeters representing a point in <paramref name="sourceCamera"/>.</param>
        /// <param name="sourceCamera">The current camera.</param>
        /// <param name="targetCamera">The target camera.</param>
        /// <returns>A point in 3D coordiantes of <paramref name="targetCamera"/> stored in millimeters.</returns>
        public Vector3?TransformTo3D(Vector3 sourcePoint3D, CalibrationDeviceType sourceCamera, CalibrationDeviceType targetCamera)
        {
            using (LoggingTracer tracer = new LoggingTracer())
            {
                AzureKinectException.ThrowIfNotSuccess(tracer, NativeMethods.k4a_calibration_3d_to_3d(
                                                           ref this,
                                                           ref sourcePoint3D,
                                                           sourceCamera,
                                                           targetCamera,
                                                           out Vector3 target_point3d));

                return(target_point3d);
            }
        }
Exemplo n.º 13
0
        /// <summary>
        /// Gets the calibration of the device.
        /// </summary>
        /// <param name="depthMode">Depth mode for the calibration.</param>
        /// <param name="colorResolution">Color camera resolution for the calibration.</param>
        /// <returns>Calibration object.</returns>
        public Calibration GetCalibration(DepthMode depthMode, ColorResolution colorResolution)
        {
            lock (this)
            {
                if (this.disposedValue)
                {
                    throw new ObjectDisposedException(nameof(Device));
                }

                Calibration calibration = default;
                AzureKinectException.ThrowIfNotSuccess(() => NativeMethods.k4a_device_get_calibration(this.handle, depthMode, colorResolution, out calibration));
                return(calibration);
            }
        }
Exemplo n.º 14
0
        /// <summary>
        /// Transform a 3D point of a source coordinate system into a 2D pixel coordinate of the target camera.
        /// </summary>
        /// <param name="sourcePoint3D">The 3D coordinate in millimeters representing a point in <paramref name="sourceCamera"/> coordinate system.</param>
        /// <param name="sourceCamera">The current camera.</param>
        /// <param name="targetCamera">The target camera.</param>
        /// <returns>The 2D pixel coordinate in <paramref name="targetCamera"/> coordinates or null if the point is not valid in that coordinate system.</returns>
        public Vector2?TransformTo2D(Vector3 sourcePoint3D, CalibrationDeviceType sourceCamera, CalibrationDeviceType targetCamera)
        {
            using (LoggingTracer tracer = new LoggingTracer())
            {
                AzureKinectException.ThrowIfNotSuccess(tracer, NativeMethods.k4a_calibration_3d_to_2d(
                                                           ref this,
                                                           ref sourcePoint3D,
                                                           sourceCamera,
                                                           targetCamera,
                                                           out Vector2 target_point2d,
                                                           out bool valid));

                return(valid ? (Vector2?)target_point2d : null);
            }
        }
Exemplo n.º 15
0
        /// <summary>
        /// Initializes a new instance of the <see cref="Image"/> class.
        /// </summary>
        /// <param name="format">The pixel format of the image. Must be a format with a constant pixel size.</param>
        /// <param name="widthPixels">Width of the image in pixels.</param>
        /// <param name="heightPixels">Height of the image in pixels.</param>
        public Image(ImageFormat format, int widthPixels, int heightPixels)
        {
            // Hook the native allocator and register this object.
            // .Dispose() will be called on this object when the allocator is shut down.
            Allocator.Singleton.RegisterForDisposal(this);

#pragma warning disable CA2000 // Dispose objects before losing scope
            AzureKinectException.ThrowIfNotSuccess(() => NativeMethods.k4a_image_create(
                                                       format,
                                                       widthPixels,
                                                       heightPixels,
                                                       0,
                                                       image_handle: out this.handle));
#pragma warning restore CA2000 // Dispose objects before losing scope
        }
Exemplo n.º 16
0
        /// <summary>
        /// Get the Azure Kinect color sensor control value.
        /// </summary>
        /// <param name="command">Color sensor control command.</param>
        /// <param name="mode">The mode of the color control option.</param>
        /// <returns>The value of the color control option.</returns>
        public int GetColorControl(ColorControlCommand command, out ColorControlMode mode)
        {
            lock (this)
            {
                if (this.disposedValue)
                {
                    throw new ObjectDisposedException(nameof(Device));
                }

                using (LoggingTracer tracer = new LoggingTracer())
                {
                    AzureKinectException.ThrowIfNotSuccess(tracer, NativeMethods.k4a_device_get_color_control(this.handle, command, out mode, out int value));
                    return(value);
                }
            }
        }
Exemplo n.º 17
0
        /// <summary>
        /// Transforms an Image from the color camera perspective to the depth camera perspective.
        /// </summary>
        /// <param name="depth">Depth map of the space the color image is being transformed in to.</param>
        /// <param name="color">Color image to transform in to the depth space.</param>
        /// <param name="transformed">An Image to hold the output.</param>
        /// <remarks>
        /// The <paramref name="transformed"/> Image must be of the resolution of the depth camera, and
        /// of the pixel format of the color image.
        /// </remarks>
        public void ColorImageToDepthCamera(Image depth, Image color, Image transformed)
        {
            if (depth == null)
            {
                throw new ArgumentNullException(nameof(depth));
            }

            if (color == null)
            {
                throw new ArgumentNullException(nameof(color));
            }

            if (transformed == null)
            {
                throw new ArgumentNullException(nameof(transformed));
            }

            lock (this)
            {
                if (this.disposedValue)
                {
                    throw new ObjectDisposedException(nameof(Transformation));
                }

                // Create a new reference to the Image objects so that they cannot be disposed while
                // we are performing the transformation
                using (Image depthReference = depth.Reference())
                    using (Image colorReference = color.Reference())
                        using (Image transformedReference = transformed.Reference())
                        {
                            // Ensure changes made to the managed memory are visible to the native layer
                            depthReference.FlushMemory();
                            colorReference.FlushMemory();

                            AzureKinectException.ThrowIfNotSuccess(() => NativeMethods.k4a_transformation_color_image_to_depth_camera(
                                                                       this.handle,
                                                                       depthReference.DangerousGetHandle(),
                                                                       colorReference.DangerousGetHandle(),
                                                                       transformedReference.DangerousGetHandle()));

                            // Copy the native memory back to managed memory if required
                            transformedReference.InvalidateMemory();
                        }
            }
        }
Exemplo n.º 18
0
        /// <summary>
        /// Transform a 2D pixel coordinate from color camera into a 2D pixel coordinate of the depth camera.
        /// </summary>
        /// <param name="sourcePoint2D">The 2D pixel color camera coordinates.</param>
        /// <param name="depth">The depth image.</param>
        /// <returns>The 2D pixel in depth camera coordinates, or null if the source point is not valid in the depth camera coordinate system.</returns>
        public Vector2?TransformColor2DToDepth2D(Vector2 sourcePoint2D, Image depth)
        {
            if (depth == null)
            {
                throw new ArgumentNullException(nameof(depth));
            }

            using (LoggingTracer tracer = new LoggingTracer())
                using (Image depthReference = depth.Reference())
                {
                    AzureKinectException.ThrowIfNotSuccess(tracer, NativeMethods.k4a_calibration_color_2d_to_depth_2d(
                                                               ref this,
                                                               ref sourcePoint2D,
                                                               depthReference.DangerousGetHandle(),
                                                               out Vector2 target_point2d,
                                                               out bool valid));

                    return(valid ? (Vector2?)target_point2d : null);
                }
        }
Exemplo n.º 19
0
        public void StartCameras(DeviceConfiguration configuration)
        {
            if (configuration == null)
            {
                throw new ArgumentNullException(nameof(configuration));
            }

            lock (this)
            {
                if (disposedValue)
                {
                    throw new ObjectDisposedException(nameof(Device));
                }

                NativeMethods.k4a_device_configuration_t nativeConfig = configuration.GetNativeConfiguration();
                AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_device_start_cameras(handle, ref nativeConfig));

                this.CurrentDepthMode       = configuration.DepthMode;
                this.CurrentColorResolution = configuration.ColorResolution;
            }
        }
Exemplo n.º 20
0
        public ImuSample GetImuSample(int timeoutInMS = -1)
        {
            lock (this)
            {
                if (disposedValue)
                {
                    throw new ObjectDisposedException(nameof(Device));
                }

                ImuSample sample = new ImuSample();
                NativeMethods.k4a_wait_result_t result = NativeMethods.k4a_device_get_imu_sample(handle, sample, timeoutInMS);

                if (result == NativeMethods.k4a_wait_result_t.K4A_WAIT_RESULT_TIMEOUT)
                {
                    throw new TimeoutException("Timed out waiting for imu sample");
                }

                AzureKinectException.ThrowIfNotSuccess(result);

                return(sample);
            }
        }
Exemplo n.º 21
0
        public void DepthImageToColorCamera(Image depth, Image transformed)
        {
            lock (this)
            {
                if (disposedValue)
                {
                    throw new ObjectDisposedException(nameof(Transformation));
                }

                // Create a new reference to the Image objects so that they cannot be disposed while
                // we are performing the transformation
                using (Image depthReference = depth.Reference())
                    using (Image transformedReference = transformed.Reference())
                    {
                        AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_transformation_depth_image_to_color_camera(
                                                                   handle,
                                                                   depthReference.DangerousGetHandle(),
                                                                   transformedReference.DangerousGetHandle()
                                                                   ));
                    }
            }
        }
Exemplo n.º 22
0
        /// <summary>
        /// Initializes a new instance of the <see cref="Image"/> class.
        /// </summary>
        /// <param name="format">The pixel format of the image. Must be a format with a constant pixel size.</param>
        /// <param name="widthPixels">Width of the image in pixels.</param>
        /// <param name="heightPixels">Height of the image in pixels.</param>
        public Image(ImageFormat format, int widthPixels, int heightPixels)
        {
            int pixelSize;

            switch (format)
            {
            case ImageFormat.ColorBGRA32:
                pixelSize = 4;
                break;

            case ImageFormat.Depth16:
            case ImageFormat.IR16:
            case ImageFormat.Custom16:
                pixelSize = 2;
                break;

            case ImageFormat.Custom8:
                pixelSize = 1;
                break;

            default:
                throw new AzureKinectException($"Unable to allocate array for format {format}");
            }

            int stride_bytes = widthPixels * pixelSize;

            // Hook the native allocator and register this object.
            // .Dispose() will be called on this object when the allocator is shut down.
            Allocator.Singleton.RegisterForDisposal(this);

#pragma warning disable CA2000 // Dispose objects before losing scope
            AzureKinectException.ThrowIfNotSuccess(() => NativeMethods.k4a_image_create(
                                                       format,
                                                       widthPixels,
                                                       heightPixels,
                                                       stride_bytes,
                                                       image_handle: out this.handle));
#pragma warning restore CA2000 // Dispose objects before losing scope
        }
Exemplo n.º 23
0
        public void DepthImageToPointCloud(Image depth, Image pointCloud, Calibration.DeviceType camera = Calibration.DeviceType.Depth)
        {
            lock (this)
            {
                if (disposedValue)
                {
                    throw new ObjectDisposedException(nameof(Transformation));
                }

                // Create a new reference to the Image objects so that they cannot be disposed while
                // we are performing the transformation
                using (Image depthReference = depth.Reference())
                    using (Image pointCloudReference = pointCloud.Reference())
                    {
                        AzureKinectException.ThrowIfNotSuccess(NativeMethods.k4a_transformation_depth_image_to_point_cloud(
                                                                   handle,
                                                                   depthReference.DangerousGetHandle(),
                                                                   camera,
                                                                   pointCloudReference.DangerousGetHandle()));
                    }
            }
        }
Exemplo n.º 24
0
        /// <summary>
        /// Creates a point cloud from a depth image.
        /// </summary>
        /// <param name="depth">The depth map to generate the point cloud from.</param>
        /// <param name="pointCloud">The image to store the output point cloud.</param>
        /// <param name="camera">The perspective the depth map is from.</param>
        /// <remarks>
        /// If the depth map is from the original depth perspective, <paramref name="camera"/> should be Depth. If it has
        /// been transformed to the color camera perspective, <paramref name="camera"/> should be Color.
        ///
        /// The <paramref name="pointCloud"/> image must be of format Custom. Each pixel will be an XYZ set of 16 bit values,
        /// therefore its stride must be 2(bytes) * 3(x,y,z) * width of the <paramref name="depth"/> image in pixels.
        /// </remarks>
        public void DepthImageToPointCloud(Image depth, Image pointCloud, CalibrationDeviceType camera = CalibrationDeviceType.Depth)
        {
            if (depth == null)
            {
                throw new ArgumentNullException(nameof(depth));
            }

            if (pointCloud == null)
            {
                throw new ArgumentNullException(nameof(pointCloud));
            }

            lock (this)
            {
                if (this.disposedValue)
                {
                    throw new ObjectDisposedException(nameof(Transformation));
                }

                // Create a new reference to the Image objects so that they cannot be disposed while
                // we are performing the transformation
                using (Image depthReference = depth.Reference())
                    using (Image pointCloudReference = pointCloud.Reference())
                    {
                        // Ensure changes made to the managed memory are visible to the native layer
                        depthReference.FlushMemory();

                        AzureKinectException.ThrowIfNotSuccess(() => NativeMethods.k4a_transformation_depth_image_to_point_cloud(
                                                                   this.handle,
                                                                   depthReference.DangerousGetHandle(),
                                                                   camera,
                                                                   pointCloudReference.DangerousGetHandle()));

                        // Copy the native memory back to managed memory if required
                        pointCloudReference.InvalidateMemory();
                    }
            }
        }
Exemplo n.º 25
0
        /// <summary>
        /// Transforms a depth Image and a custom Image from the depth camera perspective to the color camera perspective.
        /// </summary>
        /// <param name="depth">Depth image to transform.</param>
        /// <param name="custom">Custom image to transform.</param>
        /// <param name="transformedDepth">An transformed depth image to hold the output.</param>
        /// <param name="transformedCustom">An transformed custom image to hold the output.</param>
        /// <param name="interpolationType">Parameter that controls how pixels in custom image should be interpolated when transformed to color camera space.</param>
        /// <param name="invalidCustomValue">Defines the custom image pixel value that should be written to transformedCustom in case the corresponding depth pixel can not be transformed into the color camera space.</param>
        /// <remarks>
        /// The <paramref name="transformedDepth"/> Image must be of the resolution of the color camera, and
        /// of the pixel format of the depth image.
        /// The <paramref name="transformedCustom"/> Image must be of the resolution of the color camera, and
        /// of the pixel format of the custom image.
        /// </remarks>
        public void DepthImageToColorCameraCustom(Image depth, Image custom, Image transformedDepth, Image transformedCustom, TransformationInterpolationType interpolationType, uint invalidCustomValue)
        {
            if (depth == null)
            {
                throw new ArgumentNullException(nameof(depth));
            }

            if (custom == null)
            {
                throw new ArgumentNullException(nameof(custom));
            }

            if (transformedDepth == null)
            {
                throw new ArgumentNullException(nameof(transformedDepth));
            }

            if (transformedCustom == null)
            {
                throw new ArgumentNullException(nameof(transformedCustom));
            }

            if (custom.Format != ImageFormat.Custom8 && custom.Format != ImageFormat.Custom16)
            {
                throw new NotSupportedException("Failed to support this format of custom image!");
            }

            if (transformedCustom.Format != ImageFormat.Custom8 && transformedCustom.Format != ImageFormat.Custom16)
            {
                throw new NotSupportedException("Failed to support this format of transformed custom image!");
            }

            if (custom.Format != transformedCustom.Format)
            {
                throw new NotSupportedException("Failed to support this different format of custom image and transformed custom image!!");
            }

            lock (this)
            {
                // Create a new reference to the Image objects so that they cannot be disposed while
                // we are performing the transformation
                using (Image depthReference = depth.Reference())
                    using (Image customReference = custom.Reference())
                        using (Image transformedDepthReference = transformedDepth.Reference())
                            using (Image transformedCustomReference = transformedCustom.Reference())
                            {
                                // Ensure changes made to the managed memory are visible to the native layer
                                depthReference.FlushMemory();
                                customReference.FlushMemory();

                                AzureKinectException.ThrowIfNotSuccess(() => NativeMethods.k4a_transformation_depth_image_to_color_camera_custom(
                                                                           this.handle,
                                                                           depthReference.DangerousGetHandle(),
                                                                           customReference.DangerousGetHandle(),
                                                                           transformedDepthReference.DangerousGetHandle(),
                                                                           transformedCustom.DangerousGetHandle(),
                                                                           interpolationType,
                                                                           invalidCustomValue));

                                // Copy the native memory back to managed memory if required
                                transformedDepthReference.InvalidateMemory();
                                transformedCustom.InvalidateMemory();
                            }
            }
        }