Click here to show toolbars of the Web Online Help System: show toolbars
 

//***************************************************************************************/
// 
// File name: Simple3dStiching.cpp  
// Location: See Matrox Example Launcher in the MIL Control Center
// 
//
// Synopsis:  This program contains an example of 3D surface alignment. It aligns two partial
//            point clouds of a 3D object and stitches them into a single complete point cloud.
//            See the PrintHeader() function below for detailed description.
//
// Copyright (C) Matrox Electronic Systems Ltd., 1992-2016.
// All Rights Reserved
//***************************************************************************************/                                  

#include <mil.h>
#include <math.h>

// Preprocessor switch to enable a 3d display (not available on Linux).
#if M_MIL_USE_LINUX
#define ENABLE_DISPLAY_3D  0
#else
#define ENABLE_DISPLAY_3D  1
#include <MdispD3D.h>
#endif


//****************************************************************************
// Example description.
//****************************************************************************
void PrintHeader()   
   {
   MosPrintf(MIL_TEXT("[EXAMPLE NAME]\n"));
   MosPrintf(MIL_TEXT("Simple3dStiching\n\n"));

   MosPrintf(MIL_TEXT("[SYNOPSIS]\n"));
   MosPrintf(MIL_TEXT("This example demonstrates how to use the 3D surface alignment   \n"));
   MosPrintf(MIL_TEXT("operation to align two partial point clouds of a 3D object      \n"));
   MosPrintf(MIL_TEXT("and stitch them into a single complete point cloud.\n"));
   MosPrintf(MIL_TEXT("\n"));

   MosPrintf(MIL_TEXT("[MODULES USED]\n"));
   MosPrintf(MIL_TEXT("Modules used: 3dMap, Buffer, Calibration, Display,\n")
             MIL_TEXT("Graphics, Image Processing.\n\n"));
   }

// Functions declarations.
void AddLutColorForInvalidDepth(MIL_ID MilLut, MIL_UINT8 InvalidDepthGray);
MIL_ID CreateLutLegend(MIL_ID MilSystem);
void   DrawLutLegend(MIL_ID DstImage, MIL_ID MilLutLegend);
void   GetBoxPoints(MIL_ID     MilDepthMap, 
                    MIL_DOUBLE BoxPtsInAbsX[], 
                    MIL_DOUBLE BoxPtsInAbsY[], 
                    MIL_DOUBLE BoxPtsInAbsZ[]);
void   DrawExpectedOverlap3dBoxes(MIL_ID MilSystem,
                                  MIL_ID MilDepthMap[], 
                                  MIL_ID MilDisplay[]);
void   DrawResultOverlap3dBoxes(MIL_ID MilSystem,
                                MIL_ID MilDepthMap[], 
                                MIL_ID MilDisplay[], 
                                MIL_ID MilPointCloud[]);
void   DrawBoxInOneDepthMap(MIL_ID     MilSystem, 
                            MIL_ID     MilDisplay, 
                            MIL_ID     MilDepthMap, 
                            MIL_DOUBLE BoxPtsInAbsX[],
                            MIL_DOUBLE BoxPtsInAbsY[],
                            MIL_DOUBLE BoxPtsInAbsZ[],
                            MIL_INT    BoxColor);
void GenerateUnorganizedPointClouds(MIL_ID MilPointCloud[]);
bool CheckForRequiredMILFile(MIL_CONST_TEXT_PTR FileName);

// Enumerators definitions.
enum { eReference = 0, eTarget, eStitched, ePrealigned, e3dDispReference, e3dDispAlignedTarget, eNbDepthMap};

enum StitchMethodEnum
   {
   eOverlapRegionAlignOnly = 0,
   eOverlapFullAlignOnly,
   eBothAlign
   };

// The number of point clouds.
static const MIL_INT NB_POINT_CLOUD = 2;

// Depth maps parameters definitions.
static const MIL_INT    NUM_DEPTHMAP_VALUES    =  65536;
static const MIL_INT    DEPTHMAP_INVALID_VALUE =  NUM_DEPTHMAP_VALUES-1;
static const MIL_UINT8  DEPTHMAP_INVALID_COLOR =  128;
static const MIL_INT    DEPTH_MAP_SIZE_X       =  525; 
static const MIL_INT    DEPTH_MAP_SIZE_Y       =  400;

// Extraction box definitions.
static const MIL_DOUBLE EXTRACTION_BOX_SIZE_X   = 220.0;
static const MIL_DOUBLE EXTRACTION_BOX_SIZE_Y   = 200.0;
static const MIL_DOUBLE EXTRACTION_BOX_SIZE_Z   = -66;

// Expected target location.
static const MIL_DOUBLE BOX_OVERLAP             = 0.20;
static const MIL_DOUBLE BOX_USED_OVERLAP        = 0.9 * BOX_OVERLAP;
static const MIL_DOUBLE EXP_TX  = 0.0;
static const MIL_DOUBLE EXP_TY  = -15.0;
static const MIL_DOUBLE EXP_TZ  = 0.0;
static const MIL_DOUBLE EXP_RX  = 0.0;
static const MIL_DOUBLE EXP_RY  = 0.0;
static const MIL_DOUBLE EXP_RZ  = 6.0;

// Align context controls definitions.
static const MIL_INT    DECIMATION_STEP_MODEL              = 8;
static const MIL_INT    DECIMATION_STEP_OBJECT             = 8;
static const MIL_DOUBLE MODEL_OVERLAP                      = 95; // %
static const MIL_INT    MAX_ITERATIONS                     = 100;
static const MIL_DOUBLE ALIGN_RMS_ERROR_RELATIVE_THRESHOLD = 0.5;  // %
static const MIL_INT    ALIGN_ERROR_MINIMIZATION_METRIC    = M_POINT_TO_POINT;
static StitchMethodEnum STITCH_ALIGN_METHOD                = eBothAlign;

