///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Copyright (c) 2016 Raytrix GmbH. All rights reserved. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // namespace: Example.LFR.CS.BasicC ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// namespace Example.LFR.CS.BasicC { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// The application's main form. /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public partial class MainForm : Form { // The first CLUViz Control instance CLUViz.Net.ViewCtrl m_xCluView1; // The second CLUViz Control instance CLUViz.Net.ViewCtrl m_xCluView2; // The CLUViz Engine instance CLUViz.Net.EngineCtrl m_xCluEngine; // The CUDA compute instance. Rx.LFR.Net.CudaCompute m_xCudaCompute; // The open GL interop instance that holes the open GL context Rx.LFR.Net.OpenGlInterop m_xOpenGlInterop; Rx.Net.Image m_xImgDepth3D; Rx.Net.ImageFormat m_xImgFmtTotalFocus; Rx.Net.ImageFormat m_xImgFmtDepthMap; Rx.Net.ImageFormat m_xImgFmtDepth3D; delegate void ShowPositionHandler(Rx.Net.Vector3D vPos); ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// Default constructor. /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public MainForm() { InitializeComponent(); // Initialize the depth 3D image m_xImgDepth3D = new Rx.Net.Image(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// Event handler for mouse events in left CluView. /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void OnCluViewMouseEvent1(CLUViz.Net.SMouseEventData xMouseEventData) { // /////////////////////////////////////////////////////////////////////// // !!!!!! IMPORTANT !!!!!! // This function runs in the thread context of the CluView visualization, // which is a different thread to the Windows Forms GUI thread. // Therefore, you MUST NOT call any GUI functions in this event handler! // Furthermore, you MUST NOT call any functions on the CluView control! // Instead INVOKE events with BeginInvoke() to perform operations in the // context of the GUI thread. // /////////////////////////////////////////////////////////////////////// if (xMouseEventData.bIsLeftButtonDown && (xMouseEventData.bIsCtrlDown || (xMouseEventData.eMouseEventType == CLUViz.Net.EMouseEventType.Select))) { BeginInvoke(new ShowPositionHandler(OnShowImagePosition), xMouseEventData.vMouseDragLocal); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// Event handler for mouse events in left CluView. /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public void OnCluViewMouseEvent2(CLUViz.Net.SMouseEventData xMouseEventData) { // /////////////////////////////////////////////////////////////////////// // !!!!!! IMPORTANT !!!!!! // This function runs in the thread context of the CluView visualization, // which is a different thread to the Windows Forms GUI thread. // Therefore, you MUST NOT call any GUI functions in this event handler! // Furthermore, you MUST NOT call any functions on the CluView control! // Instead INVOKE events with BeginInvoke() to perform operations in the // context of the GUI thread. // /////////////////////////////////////////////////////////////////////// if (xMouseEventData.bIsLeftButtonDown && (xMouseEventData.bIsCtrlDown || (xMouseEventData.eMouseEventType == CLUViz.Net.EMouseEventType.Select))) { BeginInvoke(new ShowPositionHandler(OnShowDepthImagePosition), xMouseEventData.vMouseDragLocal); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// Show the image position under the mouse as pixel position in image and 3D metric position /// for metrically calibrated images. /// /// This function needs to be "unsafe" since we need to access native memory. /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// unsafe void OnShowImagePosition(Rx.Net.Vector3D vPos) { // Get the pointer to the image data in native memory. float* pfData = (float*) m_xImgDepth3D.GetDataPtr(); // The size of the total focus image may be different to the depth map. // Therefore, we calculate the size ratios of the two images. double dRatioX = (double) (m_xImgFmtDepth3D.Width - 1) / (double) (m_xImgFmtTotalFocus.Width - 1); double dRatioY = (double) (m_xImgFmtDepth3D.Height - 1) / (double) (m_xImgFmtTotalFocus.Height - 1); // Get the integer pixel position in the image int iX = Rx.Net.Math.Clamp((int) System.Math.Floor(vPos.X * dRatioX), 0, m_xImgFmtDepth3D.Width - 1); int iY = (m_xImgFmtDepth3D.Height - 1) - Rx.Net.Math.Clamp((int) System.Math.Floor(vPos.Y * dRatioY), 0, m_xImgFmtDepth3D.Height - 1); // Calculate memory position in depth 3d data. // There are 4 floats per pixel giving X,Y,Z and an unused component. int iPos = (iY * m_xImgFmtDepth3D.Width + iX) * 4; float fX = pfData[iPos]; float fY = pfData[iPos + 1]; float fZ = pfData[iPos + 2]; toolStripStatusLabel1.Text = String.Format("Pixel: {0:F1}; {1:F1} - Position: {2:F2}; {3:F2}; {4:F3} mm", vPos.X, vPos.Y, fX, fY, fZ); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// Show the image position under the mouse as pixel position in image and 3D metric position /// for metrically calibrated images. /// /// This function needs to be "unsafe" since we need to access native memory. /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// unsafe void OnShowDepthImagePosition(Rx.Net.Vector3D vPos) { // Get the pointer to the image data in native memory. float* pfData = (float*) m_xImgDepth3D.GetDataPtr(); // The size of the colored depth map image may be different to the depth map. // Therefore, we calculate the size ratios of the two images. double dRatioX = (double) (m_xImgFmtDepth3D.Width - 1) / (double) (m_xImgFmtDepthMap.Width - 1); double dRatioY = (double) (m_xImgFmtDepth3D.Height - 1) / (double) (m_xImgFmtDepthMap.Height - 1); // Get the integer pixel position in the image int iX = Rx.Net.Math.Clamp((int) System.Math.Floor(vPos.X * dRatioX), 0, m_xImgFmtDepth3D.Width - 1); int iY = (m_xImgFmtDepth3D.Height - 1) - Rx.Net.Math.Clamp((int) System.Math.Floor(vPos.Y * dRatioY), 0, m_xImgFmtDepth3D.Height - 1); // Calculate memory position in depth 3d data. // There are 4 floats per pixel giving X,Y,Z and an unused component. int iPos = (iY * m_xImgFmtDepth3D.Width + iX) * 4; float fX = pfData[iPos]; float fY = pfData[iPos + 1]; float fZ = pfData[iPos + 2]; toolStripStatusLabel1.Text = String.Format("Pixel: {0:F1}; {1:F1} - Position: {2:F2}; {3:F2}; {4:F3} mm", vPos.X, vPos.Y, fX, fY, fZ); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// Event handler. Called by MainForm for shown events. /// /// /// Source of the event. /// Event information. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private void MainForm_Shown(object sender, EventArgs e) { try { // Start the CLU engine m_xCluEngine = new CLUViz.Net.EngineCtrl(); m_xCluEngine.Start(); // Now create the CLU control and embed it into the "panelClu" control. // Changes in size and position of panelClu are automatically applied // to the CLU control. // Since the CLU control display runs in a separate thread, we must // create an OpenGL rendering context for the CUDA thread (this thread) // that shares its texture memory with the OpenGL rendering context in // the CLU control thread. This is done automatically by setting the second // parameter to "true". m_xCluView1 = new CLUViz.Net.ViewCtrl(true, 0); // Now create the second CLU control that shares the OpenGL rendering // context with the first CLU control and CUDA. m_xCluView2 = new CLUViz.Net.ViewCtrl(false, m_xCluView1.SharedGLRC); // We now have to make this shared rendering context current, // since otherwise all CUDA commands will fail. That is, // CUDA is only initialized once this rendering context is made current. // Note that the shared rendering context for this thread is attached to // an invisible dummy window that is held internally. m_xCluView1.MakeCurrentSharedRC(); // Initialize the open GL interop instance and store the current context in it. m_xOpenGlInterop = new Rx.LFR.Net.OpenGlInterop(); m_xOpenGlInterop.StoreCurrentContext(); // Authenticate the Lightfield Runtime. This always has to be called first. Rx.LFR.Net.LightFieldRuntime.Authenticate(); // Enumerate all CUDA devices at the beginning Rx.LFR.Net.Cuda.EnumerateCudaDevices(); // Assign the CUDA device and the calibration m_xCudaCompute = new Rx.LFR.Net.CudaCompute(); m_xCudaCompute.GetParams().SetValue(Rx.LFR.Net.Params.ECudaCompute.PreProc_DataType, (uint) Rx.InteropNet.Runtime28.EDataType.UByte); if (Rx.LFR.Net.Cuda.GetDeviceCount() == 0) { throw new Exception("No Cuda device found"); } m_xCudaCompute.SetCudaDevice(Rx.LFR.Net.Cuda.GetDevice((int) 0)); // Add the first CluView to the left panel panelClu1.Controls.Add(m_xCluView1); m_xCluView1.Dock = DockStyle.Fill; // Add the second CluView to the right panel panelClu2.Controls.Add(m_xCluView2); m_xCluView2.Dock = DockStyle.Fill; // Now set the visualization CLUScript from the resources. m_xCluView1.SetScript(Example.LFR.CS.BasicC.Properties.Resources.View_Image_01); m_xCluView2.SetScript(Example.LFR.CS.BasicC.Properties.Resources.View_Image_01); // Enable Mouse event callbacks m_xCluView1.ViewMouseEvent += new CLUViz.Net.ViewMouseEventHandler(OnCluViewMouseEvent1); m_xCluView1.EnableViewMouseEvents(true); // Enable Mouse event callbacks m_xCluView2.ViewMouseEvent += new CLUViz.Net.ViewMouseEventHandler(OnCluViewMouseEvent2); m_xCluView2.EnableViewMouseEvents(true); } catch (Rx.Net.RxException xEx) { MessageBox.Show(xEx.ToString()); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// Event handler. Called by loadRayImageToolStripMenuItem for click events. /// /// /// Source of the event. /// Event information. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private void loadRayImageToolStripMenuItem_Click(object sender, EventArgs e) { try { openFileDialog.Filter = "Ray Image|*.ray"; DialogResult diagRes = openFileDialog.ShowDialog(); if (diagRes == DialogResult.OK) { Rx.LFR.Net.RayImage xRayImg = new Rx.LFR.Net.RayImage(); // Read the ray file Rx.LFR.Net.RayFileReader.Read(openFileDialog.FileName, xRayImg); // Upload the new image to the compute class and apply the calibration m_xCudaCompute.ApplyCalibration(xRayImg.GetCalibration(), true); m_xCudaCompute.UploadRawImage(xRayImg); // Initialize image view, since new image may have different dimensions. NewImage(); } } catch (Rx.Net.RxException xEx) { MessageBox.Show(xEx.ToString()); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// React to user selecting "Save Views" from main menu. /// /// /// Source of the event. /// Event information. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private void saveViewsToolStripMenuItem_Click(object sender, EventArgs e) { try { DialogResult diagRes = saveFileDialog.ShowDialog(); if (diagRes == DialogResult.OK) { // Export the both views. // They are saved based on the extension chosen. The second view is also always saved as .bmp. String sFileName = saveFileDialog.FileName; String sFileName2 = System.IO.Path.GetDirectoryName(sFileName); sFileName2 += @"\" + System.IO.Path.GetFileNameWithoutExtension(sFileName); sFileName2 += "_right_hand_view"; String sFileName2Bmp = sFileName2 + ".bmp"; String sFileName2Ply = sFileName2 + ".ply"; sFileName2 += System.IO.Path.GetExtension(sFileName); // Get the image access interface. This interface allows: // * Download CUDA image into host memory // * Upload a host image to CUDA to overwrite a certain CUDA image // * Access to CUDA device pointer to do own CUDA image processing Rx.LFR.Net.ICudaDataImages xImages = (m_xCudaCompute.GetInterface(Rx.LFR.Net.Interfaces.ECudaCompute.Images)) as Rx.LFR.Net.ICudaDataImages; // Get the images from CUDA memory into local memory Rx.Net.Image xImgTotal = new Rx.Net.Image(); Rx.Net.Image xImgDepth = new Rx.Net.Image(); Rx.Net.Image xImgDepth3D = new Rx.Net.Image(); xImages.Download(Rx.LFR.Net.EImage.DepthMapColored_View_Object_Pinhole, xImgDepth); xImages.Download(Rx.LFR.Net.EImage.TotalFocus_View_Object_Pinhole, xImgTotal); xImages.Download(Rx.LFR.Net.EImage.Depth3D, xImgDepth3D); // The ImageFile class allows you to save files with a codec that is chosen by the // extension given. // Supported suffixes: .png, .bmp, .tiff, .jpg Rx.Net.Codec.ImageFile xImgHelper = new Rx.Net.Codec.ImageFile(); xImgHelper.Save(xImgTotal, sFileName); xImgHelper.Save(xImgDepth, sFileName2); xImgHelper.Save(xImgDepth, sFileName2Bmp); Rx.LFR.Net.Export.SaveMeshPLY(sFileName2Ply, xImgTotal, xImgDepth3D, false); } } catch (Rx.Net.RxException xEx) { MessageBox.Show(xEx.ToString()); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// Initialize visualization for new image. /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private void NewImage() { try { // Preprocess the image -> must be done before any other image processing can be performened m_xCudaCompute.Compute_PreProcess(); // Calculate the raw depth image m_xCudaCompute.Compute_DepthRay(); // Calculate the depth map m_xCudaCompute.Compute_DepthMap(Rx.LFR.Net.ESpace.View_Object_Pinhole); // Calculate the 3D depth map m_xCudaCompute.Compute_Depth3D(); // Color code the depth map for better visualization m_xCudaCompute.Compute_DepthColorCode(Rx.LFR.Net.ESpace.View_Object_Pinhole); // Create a refocused image m_xCudaCompute.Compute_TotalFocus(Rx.LFR.Net.ESpace.View_Object_Pinhole); /************************************************************************/ /* Set the total focus image on the left side */ /************************************************************************/ // Get the texture access interface. This interface allows: // * Updating the texture. Rx.LFR.Net.ICudaDataTextures xTexture = (m_xCudaCompute.GetInterface(Rx.LFR.Net.Interfaces.ECudaCompute.Textures)) as Rx.LFR.Net.ICudaDataTextures; // Update texture stored in the open GL interop instance with the given image id. xTexture.UpdateTexture(Rx.LFR.Net.EImage.TotalFocus_View_Object_Pinhole, m_xOpenGlInterop); // Get the image access interface. This interface allows: // * Download CUDA image into host memory // * Upload a host image to CUDA to overwrite a certain CUDA image // * Access to CUDA device pointer to do own CUDA image processing Rx.LFR.Net.ICudaDataImages xImages = (m_xCudaCompute.GetInterface(Rx.LFR.Net.Interfaces.ECudaCompute.Images)) as Rx.LFR.Net.ICudaDataImages; // Get the total focus image format m_xImgFmtTotalFocus = xImages.GetImageFormat(Rx.LFR.Net.EImage.TotalFocus_View_Object_Pinhole); // Initialize the CLUScript variables iWidth and iHeight with // the dimensions of the refocused image. m_xCluView1.SetVarNumber("iWidth", m_xImgFmtTotalFocus.Width); m_xCluView1.SetVarNumber("iHeight", m_xImgFmtTotalFocus.Height); // Check if this is a luminance image bool bLuminance = ((m_xImgFmtTotalFocus.PixelType == Rx.InteropNet.Runtime28.EPixelType.Lum) || (m_xImgFmtTotalFocus.PixelType == Rx.InteropNet.Runtime28.EPixelType.LumA)); // Execute the script with ToolName == "Image_Create". // This prepared the image view for an image of the given size. // The image itself is not copied at this point. m_xCluView1.ExecScript("Image_Create"); // Get the texture id uint uiTexID = m_xOpenGlInterop.GetTextureID(Rx.LFR.Net.EImage.TotalFocus_View_Object_Pinhole); if (uiTexID > 0) { // Set the id to the cluView script m_xCluView1.SetVarNumber("texImage", (int) uiTexID); // If the refocused image is of luminance type, we need to select // a different shader for the image display. if (bLuminance) { m_xCluView1.ExecScript("Image_Lum_Enable"); } // Tell the CLU control to redraw the current scene tree. // This does not re-execute the script itself. // It needs to be called to ensure that the new content of // the image texture is displayed. m_xCluView1.Update(); } /************************************************************************/ /* Set the depth image on the right side */ /************************************************************************/ // Download the depth 3D image which is needed for the calculations xImages.Download(Rx.LFR.Net.EImage.Depth3D, m_xImgDepth3D); // Get the 3d depth map image format m_xImgFmtDepth3D = xImages.GetImageFormat(Rx.LFR.Net.EImage.Depth3D); // Read image format of resultant color coded depth image. m_xImgFmtDepthMap = xImages.GetImageFormat(Rx.LFR.Net.EImage.DepthMapColored_View_Object_Pinhole); // Initialize the CLUScript variables iWidth and iHeight with // the dimensions of the refocused image. m_xCluView2.SetVarNumber("iWidth", m_xImgFmtDepthMap.Width); m_xCluView2.SetVarNumber("iHeight", m_xImgFmtDepthMap.Height); // Execute the script with ToolName == "Image_Create". // This prepared the image view for an image of the given size. // The image itself is not copied at this point. m_xCluView2.ExecScript("Image_Create"); // Update texture stored in the open GL interop instance with the given image id. xTexture.UpdateTexture(Rx.LFR.Net.EImage.DepthMapColored_View_Object_Pinhole, m_xOpenGlInterop); // Get the texture id uiTexID = m_xOpenGlInterop.GetTextureID(Rx.LFR.Net.EImage.DepthMapColored_View_Object_Pinhole); // Set the id to the CluView script m_xCluView2.SetVarNumber("texImage", (int) uiTexID); // Tell the CLU control to redraw the current scene tree. // This does not re-execute the script itself. // It needs to be called to ensure that the new content of // the image texture is displayed. m_xCluView2.Update(); } catch (Rx.Net.RxException xEx) { MessageBox.Show(xEx.ToString()); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// /// Redisplay the image. /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private void UpdateImage() { try { // Get the texture access interface. This interface allows: // * Updating the texture. Rx.LFR.Net.ICudaDataTextures xTexture = (m_xCudaCompute.GetInterface(Rx.LFR.Net.Interfaces.ECudaCompute.Textures)) as Rx.LFR.Net.ICudaDataTextures; // Update texture stored in the open GL interop instance with the given image id. xTexture.UpdateTexture(Rx.LFR.Net.EImage.TotalFocus_View_Object_Pinhole, m_xOpenGlInterop); // Get the texture id uint uiTexID = m_xOpenGlInterop.GetTextureID(Rx.LFR.Net.EImage.TotalFocus_View_Object_Pinhole); if (uiTexID > 0) { // Set the id to the CluView script m_xCluView1.SetVarNumber("texImage", (int) uiTexID); // Tell the CLU control to redraw the current scene tree. // This does not re-execute the script itself. // It needs to be called to ensure that the new content of // the image texture is displayed. m_xCluView1.Update(); } // Update texture stored in the open GL interop instance with the given image id. xTexture.UpdateTexture(Rx.LFR.Net.EImage.DepthMapColored_View_Object_Pinhole, m_xOpenGlInterop); // Get the texture id uiTexID = m_xOpenGlInterop.GetTextureID(Rx.LFR.Net.EImage.DepthMapColored_View_Object_Pinhole); if (uiTexID > 0) { // Set the id to the CluView script m_xCluView2.SetVarNumber("texImage", (int) uiTexID); // Tell the CLU control to redraw the current scene tree. // This does not re-execute the script itself. // It needs to be called to ensure that the new content of // the image texture is displayed. m_xCluView2.Update(); } } catch (Rx.Net.RxException xEx) { MessageBox.Show(xEx.ToString()); } } } }