/// <summary> /// Sets the image wrapper provided to a property. /// </summary> /// <param name="nativeMethod">Native set method.</param> /// <param name="cachedImage">Reference to the cached image wrapper used by this class.</param> /// <param name="value">Value to assign the image wrapper to.</param> /// <remarks> /// This function takes ownership of the wrapper and stores it in the class. If there was /// a previous wrapper owned by the class, this function will dispose it. /// </remarks> private void SetImageWrapperAndDisposePrevious( Action <NativeMethods.k4a_capture_t, NativeMethods.k4a_image_t> nativeMethod, ref Image cachedImage, Image value) { lock (this) { if (this.disposedValue) { throw new ObjectDisposedException(nameof(Capture)); } // If the assignment is a new managed wrapper we need // to release the reference to the old wrapper. // If it is the same object though, we should not dispose it. if (cachedImage != null && !object.ReferenceEquals( cachedImage, value)) { cachedImage.Dispose(); } cachedImage = value; // Take an extra reference on the image to ensure it isn't disposed // prior while we have the handle. using (Image reference = cachedImage.Reference()) { nativeMethod(this.handle, cachedImage.DangerousGetHandle()); } } }
/// <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(); } } }
/// <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); } }
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() )); } } }
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())); } } }
/// <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(); } } }
/// <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(); } } }
/// <summary> /// Retrieves a native image handle from the native API. /// </summary> /// <param name="nativeMethod">Native method to retrieve the image.</param> /// <param name="cachedImage">A cached instance of the Image that we return to callers. (Callers may dispose this image, although they shouldn't).</param> /// <remarks> /// If this is the first time calling the property, we construct a new wrapper. /// If the handle is for an Image we have already constructed a wrapper for, we return the existing wrapper. /// If the handle is for a different Image, we construct a new wrapper and dispose the old one. /// If existing wrapper has been disposed, we throw an exception. /// </remarks> private void UpdateImageWrapperAndDisposePrevious( Func <NativeMethods.k4a_capture_t, NativeMethods.k4a_image_t> nativeMethod, ref Image cachedImage) { // Lock must be held to ensure the Image in cache is not replaced while we are inspecting it // It is still possible for that Image to be accessed or Disposed while this lock is held lock (this) { if (this.disposedValue) { throw new ObjectDisposedException(nameof(Capture)); } // Get the image object from the native SDK // We now own a reference on this image and must eventually release it. NativeMethods.k4a_image_t nativeImageHandle = nativeMethod(this.handle); // This try block ensures that we close image (and release our reference) if we fail // to create a new Image. try { // If this Capture previously had an image if (cachedImage != null) { // Get the native pointer IntPtr imageHandleValue = nativeImageHandle.DangerousGetHandle(); // Get the handle value from the cached image. If it has been disposed, this // will throw an exception, which will be passed to the caller. // Take an extra reference on the cachedImage to ensure it doesn't get disposed // after getting the IntPtr from the handle. using (Image reference = cachedImage.Reference()) { if (reference.DangerousGetHandle().DangerousGetHandle() != imageHandleValue) { // The image has changed, invalidate the current image and construct new wrappers cachedImage.Dispose(); cachedImage = null; } } } if (cachedImage == null && !nativeImageHandle.IsInvalid) { // Construct a new wrapper and return it // The native function may have returned #pragma warning disable CA2000 // Dispose objects before losing scope cachedImage = new Image(nativeImageHandle); #pragma warning restore CA2000 // Dispose objects before losing scope // Since we have wrapped image, it is now owned by Image and we should no longer close it nativeImageHandle = null; } } finally { // Ensure the native handle is closed if we have a failure creating the Image object if (nativeImageHandle != null) { nativeImageHandle.Close(); } } } }
// This function retrieves a native image handle from the native API. // // If this is the first time calling the property, we construct a new wrapper // If the handle is for an Image we have already constructed a wrapper for, we return the existing wrapper // If the handle is for a different Image, or if the wrapper has already been disposed, we construct a new wrapper and dispose the old one private Image GetImageWrapperAndDisposePrevious(Func <NativeMethods.k4a_capture_t, NativeMethods.k4a_image_t> nativeMethod, ref Image cache) { // Lock must be held to ensure the Image in cache is not replaced while we are inspecting it // It is still possible for that Image to be accessed or Disposed while this lock is held lock (this) { if (this.disposedValue) { throw new ObjectDisposedException(nameof(Capture)); } NativeMethods.k4a_image_t image = nativeMethod(this.handle); // This try block ensures that we close image try { if (cache != null) { IntPtr imageHandle = image.DangerousGetHandle(); // Only attempt to access cache in this try block try { // If cache was disposed before we called the native accessor (by the caller or another thread), // the handle could have been reused and the values would incorrectly match. However, cache.DangerousGetHandle() // will throw an exception, and we will correctly construct a new image wrapper. if (cache.DangerousGetHandle().DangerousGetHandle() != imageHandle) { // The image has changed, invalidate the current image and construct a new wrapper cache.Dispose(); cache = null; } } catch (ObjectDisposedException) { // If cache has been disposed by the caller or another thread we will discard // it and construct a new wrapper cache = null; } } if (cache == null && !image.IsInvalid) { // Construct a new wrapper and return it // The native function may have returned cache = new Image(image); // Since we have wrapped image, it is now owned by the UnsafeImage object and we should no longer close it image = null; } } finally { // Ensure the native handle is closed if we have a failure creating the Image object if (image != null) { image.Close(); } } return(cache); } }