// Visualization variables definitions.
static const MIL_INT    NUM_BOX_POINTS       =  24; // A 3d cube box has 24 points.
static const MIL_DOUBLE DRAW_BOX_MIN_X       =  -EXTRACTION_BOX_SIZE_X/2;
static const MIL_DOUBLE DRAW_BOX_MIN_Y       =  -EXTRACTION_BOX_SIZE_Y/2 * BOX_USED_OVERLAP;
static const MIL_DOUBLE DRAW_BOX_MIN_Z       =  EXTRACTION_BOX_SIZE_Z/2;
static const MIL_DOUBLE DRAW_BOX_MAX_X       =  EXTRACTION_BOX_SIZE_X/2;
static const MIL_DOUBLE DRAW_BOX_MAX_Y       =  EXTRACTION_BOX_SIZE_Y/2 * BOX_USED_OVERLAP;
static const MIL_DOUBLE DRAW_BOX_MAX_Z       =  -EXTRACTION_BOX_SIZE_Z/2;

// Displays constants.
static const MIL_INT WINDOWS_OFFSET_X = 15;
static const MIL_INT NB_DISPLAY = 3;
static MIL_CONST_TEXT_PTR DISPLAY_NAMES[NB_DISPLAY] = 
   {
   MIL_TEXT("Reference partial point"),
   MIL_TEXT("Target partial point"),
   MIL_TEXT("Stitched point cloud")
   };

static const MIL_INT DISP_3D_SIZE_X = 906;
static const MIL_INT DISP_3D_SIZE_Y = 680;
static const MIL_DOUBLE DISP_3D_LOOK_AT_X = 0.0;
static const MIL_DOUBLE DISP_3D_LOOK_AT_Y = 0.0;
static const MIL_DOUBLE DISP_3D_LOOK_AT_Z = 0.0;
static const MIL_DOUBLE DISP_3D_EYE_DIST  = 263.28;
static const MIL_DOUBLE DISP_3D_EYE_THETA = 59;
static const MIL_DOUBLE DISP_3D_EYE_PHI   = 36.61;

// Point clouds generation information.
// Input data files.
static const MIL_TEXT_CHAR* const FILE_SOURCE_POINT_CLOUD[2] = 
   {
      M_IMAGE_PATH MIL_TEXT("/Simple3dStitching/StitchReference.ply"),
      M_IMAGE_PATH MIL_TEXT("/Simple3dStitching/StitchTarget.ply")
   };

