' *****************************************************************************
'
' File name: M3dmap.cs
' Location: See Matrox Example Launcher in the MIL Control Center
' 
'
' Synopsis: This program inspects a wood surface Using
'           sheet-of-light profiling (laser) to find any depth defects.
'
' Printable calibration grids in PDF format can be found in your
' "Matrox Imaging/Images/" directory.
'
' When considering a laser-based 3D reconstruction system, the file "3D Setup Helper.xls"
' can be used to accelerate prototyping by choosing an adequate hardware configuration
' (angle, distance, lens, camera, ...). The file Is located in your
' "Matrox Imaging/Tools/" directory.
'
' Copyright (C) Matrox Electronic Systems Ltd., 1992-2020.
' All Rights Reserved
' *****************************************************************************
Imports System
Imports System.Text
Imports Matrox.MatroxImagingLibrary

Namespace M3dmap
    Friend Class Program

        ' *****************************************************************************
        ' Main.
        ' *****************************************************************************
        Shared Sub Main(ByVal args() As String)
            Dim MilApplication As MIL_ID = MIL.M_NULL ' Application identifier.
            Dim MilSystem As MIL_ID = MIL.M_NULL      ' System identifier.
            Dim MilDisplay As MIL_ID = MIL.M_NULL     ' Display identifier.

            MIL.MappAllocDefault(MIL.M_DEFAULT, MilApplication, MilSystem, MilDisplay, CType(MIL.M_NULL, IntPtr), CType(MIL.M_NULL, IntPtr))
            DepthCorrectionExample(MilSystem, MilDisplay)
            CalibratedCameraExample(MilSystem, MilDisplay)

            MIL.MappFreeDefault(MilApplication, MilSystem, MilDisplay, MIL.M_NULL, MIL.M_NULL)
        End Sub

        ' *****************************************************************************
        ' Depth correction example.
        ' *****************************************************************************

        ' Input sequence specifications.
        Private Const REFERENCE_PLANES_SEQUENCE_FILE As String = MIL.M_IMAGE_PATH & "ReferencePlanes.avi"
        Private Const OBJECT_SEQUENCE_FILE As String = MIL.M_IMAGE_PATH & "ScannedObject.avi"

        ' Peak detection parameters.
        Private Const PEAK_WIDTH_NOMINAL As Integer = 10
        Private Const PEAK_WIDTH_DELTA As Integer = 8
        Private Const MIN_CONTRAST As Integer = 140

        ' Calibration heights in mm.
        Private Shared ReadOnly CORRECTED_DEPTHS As Double() = {1.25, 2.5, 3.75, 5.0}

        Private Const SCALE_FACTOR As Double = 10000.0 ' (depth in world units) * SCALE_FACTOR gives gray levels

        ' Annotation position.
        Private Const CALIB_TEXT_POS_X As Integer = 400
        Private Const CALIB_TEXT_POS_Y As Integer = 15

        Private Shared Sub DepthCorrectionExample(ByVal MilSystem As MIL_ID, ByVal MilDisplay As MIL_ID)
            Dim MilOverlayImage As MIL_ID = MIL.M_NULL        ' Overlay image buffer identifier.
            Dim MilImage As MIL_ID = MIL.M_NULL               ' Image buffer identifier (for processing).
            Dim MilDepthMap As MIL_ID = MIL.M_NULL            ' Image buffer identifier (for results).
            Dim MilLaser As MIL_ID = MIL.M_NULL               ' 3dmap laser profiling context identifier.
            Dim MilCalibScan As MIL_ID = MIL.M_NULL           ' 3dmap result buffer identifier for laser line calibration.
            Dim MilScan As MIL_ID = MIL.M_NULL                ' 3dmap result buffer identifier.

            Dim SizeX As MIL_INT = 0                          ' Width of grabbed images.
            Dim SizeY As MIL_INT = 0                          ' Height of grabbed images.
            Dim NbReferencePlanes As MIL_INT = 0              ' Number of reference planes of known heights.
            Dim NbObjectImages As MIL_INT = 0                 ' Number of frames for scanned objects.
            Dim n As Integer = 0                              ' Counter.
            Dim FrameRate As Double = 0.0                     ' Number of grabbed frames per second (in AVI).
            Dim StartTime As Double = 0.0                     ' Time at the beginning of each iteration.
            Dim EndTime As Double = 0.0                       ' Time after processing for each iteration.
            Dim WaitTime As Double = 0.0                      ' Time to wait for next frame.

            ' Inquire characteristics of the input sequences.
            MIL.MbufDiskInquire(REFERENCE_PLANES_SEQUENCE_FILE, MIL.M_SIZE_X, SizeX)
            MIL.MbufDiskInquire(REFERENCE_PLANES_SEQUENCE_FILE, MIL.M_SIZE_Y, SizeY)
            MIL.MbufDiskInquire(REFERENCE_PLANES_SEQUENCE_FILE, MIL.M_NUMBER_OF_IMAGES, NbReferencePlanes)
            MIL.MbufDiskInquire(REFERENCE_PLANES_SEQUENCE_FILE, MIL.M_FRAME_RATE, FrameRate)
            MIL.MbufDiskInquire(OBJECT_SEQUENCE_FILE, MIL.M_NUMBER_OF_IMAGES, NbObjectImages)

            ' Allocate buffer to hold images.
            MIL.MbufAlloc2d(MilSystem, SizeX, SizeY, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_DISP + MIL.M_PROC, MilImage)
            MIL.MbufClear(MilImage, 0.0)

            Console.WriteLine()
            Console.WriteLine("DEPTH ANALYSIS:")
            Console.WriteLine("---------------")
            Console.WriteLine()
            Console.WriteLine("This program performs a surface inspection to detect depth defects ")
            Console.WriteLine("on a wood surface using a laser (sheet-of-light) profiling system.")
            Console.WriteLine()
            Console.WriteLine("Press <Enter> to continue.")
            Console.WriteLine()
            Console.ReadKey()

            ' Select display.
            MIL.MdispSelect(MilDisplay, MilImage)

            ' Prepare for overlay annotations.
            MIL.MdispControl(MilDisplay, MIL.M_OVERLAY, MIL.M_ENABLE)
            MIL.MdispInquire(MilDisplay, MIL.M_OVERLAY_ID, MilOverlayImage)
            MIL.MgraControl(MIL.M_DEFAULT, MIL.M_BACKGROUND_MODE, MIL.M_TRANSPARENT)
            MIL.MgraColor(MIL.M_DEFAULT, MIL.M_COLOR_WHITE)

            ' Allocate 3dmap objects.
            MIL.M3dmapAlloc(MilSystem, MIL.M_LASER, MIL.M_DEPTH_CORRECTION, MilLaser)
            MIL.M3dmapAllocResult(MilSystem, MIL.M_LASER_CALIBRATION_DATA, MIL.M_DEFAULT, MilCalibScan)

            ' Set laser line extraction options.
            Dim MilPeakLocator As MIL_ID = MIL.M_NULL
            MIL.M3dmapInquire(MilLaser, MIL.M_DEFAULT, MIL.M_LOCATE_PEAK_1D_CONTEXT_ID + MIL.M_TYPE_MIL_ID, MilPeakLocator)
            MIL.MimControl(MilPeakLocator, MIL.M_PEAK_WIDTH_NOMINAL, PEAK_WIDTH_NOMINAL)
            MIL.MimControl(MilPeakLocator, MIL.M_PEAK_WIDTH_DELTA, PEAK_WIDTH_DELTA)
            MIL.MimControl(MilPeakLocator, MIL.M_MINIMUM_CONTRAST, MIN_CONTRAST)

            ' Open the calibration sequence file for reading.
            MIL.MbufImportSequence(REFERENCE_PLANES_SEQUENCE_FILE, MIL.M_DEFAULT, MIL.M_NULL, MIL.M_NULL, CType(MIL.M_NULL, IntPtr), MIL.M_NULL, MIL.M_NULL, MIL.M_OPEN)

            ' Read and process all images in the input sequence.
            MIL.MappTimer(MIL.M_DEFAULT, MIL.M_TIMER_READ + MIL.M_SYNCHRONOUS, StartTime)

            For n = 0 To CInt(NbReferencePlanes) - 1
                Dim CalibString As String

                MIL.MbufImportSequence(REFERENCE_PLANES_SEQUENCE_FILE, MIL.M_DEFAULT, MIL.M_LOAD, MIL.M_NULL, MilImage, MIL.M_DEFAULT, 1, MIL.M_READ)

                ' Annotate the image with the calibration height.
                MIL.MdispControl(MilDisplay, MIL.M_OVERLAY_CLEAR, MIL.M_DEFAULT)
                CalibString = String.Format("Reference plane {0}: {1,0:f2} mm", (n + 1), CORRECTED_DEPTHS(n))
                MIL.MgraText(MIL.M_DEFAULT, MilOverlayImage, CALIB_TEXT_POS_X, CALIB_TEXT_POS_Y, CalibString)

                ' Set desired corrected depth of next reference plane.
                MIL.M3dmapControl(MilLaser, MIL.M_DEFAULT, MIL.M_CORRECTED_DEPTH, CORRECTED_DEPTHS(n) * SCALE_FACTOR)

                ' Analyze the image to extract laser line.
                MIL.M3dmapAddScan(MilLaser, MilCalibScan, MilImage, MIL.M_NULL, MIL.M_NULL, MIL.M_DEFAULT, MIL.M_DEFAULT)

                ' Wait to have a proper frame rate, if necessary.
                MIL.MappTimer(MIL.M_DEFAULT, MIL.M_TIMER_READ + MIL.M_SYNCHRONOUS, EndTime)
                WaitTime = 1.0 / FrameRate - (EndTime - StartTime)
                If WaitTime > 0 Then
                    MIL.MappTimer(MIL.M_DEFAULT, MIL.M_TIMER_WAIT, WaitTime)
                End If
                MIL.MappTimer(MIL.M_DEFAULT, MIL.M_TIMER_READ + MIL.M_SYNCHRONOUS, StartTime)
            Next

            ' Close the calibration sequence file.
            MIL.MbufImportSequence(REFERENCE_PLANES_SEQUENCE_FILE, MIL.M_DEFAULT, MIL.M_NULL, MIL.M_NULL, CType(MIL.M_NULL, IntPtr), MIL.M_NULL, MIL.M_NULL, MIL.M_CLOSE)

            ' Calibrate the laser profiling context using reference planes of known heights.
            MIL.M3dmapCalibrate(MilLaser, MilCalibScan, MIL.M_NULL, MIL.M_DEFAULT)

            Console.WriteLine("The laser profiling system has been calibrated using 4 reference")
            Console.WriteLine("planes of known heights.")
            Console.WriteLine()
            Console.WriteLine("Press <Enter> to continue.")
            Console.WriteLine()
            Console.ReadKey()

            Console.WriteLine("The wood surface is being scanned.")
            Console.WriteLine()

            ' Free the result buffer used for calibration because it will not be used anymore.
            MIL.M3dmapFree(MilCalibScan)
            MilCalibScan = MIL.M_NULL

            ' Allocate the result buffer for the scanned depth corrected data.
            MIL.M3dmapAllocResult(MilSystem, MIL.M_DEPTH_CORRECTED_DATA, MIL.M_DEFAULT, MilScan)

            ' Open the object sequence file for reading.
            MIL.MbufDiskInquire(OBJECT_SEQUENCE_FILE, MIL.M_FRAME_RATE, FrameRate)
            MIL.MbufImportSequence(OBJECT_SEQUENCE_FILE, MIL.M_DEFAULT, MIL.M_NULL, MIL.M_NULL, CType(MIL.M_NULL, IntPtr), MIL.M_NULL, MIL.M_NULL, MIL.M_OPEN)

            ' Read and process all images in the input sequence.
            MIL.MappTimer(MIL.M_DEFAULT, MIL.M_TIMER_READ + MIL.M_SYNCHRONOUS, StartTime)
            MIL.MdispControl(MilDisplay, MIL.M_OVERLAY_CLEAR, MIL.M_DEFAULT)

            For n = 0 To CInt(NbObjectImages - 1)
                ' Read image from sequence.
                MIL.MbufImportSequence(OBJECT_SEQUENCE_FILE, MIL.M_DEFAULT, MIL.M_LOAD, MIL.M_NULL, MilImage, MIL.M_DEFAULT, 1, MIL.M_READ)

                ' Analyze the image to extract laser line and correct its depth.
                MIL.M3dmapAddScan(MilLaser, MilScan, MilImage, MIL.M_NULL, MIL.M_NULL, MIL.M_DEFAULT, MIL.M_DEFAULT)

                ' Wait to have a proper frame rate, if necessary.
                MIL.MappTimer(MIL.M_DEFAULT, MIL.M_TIMER_READ + MIL.M_SYNCHRONOUS, EndTime)
                WaitTime = 1.0 / FrameRate - (EndTime - StartTime)
                If WaitTime > 0 Then
                    MIL.MappTimer(MIL.M_DEFAULT, MIL.M_TIMER_WAIT, WaitTime)
                End If
                MIL.MappTimer(MIL.M_DEFAULT, MIL.M_TIMER_READ + MIL.M_SYNCHRONOUS, StartTime)
            Next

            ' Close the object sequence file.
            MIL.MbufImportSequence(OBJECT_SEQUENCE_FILE, MIL.M_DEFAULT, MIL.M_NULL, MIL.M_NULL, CType(MIL.M_NULL, IntPtr), MIL.M_NULL, MIL.M_NULL, MIL.M_CLOSE)

            ' Allocate the image for a partially corrected depth map.
            MIL.MbufAlloc2d(MilSystem, SizeX, NbObjectImages, 16 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_PROC + MIL.M_DISP, MilDepthMap)

            ' Get partially corrected depth map from accumulated information in the result buffer.
            MIL.M3dmapCopyResult(MilScan, MIL.M_DEFAULT, MilDepthMap, MIL.M_PARTIALLY_CORRECTED_DEPTH_MAP, MIL.M_DEFAULT)

            ' Show partially corrected depth map and find defects.
            SetupColorDisplay(MilSystem, MilDisplay, MIL.MbufInquire(MilDepthMap, MIL.M_SIZE_BIT, MIL.M_NULL))

            ' Display partially corrected depth map.
            MIL.MdispSelect(MilDisplay, MilDepthMap)
            MIL.MdispInquire(MilDisplay, MIL.M_OVERLAY_ID, MilOverlayImage)

            Console.WriteLine("The pseudo-color depth map of the surface is displayed.")
            Console.WriteLine()
            Console.WriteLine("Press <Enter> to continue.")
            Console.WriteLine()
            Console.ReadKey()

            PerformBlobAnalysis(MilSystem, MilDisplay, MilOverlayImage, MilDepthMap)

            Console.WriteLine("Press <Enter> to continue.")
            Console.WriteLine()
            Console.ReadKey()

            ' Disassociates display LUT and clear overlay.
            MIL.MdispLut(MilDisplay, MIL.M_DEFAULT)
            MIL.MdispControl(MilDisplay, MIL.M_OVERLAY_CLEAR, MIL.M_DEFAULT)

            ' Free all allocations.
            MIL.M3dmapFree(MilScan)
            MIL.M3dmapFree(MilLaser)
            MIL.MbufFree(MilDepthMap)
            MIL.MbufFree(MilImage)
        End Sub

        ' Values used for binarization.
        Private Const EXPECTED_HEIGHT As Double = 3.4  ' Inspected surface should be at this height (In mm)
        Private Const DEFECT_THRESHOLD As Double = 0.2 ' Max acceptable deviation from expected height (mm)
        Private Const SATURATED_DEFECT As Double = 1.0 ' Deviation at which defect will appear red (In mm)

        ' Radius of the smallest particles to keep.
        Private Const MIN_BLOB_RADIUS As Integer = 3

        ' Pixel offset for drawing text.
        Private Const TEXT_H_OFFSET_1 As Integer = -50
        Private Const TEXT_V_OFFSET_1 As Integer = -6
        Private Const TEXT_H_OFFSET_2 As Integer = -30
        Private Const TEXT_V_OFFSET_2 As Integer = 6

        ' Find defects in corrected depth map, compute max deviation and draw contours.
        Private Shared Sub PerformBlobAnalysis(ByVal MilSystem As MIL_ID, ByVal MilDisplay As MIL_ID, ByVal MilOverlayImage As MIL_ID, ByVal MilDepthMap As MIL_ID)
            Dim MilBinImage As MIL_ID = MIL.M_NULL         ' Binary image buffer identifier.
            Dim MilBlobContext As MIL_ID = MIL.M_NULL      ' Blob context identifier.
            Dim MilBlobResult As MIL_ID = MIL.M_NULL       ' Blob result buffer identifier.
            Dim SizeX As MIL_INT = 0                       ' Width of depth map.
            Dim SizeY As MIL_INT = 0                       ' Height of depth map.
            Dim TotalBlobs As MIL_INT = 0                  ' Total number of blobs.
            Dim n As Integer = 0                           ' Counter.
            Dim MinPixels As MIL_INT()                     ' Maximum height of defects.
            Dim DefectThreshold As Double = 0.0            ' A gray level below it is a defect.
            Dim CogX As Double()                           ' X coordinate of center of gravity.
            Dim CogY As Double()                           ' Y coordinate of center of gravity.

            ' Get size of depth map.
            MIL.MbufInquire(MilDepthMap, MIL.M_SIZE_X, SizeX)
            MIL.MbufInquire(MilDepthMap, MIL.M_SIZE_Y, SizeY)

            ' Allocate a binary image buffer for fast processing.
            MIL.MbufAlloc2d(MilSystem, SizeX, SizeY, 1 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_PROC, MilBinImage)

            ' Binarize image.
            DefectThreshold = (EXPECTED_HEIGHT - DEFECT_THRESHOLD) * SCALE_FACTOR
            MIL.MimBinarize(MilDepthMap, MilBinImage, MIL.M_FIXED + MIL.M_LESS_OR_EQUAL, DefectThreshold, MIL.M_NULL)

            ' Remove small particles.
            MIL.MimOpen(MilBinImage, MilBinImage, MIN_BLOB_RADIUS, MIL.M_BINARY)

            ' Allocate a blob context.
            MIL.MblobAlloc(MilSystem, MIL.M_DEFAULT, MIL.M_DEFAULT, MilBlobContext)

            ' Enable the Center Of Gravity And Min Pixel features calculation.
            MIL.MblobControl(MilBlobContext, MIL.M_CENTER_OF_GRAVITY + MIL.M_GRAYSCALE, MIL.M_ENABLE)
            MIL.MblobControl(MilBlobContext, MIL.M_MIN_PIXEL, MIL.M_ENABLE)

            ' Allocate a blob result buffer.
            MIL.MblobAllocResult(MilSystem, MIL.M_DEFAULT, MIL.M_DEFAULT, MilBlobResult)

            ' Calculate selected features for each blob.
            MIL.MblobCalculate(MilBlobContext, MilBinImage, MilDepthMap, MilBlobResult)

            ' Get the total number of selected blobs.
            MIL.MblobGetResult(MilBlobResult, MIL.M_DEFAULT, MIL.M_NUMBER + MIL.M_TYPE_MIL_INT, TotalBlobs)
            Console.WriteLine("Number of defects: {0}", TotalBlobs)

            ' Read And print the blob characteristics.
            CogX = New Double(CInt(TotalBlobs - 1)) {}
            CogY = New Double(CInt(TotalBlobs - 1)) {}
            MinPixels = New MIL_INT(CInt(TotalBlobs - 1)) {}

            If CogX IsNot Nothing AndAlso CogY IsNot Nothing AndAlso MinPixels IsNot Nothing Then
                ' Get the results.
                MIL.MblobGetResult(MilBlobResult, MIL.M_DEFAULT, MIL.M_CENTER_OF_GRAVITY_X + MIL.M_GRAYSCALE, CogX)
                MIL.MblobGetResult(MilBlobResult, MIL.M_DEFAULT, MIL.M_CENTER_OF_GRAVITY_Y + MIL.M_GRAYSCALE, CogY)
                MIL.MblobGetResult(MilBlobResult, MIL.M_DEFAULT, MIL.M_MIN_PIXEL + MIL.M_TYPE_MIL_INT, MinPixels)

                ' Draw the defects.
                MIL.MgraColor(MIL.M_DEFAULT, MIL.M_COLOR_RED)
                MIL.MblobDraw(MIL.M_DEFAULT, MilBlobResult, MilOverlayImage, MIL.M_DRAW_BLOBS, MIL.M_INCLUDED_BLOBS, MIL.M_DEFAULT)
                MIL.MgraColor(MIL.M_DEFAULT, MIL.M_COLOR_WHITE)

                ' Print the depth of each blob.
                For n = 0 To CInt(TotalBlobs - 1)
                    Dim DepthOfDefect As Double
                    Dim DepthString As String

                    ' Write the depth of the defect in the overlay.
                    DepthOfDefect = EXPECTED_HEIGHT - CDbl(MinPixels(n)) / SCALE_FACTOR
                    DepthString = String.Format("{0,0:f2} mm", DepthOfDefect)

                    Console.WriteLine("Defect #{0}: depth ={1,5:f2} mm", n, DepthOfDefect)
                    Console.WriteLine()
                    MIL.MgraText(MIL.M_DEFAULT, MilOverlayImage, CogX(n) + TEXT_H_OFFSET_1, CogY(n) + TEXT_V_OFFSET_1, "Defect depth")
                    MIL.MgraText(MIL.M_DEFAULT, MilOverlayImage, CogX(n) + TEXT_H_OFFSET_2, CogY(n) + TEXT_V_OFFSET_2, DepthString.ToString)
                Next
            Else
                Console.WriteLine("Error: Not enough memory.")
                Console.WriteLine()
            End If

            ' Free all allocations.
            MIL.MblobFree(MilBlobResult)
            MIL.MblobFree(MilBlobContext)
            MIL.MbufFree(MilBinImage)
        End Sub

        ' Color constants for display LUT.
        Private Const BLUE_HUE As Double = 171.0 ' Expected depths will be blue.
        Private Const RED_HUE As Double = 0.0 ' Worst defects will be red.
        Private Const FULL_SATURATION As Integer = 255 ' All colors are fully saturated.
        Private Const HALF_LUMINANCE As Integer = 128 ' All colors have half luminance.

        ' Creates a color display LUT to show defects in red.
        Private Shared Sub SetupColorDisplay(ByVal MilSystem As MIL_ID, ByVal MilDisplay As MIL_ID, ByVal SizeBit As MIL_INT)
            Dim MilRampLut1Band As MIL_ID = MIL.M_NULL ' LUT containing hue values.
            Dim MilRampLut3Band As MIL_ID = MIL.M_NULL ' RGB LUT used by display.
            Dim MilColorImage As MIL_ID = MIL.M_NULL ' Image used For HSL To RGB conversion.
            Dim DefectGrayLevel As MIL_INT = 0 ' Gray level under which all Is red.
            Dim ExpectedGrayLevel As MIL_INT = 0 ' Gray level over which all Is blue.
            Dim NbGrayLevels As MIL_INT = 0

            ' Number of possible gray levels in corrected depth map.
            NbGrayLevels = CType(1 << CInt(SizeBit), MIL_INT)

            ' Allocate 1-band LUT that will contain hue values.
            MIL.MbufAlloc1d(MilSystem, NbGrayLevels, 8 + MIL.M_UNSIGNED, MIL.M_LUT, MilRampLut1Band)

            ' Compute limit gray values.
            DefectGrayLevel = CType((EXPECTED_HEIGHT - SATURATED_DEFECT) * SCALE_FACTOR, MIL_INT)
            ExpectedGrayLevel = CType(EXPECTED_HEIGHT * SCALE_FACTOR, MIL_INT)

            ' Create hue values for each possible gray level.
            MIL.MgenLutRamp(MilRampLut1Band, 0, RED_HUE, DefectGrayLevel, RED_HUE)
            MIL.MgenLutRamp(MilRampLut1Band, DefectGrayLevel, RED_HUE, ExpectedGrayLevel, BLUE_HUE)
            MIL.MgenLutRamp(MilRampLut1Band, ExpectedGrayLevel, BLUE_HUE, NbGrayLevels - 1, BLUE_HUE)

            ' Create a HSL image buffer.
            MIL.MbufAllocColor(MilSystem, 3, NbGrayLevels, 1, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE, MilColorImage)
            MIL.MbufClear(MilColorImage, MIL.M_RGB888(0, FULL_SATURATION, HALF_LUMINANCE))

            ' Set its H band (hue) to the LUT contents And convert the image to RGB.
            MIL.MbufCopyColor2d(MilRampLut1Band, MilColorImage, 0, 0, 0, 0, 0, 0, NbGrayLevels, 1)
            MIL.MimConvert(MilColorImage, MilColorImage, MIL.M_HSL_TO_RGB)

            ' Create RGB LUT to give to display And copy image contents.
            MIL.MbufAllocColor(MilSystem, 3, NbGrayLevels, 1, 8 + MIL.M_UNSIGNED, MIL.M_LUT, MilRampLut3Band)
            MIL.MbufCopy(MilColorImage, MilRampLut3Band)

            ' Associates LUT to display.
            MIL.MdispLut(MilDisplay, MilRampLut3Band)

            ' Free all allocations.
            MIL.MbufFree(MilRampLut1Band)
            MIL.MbufFree(MilRampLut3Band)
            MIL.MbufFree(MilColorImage)
        End Sub

        ' *****************************************************************************
        ' Calibrated camera example.
        ' *****************************************************************************

        ' Input sequence specifications.
        Private Const GRID_FILENAME As String = MIL.M_IMAGE_PATH & "GridForLaser.mim"
        Private Const LASERLINE_FILENAME As String = MIL.M_IMAGE_PATH & "LaserLine.mim"
        Private Const OBJECT2_SEQUENCE_FILE As String = MIL.M_IMAGE_PATH & "Cookie.avi"

        ' Camera calibration grid parameters.
        Private Const GRID_NB_ROWS As Integer = 13
        Private Const GRID_NB_COLS As Integer = 12
        Private Const GRID_ROW_SPACING As Double = 5.0 ' in mm
        Private Const GRID_COL_SPACING As Double = 5.0 ' in mm

        ' Laser device setup parameters.
        Private Const CONVEYOR_SPEED As Double = -0.2 ' in mm/frame

        ' Fully corrected depth map generation parameters.
        Private Const DEPTH_MAP_SIZE_X As Integer = 480 ' in pixels
        Private Const DEPTH_MAP_SIZE_Y As Integer = 480 ' in pixels
        Private Const GAP_DEPTH As Double = 1.5 ' in mm

        ' D3D display parameters
        Private Const D3D_DISPLAY_SIZE_X As Integer = 640
        Private Const D3D_DISPLAY_SIZE_Y As Integer = 480

        ' Peak detection parameters.
        Private Const PEAK_WIDTH_NOMINAL_2 As Integer = 9
        Private Const PEAK_WIDTH_DELTA_2 As Integer = 7
        Private Const MIN_CONTRAST_2 As Integer = 75

        ' Everything below this Is considered as noise.
        Private Const MIN_HEIGHT_THRESHOLD As Double = 1.0 ' in mm

        Private Shared Sub CalibratedCameraExample(ByVal MilSystem As MIL_ID, ByVal MilDisplay As MIL_ID)
            Dim MilOverlayImage As MIL_ID = MIL.M_NULL ' Overlay image buffer identifier.
            Dim MilImage As MIL_ID = MIL.M_NULL ' Image buffer identifier (For processing).
            Dim MilCalibration As MIL_ID = MIL.M_NULL ' Calibration context.
            Dim MilDepthMap As MIL_ID = MIL.M_NULL ' Image buffer identifier (For results).
            Dim MilLaser As MIL_ID = MIL.M_NULL ' 3Dmap laser profiling context identifier.
            Dim MilCalibScan As MIL_ID = MIL.M_NULL ' 3Dmap result buffer identifier For laser
            ' line calibration.
            Dim MilScan As MIL_ID = MIL.M_NULL ' 3map result buffer identifier.
            Dim MilContainerId As MIL_ID = MIL.M_NULL ' Point cloud container identifier.
            Dim FillGapsContext As MIL_ID = MIL.M_NULL ' Fill gaps context identifier.
            Dim CalibrationStatus As MIL_INT = 0 ' Used To ensure If MIL.McalGrid() worked.
            Dim SizeX As MIL_INT = 0 ' Width Of grabbed images.
            Dim SizeY As MIL_INT = 0 ' Height Of grabbed images.
            Dim NumberOfImages As MIL_INT = 0 ' Number Of frames For scanned objects.
            Dim n As MIL_INT = 0 ' Counter.
            Dim FrameRate As Double = 0.0                       ' Number Of grabbed frames per second (In AVI).
            Dim StartTime As Double = 0.0 ' Time at the beginning Of Each iteration.
            Dim EndTime As Double = 0.0 ' Time after processing For Each iteration.
            Dim WaitTime As Double = 0.0 ' Time To wait For Next frame.
            Dim Volume As Double = 0.0 ' Volume Of scanned Object.

            Console.WriteLine()
            Console.WriteLine("3D PROFILING AND VOLUME ANALYSIS:")
            Console.WriteLine("---------------------------------")
            Console.WriteLine()
            Console.WriteLine("This program generates fully corrected 3D data of a")
            Console.WriteLine("scanned cookie and computes its volume.")
            Console.WriteLine("The laser (sheet-of-light) profiling system uses a")
            Console.WriteLine("3d-calibrated camera.")
            Console.WriteLine()

            ' Load grid image for camera calibration.
            MIL.MbufRestore(GRID_FILENAME, MilSystem, MilImage)

            ' Select display.
            MIL.MdispSelect(MilDisplay, MilImage)
            Console.WriteLine("Calibrating the camera...")
            Console.WriteLine()

            MIL.MbufInquire(MilImage, MIL.M_SIZE_X, SizeX)
            MIL.MbufInquire(MilImage, MIL.M_SIZE_Y, SizeY)

            ' Allocate calibration context in 3D mode.
            MIL.McalAlloc(MilSystem, MIL.M_TSAI_BASED, MIL.M_DEFAULT, MilCalibration)

            ' Calibrate the camera.
            MIL.McalGrid(MilCalibration, MilImage, 0.0, 0.0, 0.0, GRID_NB_ROWS, GRID_NB_COLS, GRID_ROW_SPACING, GRID_COL_SPACING, MIL.M_DEFAULT, MIL.M_CHESSBOARD_GRID)
            MIL.McalInquire(MilCalibration, MIL.M_CALIBRATION_STATUS + MIL.M_TYPE_MIL_INT, CalibrationStatus)

            If CalibrationStatus <> MIL.M_CALIBRATED Then
                MIL.McalFree(MilCalibration)
                MIL.MbufFree(MilImage)
                Console.WriteLine("Camera calibration failed.")
                Console.WriteLine("Press <Enter> to end.")
                Console.WriteLine()
                Console.ReadKey()
                Return
            End If

            ' Prepare for overlay annotations.
            MIL.MdispControl(MilDisplay, MIL.M_OVERLAY, MIL.M_ENABLE)
            MIL.MdispInquire(MilDisplay, MIL.M_OVERLAY_ID, MilOverlayImage)
            MIL.MgraColor(MIL.M_DEFAULT, MIL.M_COLOR_GREEN)

            ' Draw camera calibration points.
            MIL.McalDraw(MIL.M_DEFAULT, MilCalibration, MilOverlayImage, MIL.M_DRAW_IMAGE_POINTS, MIL.M_DEFAULT, MIL.M_DEFAULT)
            Console.WriteLine("The camera was calibrated using a chessboard grid.")
            Console.WriteLine()
            Console.WriteLine("Press <Enter> to continue.")
            Console.WriteLine()
            Console.ReadKey()

            ' Disable overlay.
            MIL.MdispControl(MilDisplay, MIL.M_OVERLAY, MIL.M_DISABLE)

            ' Load laser line image.
            MIL.MbufLoad(LASERLINE_FILENAME, MilImage)

            ' Allocate 3dmap objects.
            MIL.M3dmapAlloc(MilSystem, MIL.M_LASER, MIL.M_CALIBRATED_CAMERA_LINEAR_MOTION, MilLaser)
            MIL.M3dmapAllocResult(MilSystem, MIL.M_LASER_CALIBRATION_DATA, MIL.M_DEFAULT, MilCalibScan)

            ' Set laser line extraction options.
            Dim MilPeakLocator As MIL_ID = MIL.M_NULL
            MIL.M3dmapInquire(MilLaser, MIL.M_DEFAULT, MIL.M_LOCATE_PEAK_1D_CONTEXT_ID + MIL.M_TYPE_MIL_ID, MilPeakLocator)
            MIL.MimControl(MilPeakLocator, MIL.M_PEAK_WIDTH_NOMINAL, PEAK_WIDTH_NOMINAL_2)
            MIL.MimControl(MilPeakLocator, MIL.M_PEAK_WIDTH_DELTA, PEAK_WIDTH_DELTA_2)
            MIL.MimControl(MilPeakLocator, MIL.M_MINIMUM_CONTRAST, MIN_CONTRAST_2)

            ' Calibrate laser profiling context.
            MIL.M3dmapAddScan(MilLaser, MilCalibScan, MilImage, MIL.M_NULL, MIL.M_NULL, MIL.M_DEFAULT, MIL.M_DEFAULT)
            MIL.M3dmapCalibrate(MilLaser, MilCalibScan, MilCalibration, MIL.M_DEFAULT)
            Console.WriteLine("The laser profiling system has been calibrated using the image")
            Console.WriteLine("of one laser line.")
            Console.WriteLine()
            Console.WriteLine("Press <Enter> to continue.")
            Console.WriteLine()
            Console.ReadKey()

            ' Free the result buffer use for calibration as it will Not be used anymore.
            MIL.M3dmapFree(MilCalibScan)
            MilCalibScan = MIL.M_NULL

         ' Allocate the result buffer to hold the scanned 3D points.
         MIL.M3dmapAllocResult(MilSystem, MIL.M_POINT_CLOUD_RESULT, MIL.M_DEFAULT, MilScan)

            ' Set speed of scanned object (speed in mm/frame Is constant).
            MIL.M3dmapControl(MilLaser, MIL.M_DEFAULT, MIL.M_SCAN_SPEED, CONVEYOR_SPEED)

            ' Inquire characteristics of the input sequence.
            MIL.MbufDiskInquire(OBJECT2_SEQUENCE_FILE, MIL.M_NUMBER_OF_IMAGES, NumberOfImages)
            MIL.MbufDiskInquire(OBJECT2_SEQUENCE_FILE, MIL.M_FRAME_RATE, FrameRate)

            ' Open the object sequence file for reading.
            MIL.MbufImportSequence(OBJECT2_SEQUENCE_FILE, MIL.M_DEFAULT, MIL.M_NULL, MIL.M_NULL, CType(MIL.M_NULL, IntPtr), MIL.M_NULL, MIL.M_NULL, MIL.M_OPEN)
            Console.WriteLine("The cookie is being scanned to generate 3D data.")
            Console.WriteLine()

            ' Read And process all images in the input sequence.
            MIL.MappTimer(MIL.M_DEFAULT, MIL.M_TIMER_READ + MIL.M_SYNCHRONOUS, StartTime)

            For n = 0 To NumberOfImages - 1
                ' Read image from sequence.
                MIL.MbufImportSequence(OBJECT2_SEQUENCE_FILE, MIL.M_DEFAULT, MIL.M_LOAD, MIL.M_NULL, MilImage, MIL.M_DEFAULT, 1, MIL.M_READ)

                ' Analyze the image to extract laser line And correct its depth.
                MIL.M3dmapAddScan(MilLaser, MilScan, MilImage, MIL.M_NULL, MIL.M_NULL, MIL.M_POINT_CLOUD_LABEL(1), MIL.M_DEFAULT)

                ' Wait to have a proper frame rate, if necessary.
                MIL.MappTimer(MIL.M_DEFAULT, MIL.M_TIMER_READ + MIL.M_SYNCHRONOUS, EndTime)
                WaitTime = 1.0 / FrameRate - (EndTime - StartTime)
                If WaitTime > 0 Then
                    MIL.MappTimer(MIL.M_DEFAULT, MIL.M_TIMER_WAIT, WaitTime)
                End If
                MIL.MappTimer(MIL.M_DEFAULT, MIL.M_TIMER_READ + MIL.M_SYNCHRONOUS, StartTime)
            Next

            ' Close the object sequence file.
            MIL.MbufImportSequence(OBJECT2_SEQUENCE_FILE, MIL.M_DEFAULT, MIL.M_NULL, MIL.M_NULL, CType(MIL.M_NULL, IntPtr), MIL.M_NULL, MIL.M_NULL, MIL.M_CLOSE)

            ' Convert to MIL.M_CONTAINER for 3D processing.
            MIL.MbufAllocContainer(MilSystem, MIL.M_PROC Or MIL.M_DISP, MIL.M_DEFAULT, MilContainerId)
            Dim NbClouds As Double = 1.0
            MIL.M3dmapInquire(MilScan, MIL.M_DEFAULT, MIL.M_NUMBER_OF_POINT_CLOUDS, NbClouds)
            MIL.M3dmapCopyResult(MilScan, MIL.M_ALL, MilContainerId, MIL.M_POINT_CLOUD_UNORGANIZED, MIL.M_DEFAULT)

            ' Allocate image for the fully corrected depth map.
            MIL.MbufAlloc2d(MilSystem, DEPTH_MAP_SIZE_X, DEPTH_MAP_SIZE_Y, 16 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_PROC + MIL.M_DISP, MilDepthMap)

            ' Include all points during depth map generation.
            MIL.M3dimCalibrateDepthMap(MilContainerId, MilDepthMap, MIL.M_NULL, MIL.M_NULL, MIL.M_DEFAULT, MIL.M_NEGATIVE, MIL.M_DEFAULT)

            ' Remove noise in the container close to the Z = 0.
            Dim MilPlane As MIL_ID = MIL.M3dgeoAlloc(MilSystem, MIL.M_GEOMETRY, MIL.M_DEFAULT, MIL.M_NULL)
            MIL.M3dgeoPlane(MilPlane, MIL.M_COEFFICIENTS, 0.0, 0.0, 1.0, MIN_HEIGHT_THRESHOLD, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_DEFAULT)

            ' MIL.M_INVERSE remove what Is above the plane
            MIL.M3dimCrop(MilContainerId, MilContainerId, MilPlane, MIL.M_NULL, MIL.M_SAME, MIL.M_INVERSE)
            MIL.M3dgeoFree(MilPlane)
            Console.WriteLine("Fully corrected 3D data of the cookie is displayed.")
            Console.WriteLine()

            Dim M3dDisplay As MIL_ID = Alloc3dDisplayId(MilSystem)
            If M3dDisplay <> MIL.M_NULL Then
                Console.WriteLine("Press <R> on the display window to stop/start the rotation.")
                Console.WriteLine()
                MIL.M3ddispSelect(M3dDisplay, MilContainerId, MIL.M_SELECT, MIL.M_DEFAULT)
                MIL.M3ddispSetView(M3dDisplay, MIL.M_AUTO, MIL.M_BOTTOM_TILTED, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_DEFAULT)
                MIL.M3ddispControl(M3dDisplay, MIL.M_AUTO_ROTATE, MIL.M_ENABLE)
            End If

            ' Get fully corrected depth map from accumulated information in the result buffer.
            MIL.M3dimProject(MilContainerId, MilDepthMap, MIL.M_NULL, MIL.M_DEFAULT, MIL.M_MIN_Z, MIL.M_DEFAULT, MIL.M_DEFAULT)
            MIL.M3dimAlloc(MilSystem, MIL.M_FILL_GAPS_CONTEXT, MIL.M_DEFAULT, FillGapsContext)
            MIL.M3dimControl(FillGapsContext, MIL.M_FILL_MODE, MIL.M_X_THEN_Y)
            MIL.M3dimControl(FillGapsContext, MIL.M_FILL_SHARP_ELEVATION, MIL.M_MIN)
            MIL.M3dimControl(FillGapsContext, MIL.M_FILL_SHARP_ELEVATION_DEPTH, GAP_DEPTH)
            MIL.M3dimControl(FillGapsContext, MIL.M_FILL_BORDER, MIL.M_DISABLE)

            MIL.M3dimFillGaps(FillGapsContext, MilDepthMap, MIL.M_NULL, MIL.M_DEFAULT)

            ' Compute the volume of the depth map.
            MIL.M3dmetVolume(MilDepthMap, MIL.M_XY_PLANE, MIL.M_TOTAL, MIL.M_DEFAULT, Volume, CType(MIL.M_NULL, IntPtr))

            Console.WriteLine("Volume of the cookie is {0,4:f1} cm^3.", Volume / 1000.0)
            Console.WriteLine()
            Console.WriteLine("Press <Enter> to end.")
            Console.WriteLine()
            Console.ReadKey()

            If M3dDisplay <> MIL.M_NULL Then
                MIL.M3ddispFree(M3dDisplay)
            End If

            ' Free all allocations.
            MIL.M3dimFree(FillGapsContext)
            MIL.MbufFree(MilContainerId)
            MIL.M3dmapFree(MilScan)
            MIL.M3dmapFree(MilLaser)
            MIL.McalFree(MilCalibration)
            MIL.MbufFree(MilDepthMap)
            MIL.MbufFree(MilImage)
        End Sub

        ' *****************************************************************************
        ' Allocates a 3D display and returns its MIL identifier.
        ' *****************************************************************************
        Private Shared Function Alloc3dDisplayId(ByVal MilSystem As MIL_ID) As MIL_ID
            MIL.MappControl(MIL.M_DEFAULT, MIL.M_ERROR, MIL.M_PRINT_DISABLE)
            Dim MilDisplay3D As MIL_ID = MIL.M3ddispAlloc(MilSystem, MIL.M_DEFAULT, "M_DEFAULT", MIL.M_DEFAULT, MIL.M_NULL)
            MIL.MappControl(MIL.M_DEFAULT, MIL.M_ERROR, MIL.M_PRINT_ENABLE)

            If MilDisplay3D = MIL.M_NULL Then
                Console.WriteLine()
                Console.WriteLine("The current system does not support the 3D display.")
                Console.WriteLine()
            End If

            Return MilDisplay3D
        End Function
    End Class
End Namespace