/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 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());
}
}
}
}