//*****************************************************************************
// Main.
//*****************************************************************************
int MosMain(void)
   {
   // Print example information in console.
   PrintHeader();

   // Allocate MIL objects.
   MIL_ID MilApplication = MappAlloc(M_NULL, M_DEFAULT, M_NULL);
  
   //Check for required example files.
   if (!CheckForRequiredMILFile(FILE_SOURCE_POINT_CLOUD[0]))
      {
      MappFree(MilApplication);
      return -1;
      }

   MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n"));

   MosGetch();
   
   MIL_ID MilSystem = MsysAlloc(M_DEFAULT, M_SYSTEM_HOST, M_DEFAULT, M_DEFAULT, &MilSystem);

   //-------------------------------------------------------------------------------------------
   // Create the unorganized point cloud.

   // Allocate 3D point cloud containers.
   MIL_ID MilPointCloud[NB_POINT_CLOUD];
   M3dmapAllocResult(MilSystem, M_POINT_CLOUD_CONTAINER, M_DEFAULT, &MilPointCloud[eReference]);
   M3dmapAllocResult(MilSystem, M_POINT_CLOUD_CONTAINER, M_DEFAULT, &MilPointCloud[eTarget]);
   MgraFont(M_DEFAULT, M_FONT_DEFAULT_SMALL);

   // Generate the unorganized point cloud.
   MosPrintf(MIL_TEXT("The reference and target point clouds are being generated..."));
   GenerateUnorganizedPointClouds(MilPointCloud);
   MosPrintf(MIL_TEXT("done.\n\n"));
   
   //-------------------------------------------------------------------------------
   // Initialize displays that will show the two partial depth maps and the stitched depth map.

   // Jet LUT.
   MIL_ID MilLut = MbufAllocColor(MilSystem, 3, NUM_DEPTHMAP_VALUES, 1, 8 + M_UNSIGNED, M_LUT, M_NULL);
   AddLutColorForInvalidDepth(MilLut, DEPTHMAP_INVALID_COLOR);

   // LUT legend.
   MIL_ID MilLutLegend = CreateLutLegend(MilSystem);

   // Initialize displays.
   MIL_ID MilDisplay[NB_DISPLAY];
   for (MIL_INT d = 0; d < NB_DISPLAY; d++)
      {
      // Allocate the display.
      MdispAlloc(MilSystem, M_DEFAULT, MIL_TEXT("M_DEFAULT"), M_WINDOWED, &MilDisplay[d]);

      // Associate the jet LUT
      MdispLut(MilDisplay[d], MilLut);

      // Some display controls
      MdispControl(MilDisplay[d], M_OVERLAY, M_ENABLE);
      MdispControl(MilDisplay[d], M_WINDOW_INITIAL_POSITION_X, (MIL_INT)(d*(WINDOWS_OFFSET_X + DEPTH_MAP_SIZE_X)));
      MdispControl(MilDisplay[d], M_UPDATE, M_DISABLE);

      // Add titles to displays.
      MdispControl(MilDisplay[d], M_TITLE, M_PTR_TO_DOUBLE(DISPLAY_NAMES[d]));
      }

   //--------------------------------------------------------------------------
   // Fully corrected depth maps from partial point cloud containers.

   // Generate fully corrected depth maps before 3d alignment.
   MIL_ID MilDepthMap[eNbDepthMap];
   for(MIL_INT d = 0; d < eNbDepthMap; d++)
      MbufAlloc2d(MilSystem, DEPTH_MAP_SIZE_X, DEPTH_MAP_SIZE_Y,
                  16 + M_UNSIGNED, M_IMAGE + M_PROC + M_DISP, &MilDepthMap[d]);

   for (MIL_INT p = 0; p < NB_POINT_CLOUD; p++)
      {
      // Point cloud containers controls for depth map extraction.
      M3dmapControl(MilPointCloud[p], M_DEFAULT, M_EXTRACTION_OVERLAP   , M_MAX);
      M3dmapControl(MilPointCloud[p], M_DEFAULT, M_EXTRACTION_SATURATION, M_DISABLE);
      
      // Set the box to extract the whole object.
      M3dmapSetBox(MilPointCloud[p], M_EXTRACTION_BOX, M_CENTER_AND_DIMENSION,
                   0.0, 0.0, 0.0,
                   EXTRACTION_BOX_SIZE_X, EXTRACTION_BOX_SIZE_Y, EXTRACTION_BOX_SIZE_Z);

      // Fully corrected depth maps extraction.
      M3dmapExtract(MilPointCloud[p], MilDepthMap[p], M_NULL, M_CORRECTED_DEPTH_MAP, M_ALL, M_DEFAULT);

      // Display the depth map.
      MdispSelect(MilDisplay[p], MilDepthMap[p]);
      DrawLutLegend(MilDepthMap[p], MilLutLegend);
      MdispControl(MilDisplay[p], M_UPDATE, M_NOW);
      }

   // Get the total number of points of the reference point cloud.
   MIL_INT ReferenceTotalNbPoints;
   M3dmapGet(MilPointCloud[eReference], M_ALL, M_POSITION, M_INCLUDE_POINTS_INSIDE_BOX_ONLY,
             64 + M_FLOAT, M_NULL, M_NULL, M_NULL, M_NULL, &ReferenceTotalNbPoints);

   // Get the number of points of the reference point cloud expected overlap.
   MIL_INT ReferenceOverlapNbOfPoints;
   M3dmapSetBox(MilPointCloud[eReference], M_DEFAULT, M_CENTER_AND_DIMENSION,
      0.0, 0.0, 0.0,
      EXTRACTION_BOX_SIZE_X, EXTRACTION_BOX_SIZE_Y* BOX_OVERLAP, EXTRACTION_BOX_SIZE_Z);
   M3dmapGet(MilPointCloud[eReference], M_ALL, M_POSITION, M_INCLUDE_POINTS_INSIDE_BOX_ONLY,
             64 + M_FLOAT, M_NULL, M_NULL, M_NULL, M_NULL, &ReferenceOverlapNbOfPoints);

   //--------------------------------------------------------------------------
   // 3D alignment.

   // Set the relative coordinate system of the target point cloud at the expected location with regards to the 
   // reference point cloud.
   McalSetCoordinateSystem(MilPointCloud[eTarget], M_RELATIVE_COORDINATE_SYSTEM, M_RELATIVE_COORDINATE_SYSTEM,
                           M_TRANSLATION, M_NULL, EXP_TX, EXP_TY, EXP_TZ, M_DEFAULT);
   McalSetCoordinateSystem(MilPointCloud[eTarget], M_RELATIVE_COORDINATE_SYSTEM, M_RELATIVE_COORDINATE_SYSTEM,
                           M_ROTATION_XYZ, M_NULL, EXP_RX, EXP_RY, EXP_RZ, M_DEFAULT);

   // Fully corrected depth maps extraction.
   M3dmapExtract(MilPointCloud[eTarget], MilDepthMap[ePrealigned], M_NULL, M_CORRECTED_DEPTH_MAP, M_ALL, M_DEFAULT);

   // Draw the overlap boxes.
   DrawExpectedOverlap3dBoxes(MilSystem, MilDepthMap, MilDisplay);

   MosPrintf(MIL_TEXT("The object's partial depth map, for both the reference and target, are \n")
             MIL_TEXT("displayed using pseudo colors. A rectangular box is also displayed to indicate\n")
             MIL_TEXT("the expected common overlap region for both partial point clouds.\n\n"));
   MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n"));
   MosGetch();

   // 3D pairwise registration (align) context and result.
   MIL_ID MilAlignmentContext = M3dmapAlloc(MilSystem, M_PAIRWISE_ALIGNMENT_CONTEXT, M_DEFAULT, M_NULL);
   MIL_ID MilAlignmentResult = M3dmapAllocResult(MilSystem, M_ALIGNMENT_RESULT, M_DEFAULT, M_NULL);

   // Pairwise alignment context controls.
   M3dmapControl(MilAlignmentContext, M_DEFAULT, M_DECIMATION_STEP_MODEL             , DECIMATION_STEP_MODEL);
   M3dmapControl(MilAlignmentContext, M_DEFAULT, M_DECIMATION_STEP_SCENE             , DECIMATION_STEP_OBJECT);
   M3dmapControl(MilAlignmentContext, M_DEFAULT, M_MAX_ITERATIONS                    , MAX_ITERATIONS);
   M3dmapControl(MilAlignmentContext, M_DEFAULT, M_ALIGN_RMS_ERROR_RELATIVE_THRESHOLD, ALIGN_RMS_ERROR_RELATIVE_THRESHOLD);
   M3dmapControl(MilAlignmentContext, M_DEFAULT, M_ERROR_MINIMIZATION_METRIC         , ALIGN_ERROR_MINIMIZATION_METRIC);
   M3dmapControl(MilAlignmentContext, M_DEFAULT, M_EXTRACTION_BOX_MODEL              , M_USE );
   M3dmapControl(MilAlignmentContext, M_DEFAULT, M_EXTRACTION_BOX_SCENE              , M_USE );
   
   // Alignment.
   MIL_INT64  AlignStatus = M_NULL;
   MIL_DOUBLE AlignComputationTime = 0.0;

   MappTimer(M_TIMER_RESET, M_NULL);

   if(STITCH_ALIGN_METHOD == eOverlapRegionAlignOnly || STITCH_ALIGN_METHOD == eBothAlign)
      {
      // Set the box to the expected overlap region.
      for(MIL_INT p = 0; p < NB_POINT_CLOUD; p++)
         M3dmapSetBox(MilPointCloud[p], M_DEFAULT, M_CENTER_AND_DIMENSION,
                      0.0, 0.0, 0.0,
                      EXTRACTION_BOX_SIZE_X, EXTRACTION_BOX_SIZE_Y* BOX_USED_OVERLAP, EXTRACTION_BOX_SIZE_Z);

      // Pre-align.
      M3dmapControl(MilAlignmentContext, M_DEFAULT, M_MODEL_OVERLAP, MODEL_OVERLAP);
      M3dmapAlign(MilAlignmentContext, MilPointCloud[eReference], M_ALL, MilPointCloud[eTarget], M_ALL,
                  M_NULL, MilAlignmentResult, M_DEFAULT, &AlignStatus);
      }
   
   if(STITCH_ALIGN_METHOD == eOverlapFullAlignOnly || STITCH_ALIGN_METHOD == eBothAlign)
      {
      MIL_ID MilPrealignment = eBothAlign ? MilAlignmentResult : M_NULL;
      
      // Set the box to the full point clouds.
      for(MIL_INT p = 0; p < NB_POINT_CLOUD; p++)
         M3dmapSetBox(MilPointCloud[p], M_DEFAULT, M_CENTER_AND_DIMENSION,
                      0.0, 0.0, 0.0,
                      EXTRACTION_BOX_SIZE_X, EXTRACTION_BOX_SIZE_Y, EXTRACTION_BOX_SIZE_Z);
      
      // Set the full model overlap based on the expected overlap between the two point clouds.
      MIL_DOUBLE FullModelOverlap = ((MIL_DOUBLE)ReferenceOverlapNbOfPoints / ReferenceTotalNbPoints)*MODEL_OVERLAP;
      M3dmapControl(MilAlignmentContext, M_DEFAULT, M_MODEL_OVERLAP, FullModelOverlap);

      // Align.
      M3dmapAlign(MilAlignmentContext, MilPointCloud[eReference], M_ALL, MilPointCloud[eTarget], M_ALL,
                  MilPrealignment, MilAlignmentResult, M_DEFAULT, &AlignStatus);
      }

   MappTimer(M_TIMER_READ, &AlignComputationTime);

   MosPrintf(MIL_TEXT("The 3D stitching between the two partial point clouds has been performed with \n")
             MIL_TEXT("the help of the points within the expected common overlap regions.\n\n"));

   // Interpret the result status.
   switch (AlignStatus)
      {
      case M_NOT_INITIALIZED: 
         MosPrintf(MIL_TEXT("Alignment failed: the alignment result is not initialized.\n\n")); 
         break;
      case M_NOT_ENOUGH_POINT_PAIRS:
         MosPrintf(MIL_TEXT("Alignment failed: point clouds are not overlapping.\n\n"));
         break;
      case M_MAX_ITERATIONS_REACHED:
         MosPrintf(MIL_TEXT("Alignment reached the maximum number of iterations allowed (%d)\n")
                   MIL_TEXT("in %.2f ms. Resulting fixture may or may not be valid.\n\n"), 
                   MAX_ITERATIONS, AlignComputationTime*1000);
         break;
      case M_ALIGN_RMS_ERROR_THRESHOLD_REACHED:
      case M_ALIGN_RMS_ERROR_RELATIVE_THRESHOLD_REACHED:
         MIL_DOUBLE AlignRmsError;
         M3dmapGetResult(MilAlignmentResult, M_DEFAULT, M_ALIGN_RMS_ERROR + M_TYPE_MIL_DOUBLE, &AlignRmsError);
         MosPrintf(MIL_TEXT("The alignment of the two partial point clouds\n")
                   MIL_TEXT("succeeded in %.2f ms with a final RMS error of %f mm.\n\n"),
                   AlignComputationTime*1000, AlignRmsError);
         break;
      default:
         MosPrintf(MIL_TEXT("Unknown alignment status.\n\n"));
      }

   // The object depth map buffer changed, so redraw the LUT legend.
   DrawLutLegend(MilDepthMap[eTarget], MilLutLegend);
   MdispControl(MilDisplay[eTarget], M_UPDATE, M_NOW);

   //--------------------------------------------------------------------------
   // Fixturing and stitching

   // Use alignment result to fixture the target point cloud on the reference point cloud.
   McalFixture(MilPointCloud[eTarget], M_NULL, M_MOVE_RELATIVE, M_RESULT_ALIGNMENT_3DMAP,
               MilAlignmentResult, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT);

   // Get the data of the second point cloud and put it in the first point cloud container.
   MIL_INT NbPoints;
   M3dmapGet(MilPointCloud[eTarget], M_ALL, M_POSITION, M_EXCLUDE_INVALID_POINTS,
             64 + M_FLOAT, M_NULL, M_NULL, M_NULL, M_NULL, &NbPoints);
   MIL_DOUBLE* X = new MIL_DOUBLE[NbPoints];
   MIL_DOUBLE* Y = new MIL_DOUBLE[NbPoints];
   MIL_DOUBLE* Z = new MIL_DOUBLE[NbPoints];
   M3dmapGet(MilPointCloud[eTarget], M_ALL, M_POSITION, M_EXCLUDE_INVALID_POINTS,
             64 + M_FLOAT, NbPoints, &X[0], &Y[0], &Z[0], M_NULL);
   M3dmapPut(MilPointCloud[eReference], M_POINT_CLOUD_LABEL(eTarget+1), M_POSITION,
             64 + M_FLOAT, NbPoints, X, Y, Z, MilPointCloud[0], M_DEFAULT);
   delete [] X;
   delete [] Y;
   delete [] Z;

   // Extract the stitched depth map.
   M3dmapSetBox(MilPointCloud[eReference], M_EXTRACTION_BOX, M_CENTER_AND_DIMENSION,
                0, 0, 0,
                EXTRACTION_BOX_SIZE_X, EXTRACTION_BOX_SIZE_Y, EXTRACTION_BOX_SIZE_Z);
   M3dmapExtract(MilPointCloud[eReference], MilDepthMap[eStitched], M_NULL, M_CORRECTED_DEPTH_MAP, M_ALL, M_DEFAULT);

   // Display stitched image.
   DrawLutLegend(MilDepthMap[eStitched], MilLutLegend);
   MdispSelect(MilDisplay[eStitched], MilDepthMap[eStitched]);

   // Draw a black 3D box in the stitched depth map to show the original overlap regions of the second partial point cloud.
   DrawResultOverlap3dBoxes(MilSystem, MilDepthMap, MilDisplay, MilPointCloud);

   MosPrintf(MIL_TEXT("The two point clouds have been stitched into a single point cloud. The depth\n")
             MIL_TEXT("map of the resulting stitched point cloud is displayed. A rectangular cube is\n") 
             MIL_TEXT("also displayed to indicate the expected overlap region that has been\n")
             MIL_TEXT("transformed.\n\n"));
   MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n"));
   MosGetch();

#if ENABLE_DISPLAY_3D

   // Extract the depth maps for the 3d display.
   M3dmapExtract(MilPointCloud[eReference], MilDepthMap[e3dDispReference], M_NULL,
                 M_CORRECTED_DEPTH_MAP, M_POINT_CLOUD_INDEX(eReference), M_DEFAULT);
   M3dmapExtract(MilPointCloud[eReference], MilDepthMap[e3dDispAlignedTarget], M_NULL,
                 M_CORRECTED_DEPTH_MAP, M_POINT_CLOUD_INDEX(eTarget), M_DEFAULT);

   // Allocate the colored buffer for the 3d display.
   MIL_ID MilDepthMapsColors[2];
   MbufAllocColor(MilSystem, 3, DEPTH_MAP_SIZE_X, DEPTH_MAP_SIZE_Y, 8+M_UNSIGNED, M_IMAGE+M_PROC, &MilDepthMapsColors[0]);
   MbufClear(MilDepthMapsColors[0], M_COLOR_BLUE);
   MbufAllocColor(MilSystem, 3, DEPTH_MAP_SIZE_X, DEPTH_MAP_SIZE_Y, 8+M_UNSIGNED, M_IMAGE+M_PROC, &MilDepthMapsColors[1]);
   MbufClear(MilDepthMapsColors[1], M_COLOR_RED);

   // Allocate the 3d display.
   MIL_DISP_D3D_HANDLE Mil3dDisplayHandle = MdepthSysD3DAlloc(M_NULL, M_NULL, M_NULL, M_NULL,
                                                              &MilDepthMap[e3dDispReference], MilDepthMapsColors,
                                                              2, DISP_3D_SIZE_X, DISP_3D_SIZE_Y, M_DEFAULT, M_NULL);
   
   // Setup the display and show it.
   MdispD3DControl(Mil3dDisplayHandle, MD3D_LOOK_AT_X, DISP_3D_LOOK_AT_X);
   MdispD3DControl(Mil3dDisplayHandle, MD3D_LOOK_AT_Y, DISP_3D_LOOK_AT_Y);
   MdispD3DControl(Mil3dDisplayHandle, MD3D_LOOK_AT_Z, DISP_3D_LOOK_AT_Z);
   MdispD3DControl(Mil3dDisplayHandle, MD3D_EYE_DIST, DISP_3D_EYE_DIST);
   MdispD3DControl(Mil3dDisplayHandle, MD3D_EYE_THETA, DISP_3D_EYE_THETA);
   MdispD3DControl(Mil3dDisplayHandle, MD3D_EYE_PHI, DISP_3D_EYE_PHI);
   MdispD3DControl(Mil3dDisplayHandle, MD3D_POINT, MD3D_ENABLE);
   MdispD3DControl(Mil3dDisplayHandle, MD3D_ROTATE, MD3D_TRUE);
   MdispD3DShow(Mil3dDisplayHandle);

   MosPrintf(MIL_TEXT("The aligned point clouds, as contained in the resulting stitched point cloud,\n")
             MIL_TEXT("are displayed in the 3d display:\n")
             MIL_TEXT("   - The depth map of the reference point cloud is in blue.\n")
             MIL_TEXT("   - The depth map of the aligned target point cloud is in red.\n\n")
             MIL_TEXT("Press <Enter> to end.\n\n"));
   MosGetch();

   MbufFree(MilDepthMapsColors[0]);
   MbufFree(MilDepthMapsColors[1]);
   MdispD3DFree(Mil3dDisplayHandle);
#endif

   //--------------------------------------------------------------------------
   // Free MIL objects.
   M3dmapFree(MilAlignmentContext);
   M3dmapFree(MilAlignmentResult);
   
   for (MIL_INT d = 0; d < NB_DISPLAY; d++)
      MdispFree(MilDisplay[d]);

   for (MIL_INT d = 0; d < eNbDepthMap; d++)
      MbufFree(MilDepthMap[d]);

   for (MIL_INT p = 0; p < NB_POINT_CLOUD; p++)
      M3dmapFree(MilPointCloud[p]);

   MbufFree(MilLutLegend);
   MbufFree(MilLut);
   MsysFree(MilSystem);
   MappFree(MilApplication);

   return 0;
   }
   
