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

'***************************************************************************
' 
' File name: M3dmap.vb
' 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-2016.
' All Rights Reserved
'***************************************************************************
Imports Microsoft.VisualBasic
Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Runtime.InteropServices
Imports Matrox.MatroxImagingLibrary

Namespace M3dmap
    Friend Class Program
        ' Function declarations for DirectX display
        <DllImport("mdispd3d.dll", CallingConvention:=CallingConvention.Cdecl)> _
        Public Shared Function MdepthD3DAlloc(ByVal DepthBuffer As MIL_ID, _
                                              ByVal IntensityBuffer As MIL_ID, _
                                              ByVal DisplaySizeX As MIL_INT, _
                                              ByVal DisplaySizeY As MIL_INT, _
                                              ByVal ScaleX As Double, _
                                              ByVal ScaleY As Double, _
                                              ByVal ScaleZ As Double, _
                                              ByVal MinZ As Double, _
                                              ByVal MaxZ As Double, _
                                              ByVal MaxDistanceZ As Double, _
                                              ByVal WindowHandle As IntPtr) As IntPtr
        End Function
        <DllImport("mdispd3d.dll", CallingConvention:=CallingConvention.Cdecl)> _
        Public Shared Sub MdispD3DFree(ByVal DispHandle As IntPtr)
        End Sub
        <DllImport("mdispd3d.dll", CallingConvention:=CallingConvention.Cdecl)> _
        Public Shared Sub MdispD3DShow(ByVal DispHandle As IntPtr)
        End Sub
        <DllImport("mdispd3d.dll", CallingConvention:=CallingConvention.Cdecl)> _
        Public Shared Sub MdispD3DHide(ByVal DispHandle As IntPtr)
        End Sub
        <DllImport("mdispd3d.dll", CallingConvention:=CallingConvention.Cdecl)> _
        Public Shared Sub MdispD3DPrintHelp(ByVal DispHandle As IntPtr)
        End Sub

        Shared Sub Main(ByVal args() As String)
            '****************************************************************************
            ' Main.
            '****************************************************************************
            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.

            ' Allocate defaults.
            MIL.MappAllocDefault(MIL.M_DEFAULT, MilApplication, MilSystem, MilDisplay, CType(MIL.M_NULL, IntPtr), CType(MIL.M_NULL, IntPtr))

            ' Run the depth correction example.
            DepthCorrectionExample(MilSystem, MilDisplay)

            ' Run the calibrated camera example.
            CalibratedCameraExample(MilSystem, MilDisplay)

            ' Free defaults.
            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 Double = 400
        Private Const CALIB_TEXT_POS_Y As Double = 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 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.

            ' 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")
            Console.WriteLine("defects 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 NbReferencePlanes - 1
                Dim CalibString As String

                ' Read image from sequence.
                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.00} mm", n + 1, CORRECTED_DEPTHS(CType(n, Integer)))
                MIL.MgraText(MIL.M_DEFAULT, MilOverlayImage, CALIB_TEXT_POS_X, CALIB_TEXT_POS_Y, CalibString)

                ' Set desired corrected depth of next calibration plane.
                MIL.M3dmapControl(MilLaser, MIL.M_DEFAULT, MIL.M_CORRECTED_DEPTH, CORRECTED_DEPTHS(CType(n, Integer)) * 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 n

            ' 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 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 n

            ' 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.M3dmapExtract(MilScan, MilDepthMap, MIL.M_NULL, MIL.M_CORRECTED_DEPTH_MAP, MIL.M_DEFAULT, 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 Double = -50
        Private Const TEXT_V_OFFSET_1 As Double = -6
        Private Const TEXT_H_OFFSET_2 As Double = -30
        Private Const TEXT_V_OFFSET_2 As Double = 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 MIL_INT = 0                            ' Counter.
            Dim MinPixels() As MIL_INT = Nothing            ' Maximum height of defects.
            Dim DefectThreshold As Double = 0.0             ' A gray level below it is a defect.
            Dim CogX() As Double = Nothing                  ' X coordinate of center of gravity.
            Dim CogY() As Double = Nothing                  ' 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(CType(TotalBlobs, Integer) - 1) {}
            CogY = New Double(CType(TotalBlobs, Integer) - 1) {}
            MinPixels = New MIL_INT(CType(TotalBlobs, Integer) - 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 TotalBlobs - 1
                    Dim DepthOfDefect As Double
                    Dim DepthString As String

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

                    Console.WriteLine("Defect #{0}: depth ={1,5:0.00} mm", n, DepthOfDefect)
                    Console.WriteLine()
                    MIL.MgraText(MIL.M_DEFAULT, MilOverlayImage, CogX(CType(n, Integer)) + TEXT_H_OFFSET_1, CogY(CType(n, Integer)) + TEXT_V_OFFSET_1, "Defect depth")
                    MIL.MgraText(MIL.M_DEFAULT, MilOverlayImage, CogX(CType(n, Integer)) + TEXT_H_OFFSET_2, CogY(CType(n, Integer)) + TEXT_V_OFFSET_2, DepthString)
                Next n
            Else
                Console.WriteLine("Error: Not enough memory.")
            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

            ' Number of possible gray levels in corrected depth map.
            NbGrayLevels = CType(1 << CType(SizeBit, Integer), 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 Shared ReadOnly GRID_NB_ROWS As MIL_INT = 13
        Private Shared ReadOnly GRID_NB_COLS As MIL_INT = 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 Shared ReadOnly DEPTH_MAP_SIZE_X As MIL_INT = 480 ' in pixels
        Private Shared ReadOnly DEPTH_MAP_SIZE_Y As MIL_INT = 480 ' in pixels
        Private Const GAP_DEPTH As Double = 1.5 ' in mm

        ' D3D display parameters 
        Private Shared ReadOnly D3D_DISPLAY_SIZE_X As MIL_INT = 640
        Private Shared ReadOnly D3D_DISPLAY_SIZE_Y As MIL_INT = 480

        ' Peak detection parameters.
        Private Shared ReadOnly PEAK_WIDTH_NOMINAL_2 As MIL_INT = 9
        Private Shared ReadOnly PEAK_WIDTH_DELTA_2 As MIL_INT = 7
        Private Shared ReadOnly MIN_CONTRAST_2 As MIL_INT = 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 DepthMapId As MIL_ID = MIL.M_NULL ' Image buffer identifier (for results).
            Dim LaserId As MIL_ID = MIL.M_NULL ' 3dmap laser profiling context identifier.
            Dim CalibScanId As MIL_ID = MIL.M_NULL ' 3dmap result buffer identifier for
            ' laser line calibration.
            Dim ScanId As MIL_ID = MIL.M_NULL ' 3dmap result buffer identifier.
            Dim CalibrationStatus As MIL_INT = 0 ' Used to ensure if 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.
            Dim VolumeError As Double = 0.0 ' Volume of region outside the 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.ReadLine()
                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, LaserId)
            MIL.M3dmapAllocResult(MilSystem, MIL.M_LASER_CALIBRATION_DATA, MIL.M_DEFAULT, CalibScanId)

            ' Set laser line extraction options.
            Dim MilPeakLocator As MIL_ID = MIL.M_NULL
            MIL.M3dmapInquire(LaserId, 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(LaserId, CalibScanId, MilImage, MIL.M_NULL, MIL.M_NULL, MIL.M_DEFAULT, MIL.M_DEFAULT)
            MIL.M3dmapCalibrate(LaserId, CalibScanId, 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 used for calibration because it will not be used anymore.
            MIL.M3dmapFree(CalibScanId)
            CalibScanId = MIL.M_NULL

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

            ' Set speed of scanned object (speed in mm/frame is constant).
            MIL.M3dmapControl(LaserId, 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(LaserId, ScanId, 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 n

            ' 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)

            ' 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, DepthMapId)

            ' Set fully corrected depth map generation parameters.
            MIL.M3dmapControl(ScanId, MIL.M_DEFAULT, MIL.M_AUTO_SCALE_XY, MIL.M_CLIP)
            MIL.M3dmapControl(ScanId, MIL.M_DEFAULT, MIL.M_FILL_MODE, MIL.M_X_THEN_Y)
            MIL.M3dmapControl(ScanId, MIL.M_DEFAULT, MIL.M_FILL_SHARP_ELEVATION, MIL.M_MIN)
            MIL.M3dmapControl(ScanId, MIL.M_DEFAULT, MIL.M_FILL_SHARP_ELEVATION_DEPTH, GAP_DEPTH)

            ' Include all points during depth map generation.
            MIL.M3dmapSetBox(ScanId, MIL.M_EXTRACTION_BOX, MIL.M_BOUNDING_BOX, MIL.M_ALL, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_DEFAULT)

            ' Get fully corrected depth map from accumulated information in the result buffer.
            MIL.M3dmapExtract(ScanId, DepthMapId, MIL.M_NULL, MIL.M_CORRECTED_DEPTH_MAP, MIL.M_DEFAULT, MIL.M_DEFAULT)

            ' Compute the volume of the whole depth map.
            MIL.M3dmapStat(DepthMapId, MIL.M_NULL, MIL.M_NULL, MIL.M_NULL, MIL.M_VOLUME, MIL.M_DEFAULT, MIL.M_DEFAULT, Volume)

            ' Compute the volume caused by noise around Z = 0. Use an outlier distance of 1 mm.
            MIL.M3dmapStat(DepthMapId, MIL.M_NULL, MIL.M_NULL, MIL.M_NULL, MIL.M_VOLUME, MIN_HEIGHT_THRESHOLD, MIL.M_DEFAULT, VolumeError)

            ' Compute the volume of the cookie.
            Volume -= VolumeError

            Console.WriteLine("fully corrected 3d data of the cookie is displayed.")
            Console.WriteLine()

            ' Try to allocate D3D display.
            Dim DispHandle As IntPtr
            DispHandle = MdepthD3DAlloc(DepthMapId, MIL.M_NULL, D3D_DISPLAY_SIZE_X, D3D_DISPLAY_SIZE_Y, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_DEFAULT, IntPtr.Zero)

            If DispHandle <> IntPtr.Zero Then
                ' Hide Mil Display.
                MIL.MdispControl(MilDisplay, MIL.M_WINDOW_SHOW, MIL.M_DISABLE)
                MdispD3DShow(DispHandle)
                Console.WriteLine("D3D display controls:")
                Console.WriteLine(" .Left click" & Constants.vbTab & "move object")
                Console.WriteLine(" .Right click" & Constants.vbTab & "rotate object")
                Console.WriteLine(" .Scroll wheel" & Constants.vbTab & "zoom")
                Console.WriteLine(" .R" & Constants.vbTab + Constants.vbTab & "start/stop animation")
                Console.WriteLine(" .P" & Constants.vbTab + Constants.vbTab & "enable/disable point cloud")
                Console.WriteLine()
            Else
                MIL.MdispControl(MilDisplay, MIL.M_VIEW_MODE, MIL.M_AUTO_SCALE)
                MIL.MdispSelect(MilDisplay, DepthMapId)
            End If

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


            If DispHandle <> IntPtr.Zero Then
                MdispD3DHide(DispHandle)
                MdispD3DFree(DispHandle)
            End If

            ' Free all allocations.
            MIL.M3dmapFree(ScanId)
            MIL.M3dmapFree(LaserId)
            MIL.McalFree(MilCalibration)
            MIL.MbufFree(DepthMapId)
            MIL.MbufFree(MilImage)
        End Sub
    End Class
End Namespace