//*****************************************************************************
// Set LUT value associated to M_INVALID_POINT to a gray level value.
//*****************************************************************************
void AddLutColorForInvalidDepth(MIL_ID MilLut, MIL_UINT8 InvalidDepthGray)
   {
   // Create LUT.
   MgenLutFunction(MilLut, M_COLORMAP_JET, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT);

   // Replace the last value with a special color for invalid points.
   MIL_UINT8 ColorForInvalidDepth[3] = {InvalidDepthGray, InvalidDepthGray, InvalidDepthGray};
   MbufPutColor2d(MilLut, M_PLANAR, M_ALL_BANDS, DEPTHMAP_INVALID_VALUE, 0, 1, 1, ColorForInvalidDepth);
   }

//*****************************************************************************
// Draw a black 3d box in model and object depth maps to illustrate the overlap.
//*****************************************************************************
void DrawExpectedOverlap3dBoxes(MIL_ID MilSystem,
                                MIL_ID MilDepthMap[], 
                                MIL_ID MilDisplay[])
   {
   // Draw the expected overlap in the reference depth map.
   MIL_DOUBLE OverlapBoxPtsInAbsX[NUM_BOX_POINTS];
   MIL_DOUBLE OverlapBoxPtsInAbsY[NUM_BOX_POINTS];
   MIL_DOUBLE OverlapBoxPtsInAbsZ[NUM_BOX_POINTS];
   GetBoxPoints(MilDepthMap[eReference], OverlapBoxPtsInAbsX, OverlapBoxPtsInAbsY, OverlapBoxPtsInAbsZ);
   DrawBoxInOneDepthMap(MilSystem, 
                        MilDisplay[eReference],
                        MilDepthMap[eReference],
                        OverlapBoxPtsInAbsX,
                        OverlapBoxPtsInAbsY,
                        OverlapBoxPtsInAbsZ,
                        M_COLOR_BLACK);

   MdispControl(MilDisplay[eReference], M_UPDATE, M_NOW);

   // Draw the expected overlap in the target depth map.
   GetBoxPoints(MilDepthMap[ePrealigned], OverlapBoxPtsInAbsX, OverlapBoxPtsInAbsY, OverlapBoxPtsInAbsZ);
   DrawBoxInOneDepthMap(MilSystem, 
                        MilDisplay[eTarget],
                        MilDepthMap[eTarget],
                        OverlapBoxPtsInAbsX,
                        OverlapBoxPtsInAbsY,
                        OverlapBoxPtsInAbsZ,
                        M_COLOR_MAGENTA);

   MdispControl(MilDisplay[eTarget], M_UPDATE, M_NOW);
   }

//*****************************************************************************
// Draw a black 3d box in the stitched depth map to illustrate the overlap.
//*****************************************************************************
void DrawResultOverlap3dBoxes(MIL_ID MilSystem,
                              MIL_ID MilDepthMap[], 
                              MIL_ID MilDisplay[], 
                              MIL_ID MilPointCloud[])
   {
   MIL_DOUBLE OverlapBoxPtsInAbsX[NUM_BOX_POINTS];
   MIL_DOUBLE OverlapBoxPtsInAbsY[NUM_BOX_POINTS];
   MIL_DOUBLE OverlapBoxPtsInAbsZ[NUM_BOX_POINTS];
   
   // Draw the expected overlap in the target depth map.
   GetBoxPoints(MilDepthMap[ePrealigned], OverlapBoxPtsInAbsX, OverlapBoxPtsInAbsY, OverlapBoxPtsInAbsZ);
   M3dmapExtract(MilPointCloud[eTarget], MilDepthMap[eTarget], M_NULL, M_CORRECTED_DEPTH_MAP, M_ALL, M_DEFAULT);
   McalTransformCoordinate3dList(MilDepthMap[eTarget],
                                 M_ABSOLUTE_COORDINATE_SYSTEM,
                                 M_RELATIVE_COORDINATE_SYSTEM,
                                 24,
                                 OverlapBoxPtsInAbsX, OverlapBoxPtsInAbsY, OverlapBoxPtsInAbsZ,
                                 OverlapBoxPtsInAbsX, OverlapBoxPtsInAbsY, OverlapBoxPtsInAbsZ,
                                 M_DEFAULT);
   McalTransformCoordinate3dList(MilDepthMap[eStitched],
                                 M_RELATIVE_COORDINATE_SYSTEM,
                                 M_ABSOLUTE_COORDINATE_SYSTEM,
                                 24,
                                 OverlapBoxPtsInAbsX, OverlapBoxPtsInAbsY, OverlapBoxPtsInAbsZ,
                                 OverlapBoxPtsInAbsX, OverlapBoxPtsInAbsY, OverlapBoxPtsInAbsZ,
                                 M_DEFAULT);
   DrawBoxInOneDepthMap(MilSystem, 
                        MilDisplay[eStitched],
                        MilDepthMap[eStitched],
                        OverlapBoxPtsInAbsX,
                        OverlapBoxPtsInAbsY,
                        OverlapBoxPtsInAbsZ,
                        M_COLOR_MAGENTA);

   MdispControl(MilDisplay[eStitched], M_UPDATE, M_NOW);
   }


//*****************************************************************************
// Get list of 3d points, expressed in absolute coordinates, corresponding to 
// the start and end points of line segments of a 3d cube.
//*****************************************************************************
void GetBoxPoints(MIL_ID MilDepthMap, MIL_DOUBLE BoxPtsInAbsX[], MIL_DOUBLE BoxPtsInAbsY[], MIL_DOUBLE BoxPtsInAbsZ[])
   {
   // Define box lines (from points) expressed in the relative coordinate system.
   MIL_DOUBLE BoxPtsInRelX[NUM_BOX_POINTS] =
      {
       DRAW_BOX_MIN_X, DRAW_BOX_MAX_X, DRAW_BOX_MAX_X, DRAW_BOX_MIN_X,
       DRAW_BOX_MIN_X, DRAW_BOX_MAX_X, DRAW_BOX_MAX_X, DRAW_BOX_MIN_X,
       DRAW_BOX_MIN_X, DRAW_BOX_MIN_X, DRAW_BOX_MAX_X, DRAW_BOX_MAX_X,
       DRAW_BOX_MAX_X, DRAW_BOX_MAX_X, DRAW_BOX_MIN_X, DRAW_BOX_MIN_X,
       DRAW_BOX_MAX_X, DRAW_BOX_MAX_X, DRAW_BOX_MIN_X, DRAW_BOX_MIN_X,
       DRAW_BOX_MIN_X, DRAW_BOX_MIN_X, DRAW_BOX_MAX_X, DRAW_BOX_MAX_X
      };
   MIL_DOUBLE BoxPtsInRelY[NUM_BOX_POINTS] = 
      {
      DRAW_BOX_MIN_Y, DRAW_BOX_MIN_Y, DRAW_BOX_MAX_Y, DRAW_BOX_MAX_Y,
      DRAW_BOX_MIN_Y, DRAW_BOX_MIN_Y, DRAW_BOX_MAX_Y, DRAW_BOX_MAX_Y,
      DRAW_BOX_MIN_Y, DRAW_BOX_MAX_Y, DRAW_BOX_MIN_Y, DRAW_BOX_MAX_Y, 
      DRAW_BOX_MIN_Y, DRAW_BOX_MAX_Y, DRAW_BOX_MAX_Y, DRAW_BOX_MIN_Y,
      DRAW_BOX_MIN_Y, DRAW_BOX_MAX_Y, DRAW_BOX_MAX_Y, DRAW_BOX_MIN_Y,
      DRAW_BOX_MIN_Y, DRAW_BOX_MAX_Y, DRAW_BOX_MIN_Y, DRAW_BOX_MAX_Y
      };
   MIL_DOUBLE BoxPtsInRelZ[NUM_BOX_POINTS] = 
      {
      DRAW_BOX_MAX_Z, DRAW_BOX_MAX_Z, DRAW_BOX_MAX_Z, DRAW_BOX_MAX_Z,
      DRAW_BOX_MIN_Z, DRAW_BOX_MIN_Z, DRAW_BOX_MIN_Z, DRAW_BOX_MIN_Z,
      DRAW_BOX_MIN_Z, DRAW_BOX_MIN_Z, DRAW_BOX_MIN_Z, DRAW_BOX_MIN_Z,
      DRAW_BOX_MAX_Z, DRAW_BOX_MAX_Z, DRAW_BOX_MAX_Z, DRAW_BOX_MAX_Z,
      DRAW_BOX_MIN_Z, DRAW_BOX_MIN_Z, DRAW_BOX_MIN_Z, DRAW_BOX_MIN_Z,
      DRAW_BOX_MAX_Z, DRAW_BOX_MAX_Z, DRAW_BOX_MAX_Z, DRAW_BOX_MAX_Z
      };
   
   // Convert 3d cube box points into absolute coordinates.
   McalTransformCoordinate3dList(MilDepthMap, 
                                 M_RELATIVE_COORDINATE_SYSTEM, M_ABSOLUTE_COORDINATE_SYSTEM, 
                                 NUM_BOX_POINTS,
                                 BoxPtsInRelX, BoxPtsInRelY, BoxPtsInRelZ,
                                 BoxPtsInAbsX, BoxPtsInAbsY, BoxPtsInAbsZ, 
                                 M_DEFAULT);
   }

//*****************************************************************************
// Draw 3d cube line segments defined by 3d points in the displayed depth map.
//*****************************************************************************
void DrawBoxInOneDepthMap(MIL_ID     MilSystem, 
                          MIL_ID     MilDisplay, 
                          MIL_ID     MilDepthMap, 
                          MIL_DOUBLE BoxPtsInAbsX[],
                          MIL_DOUBLE BoxPtsInAbsY[],
                          MIL_DOUBLE BoxPtsInAbsZ[],
                          MIL_INT    BoxColor)
   {
   MIL_INT NbLines = NUM_BOX_POINTS/2;

   MIL_DOUBLE BoxPixelsInFixtureX[NUM_BOX_POINTS];
   MIL_DOUBLE BoxPixelsInFixtureY[NUM_BOX_POINTS];

   MIL_DOUBLE* BoxPixelStartX = BoxPixelsInFixtureX;
   MIL_DOUBLE* BoxPixelStartY = BoxPixelsInFixtureY;
   MIL_DOUBLE* BoxPixelEndX   = BoxPixelsInFixtureX + NbLines;
   MIL_DOUBLE* BoxPixelEndY   = BoxPixelsInFixtureY + NbLines;

   // Convert box points from absolute to pixel coordinates in the depth map.
   McalTransformCoordinate3dList(MilDepthMap, 
                                 M_ABSOLUTE_COORDINATE_SYSTEM, M_PIXEL_COORDINATE_SYSTEM, 
                                 NUM_BOX_POINTS,
                                 BoxPtsInAbsX       , BoxPtsInAbsY       , BoxPtsInAbsZ,
                                 BoxPixelsInFixtureX, BoxPixelsInFixtureY, M_NULL      , 
                                 M_DEPTH_MAP);

   // Create a buffer in which the box to draw as a depth map overlay will be defined.
   MIL_ID BoxOverlay;
   MbufAlloc2d(MilSystem, DEPTH_MAP_SIZE_X, DEPTH_MAP_SIZE_Y, 1+M_UNSIGNED, M_IMAGE+M_PROC, &BoxOverlay);

   // Draw the back square of the box first (white on black).
   MbufClear(BoxOverlay, 0.0);
   MgraColor(M_DEFAULT, 1.0);
   MgraLines(M_DEFAULT, BoxOverlay, 4, BoxPixelStartX, BoxPixelStartY, BoxPixelEndX, BoxPixelEndY, M_DEFAULT);

   // Erase (set to black) line segments overlapping valid depths of the depth map.
   MbufClearCond(BoxOverlay, 0.0, M_NULL, M_NULL, MilDepthMap, M_NOT_EQUAL, (MIL_DOUBLE)DEPTHMAP_INVALID_VALUE);

   // Draw the rest of the box.
   MgraLines(M_DEFAULT, BoxOverlay, NbLines - 4,
             &BoxPixelStartX[4], &BoxPixelStartY[4], &BoxPixelEndX[4], &BoxPixelEndY[4], M_DEFAULT);
   
   // Draw, in the depth map overlay of the display, the box created.
   MdispControl(MilDisplay, M_OVERLAY_CLEAR, M_DEFAULT);
   MIL_ID MilOverlay = MdispInquire(MilDisplay, M_OVERLAY_ID, M_NULL);
   MbufClearCond(MilOverlay, M_RGB888_R(BoxColor), M_RGB888_G(BoxColor), M_RGB888_B(BoxColor), BoxOverlay, M_EQUAL, 1.0);

   MbufFree(BoxOverlay);
   }

//*****************************************************************************
// Allocate and initialize a buffer used as a color LUT legend.
//*****************************************************************************
MIL_ID CreateLutLegend(MIL_ID MilSystem)
   {
   // Allocate the color LUT legend as a buffer.
   MIL_ID MilLutLegend;
   MbufAlloc2d(MilSystem, 1, NUM_DEPTHMAP_VALUES, 16 + M_UNSIGNED, M_IMAGE + M_PROC + M_DISP, &MilLutLegend);

   // Write a linear ramp in an array.
   MIL_UINT16* pLUTLegend = new MIL_UINT16[NUM_DEPTHMAP_VALUES];
   for (MIL_INT i = 0; i < NUM_DEPTHMAP_VALUES; i++)
      pLUTLegend[i] = static_cast<MIL_UINT16>(NUM_DEPTHMAP_VALUES - i);

   // Put linear ramp in the legend buffer.
   MbufPut(MilLutLegend, pLUTLegend);

   delete[] pLUTLegend;

   return MilLutLegend;
   }

//*****************************************************************************
// Draw the color LUT legend in the image.
//*****************************************************************************
void DrawLutLegend(MIL_ID DstImage, MIL_ID MilLutLegend)
   {
   // Allocate a child buffer to draw the legend.
   MIL_INT LegendSizeX = (DEPTH_MAP_SIZE_X) / 8;               // In pixels.
   MIL_INT LegendSizeY = (DEPTH_MAP_SIZE_Y * 3) / 4;           // In pixels.
   MIL_INT LegendOffX  =  DEPTH_MAP_SIZE_X - LegendSizeX - 10; // In pixels.
   MIL_INT LegendOffY  = (DEPTH_MAP_SIZE_Y - LegendSizeY) / 2; // In pixels.
   MIL_ID  ChildForLegend = MbufChild2d(DstImage, LegendOffX, LegendOffY, LegendSizeX, LegendSizeY, M_NULL);

   // Draw LUT legend
   MimResize(MilLutLegend, ChildForLegend, M_FILL_DESTINATION, M_FILL_DESTINATION, M_BILINEAR);

   // Write depth values for color at lowest, middle and highest depths
   MIL_DOUBLE GrayLevelSizeZ;
   McalInquire(DstImage, M_GRAY_LEVEL_SIZE_Z, &GrayLevelSizeZ);
   MIL_DOUBLE GrayScaleWorldBoxHeight = fabs(GrayLevelSizeZ) * (NUM_DEPTHMAP_VALUES-1);

   MIL_DOUBLE LegendLowZ = 0.0;                                            // In world units.
   MIL_DOUBLE LegendMidZ = LegendLowZ + (GrayScaleWorldBoxHeight * 0.5);   // In world units.
   MIL_DOUBLE LegendHiZ  = LegendLowZ +  GrayScaleWorldBoxHeight;          // In world units.

   MIL_TEXT_CHAR mmTxt[256];
   MgraControl(M_DEFAULT, M_TEXT_ALIGN_HORIZONTAL, M_CENTER);
   MgraColor(M_DEFAULT, 32767);

   MosSprintf(mmTxt, 256, MIL_TEXT("%.0f mm"), LegendHiZ);
   MgraText(M_DEFAULT, ChildForLegend, LegendSizeX / 2, 5, mmTxt);

   MosSprintf(mmTxt, 256, MIL_TEXT("%.0f mm"), LegendMidZ);
   MgraText(M_DEFAULT, ChildForLegend, LegendSizeX / 2, LegendSizeY / 2, mmTxt);

   MosSprintf(mmTxt, 256, MIL_TEXT("%.0f mm"), LegendLowZ);
   MgraText(M_DEFAULT, ChildForLegend, LegendSizeX / 2, LegendSizeY - 20, mmTxt);

   // Free legend child buffer
   MbufFree(ChildForLegend); 
   }

//*****************************************************************************
// Generates the point clouds for the example.
//*****************************************************************************
void GenerateUnorganizedPointClouds(MIL_ID MilPointCloud[])
   {
   MIL_ID MilSystem = M3dmapInquire(MilPointCloud[eReference], M_GENERAL, M_OWNER_SYSTEM, M_NULL);

   MIL_ID MilSourcePointCloud[2];
   M3dmapAllocResult(MilSystem, M_POINT_CLOUD_CONTAINER, M_DEFAULT, &MilSourcePointCloud[0]);
   M3dmapAllocResult(MilSystem, M_POINT_CLOUD_CONTAINER, M_DEFAULT, &MilSourcePointCloud[1]);

   // Import a point cloud from a PLY file.
   M3dmapImport(FILE_SOURCE_POINT_CLOUD[0], M_PLY, MilSourcePointCloud[eReference],
                M_POINT_CLOUD_LABEL(1), M_NULL, M_DEFAULT);
   M3dmapImport(FILE_SOURCE_POINT_CLOUD[1], M_PLY, MilSourcePointCloud[eTarget],
                M_POINT_CLOUD_LABEL(2), M_NULL, M_DEFAULT);
   
   // Crop the two point clouds.
   const MIL_DOUBLE BoxCropRatio = 0.5 + BOX_OVERLAP / 2.0;
   for(MIL_INT p = 0; p < 2; p++)
      {
      MIL_DOUBLE CropExtractionBoxMinY = -EXTRACTION_BOX_SIZE_Y/2;
      if(p == 1)
         CropExtractionBoxMinY += EXP_TY + EXTRACTION_BOX_SIZE_Y * (BoxCropRatio - BOX_OVERLAP);
 
      M3dmapSetBox(MilSourcePointCloud[p], M_DEFAULT, M_CORNER_AND_DIMENSION,
                   -EXTRACTION_BOX_SIZE_X/2, CropExtractionBoxMinY, -EXTRACTION_BOX_SIZE_Z/2,
                   EXTRACTION_BOX_SIZE_X, EXTRACTION_BOX_SIZE_Y* BoxCropRatio, EXTRACTION_BOX_SIZE_Z);

      // Get the points.
      MIL_INT NbPoints;
      M3dmapGet(MilSourcePointCloud[p], M_ALL, M_POSITION, M_INCLUDE_POINTS_INSIDE_BOX_ONLY,
                64 + M_FLOAT, 0, NULL, NULL, NULL, &NbPoints);
      MIL_DOUBLE* X = new MIL_DOUBLE[NbPoints];
      MIL_DOUBLE* Y = new MIL_DOUBLE[NbPoints];
      MIL_DOUBLE* Z = new MIL_DOUBLE[NbPoints];
      M3dmapGet(MilSourcePointCloud[p], M_ALL, M_POSITION, M_INCLUDE_POINTS_INSIDE_BOX_ONLY,
                64 + M_FLOAT, NbPoints, X, Y, Z, M_NULL);

      // Put the point.
      M3dmapPut(MilPointCloud[p], M_POINT_CLOUD_LABEL(1), M_POSITION,
                64 + M_FLOAT, NbPoints, X, Y, Z, MilSourcePointCloud[p], M_DEFAULT);

      delete [] X;
      delete [] Y;
      delete [] Z;
      }

   M3dmapFree(MilSourcePointCloud[eTarget]);
   M3dmapFree(MilSourcePointCloud[eReference]);
   }

//****************************************************************************
// Check that the given file is present.
//****************************************************************************
bool CheckForRequiredMILFile(MIL_CONST_TEXT_PTR FileName)
   {
   MIL_INT FilePresent;

   MappFileOperation(M_DEFAULT, FileName, M_NULL, M_NULL, M_FILE_EXISTS, M_DEFAULT, &FilePresent);
   if (FilePresent == M_NO)
      {
      MosPrintf(MIL_TEXT("\n")
                MIL_TEXT("The footage needed to run this example is missing. You need \n")
                MIL_TEXT("to obtain and apply a separate specific update to have it.\n\n"));

      MosPrintf(MIL_TEXT("Press <Enter> to end.\n\n"));
      MosGetch();
      }

   return (FilePresent == M_YES);
   }