'***************************************************************************************
' 
' File name: MdigProcess3D.vb
' Location: See Matrox Example Launcher in the MIL Control Center
' 
' 
' Synopsis:  This program shows the use Of the MdigProcess() Function And its multiple
'            buffering acquisition to do robust real-time 3D acquisition, processing
'            And display.
' 
'            The user's processing code to execute is located in a callback function
'            that will be called for each frame acquired (see ProcessingFunction()).
' 
'      Note: The average processing time must be shorter than the grab time Or some
'            frames will be missed. Also, if the processing results are Not displayed
'            the CPU usage Is reduced significantly.
' 
' Copyright (C) Matrox Electronic Systems Ltd., 1992-2020.
' All Rights Reserved
' 
'***************************************************************************************
Imports Microsoft.VisualBasic
Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Runtime.InteropServices

Imports Matrox.MatroxImagingLibrary

Namespace MDigProcess3D
    Friend Class Program

        ' Number of images in the buffering grab queue.
        ' Generally, increasing this number gives a better real-time grab.
        Private Const BUFFERING_SIZE_MAX As Integer = 5

        ' User's processing function hook data object.
        Public Class HookDataStruct
            Public MilDigitizer As MIL_ID
            Public MilContainerDisp As MIL_ID
            Public ProcessedImageCount As Integer
        End Class


        ' Main function.
        Shared Sub Main(ByVal args() As String)
            Dim MilApplication As MIL_ID = MIL.M_NULL
            Dim MilSystem As MIL_ID = MIL.M_NULL
            Dim MilDigitizer As MIL_ID = MIL.M_NULL
            Dim MilDisplay As MIL_ID = MIL.M_NULL
            Dim MilContainerDisp As MIL_ID = MIL.M_NULL
            Dim MilGrabBufferList(BUFFERING_SIZE_MAX - 1) As MIL_ID
            Dim MilGrabBufferListSize As Integer = 0
            Dim ProcessFrameCount As MIL_INT = 0
            Dim ProcessFrameRate As Double = 0

            Dim UserHookData As New HookDataStruct()

            ' Allocate defaults.
            MIL.MappAlloc(MIL.M_NULL, MIL.M_DEFAULT, MilApplication)
            MIL.MsysAlloc(MIL.M_DEFAULT, MIL.M_SYSTEM_DEFAULT, MIL.M_DEFAULT, MIL.M_DEFAULT, MilSystem)

            If Alloc3dDisplayAndContainer(MilSystem, MilDisplay, MilContainerDisp) = False Then
                MIL.MsysFree(MilSystem)
                MIL.MappFree(MilApplication)
                Console.ReadKey()
                Return
            End If
            MIL.MdigAlloc(MilSystem, MIL.M_DEFAULT, "M_DEFAULT", MIL.M_DEFAULT, MilDigitizer)

            ' Print a message. 
            Console.WriteLine()
            Console.WriteLine("MULTIPLE 3D CONTAINERS PROCESSING.")
            Console.WriteLine("----------------------------------")
            Console.WriteLine()

            ' Open the feature browser to setup the camera before acquisition (if Not using the System Host simulator).
            If MIL.MsysInquire(MilSystem, MIL.M_GENICAM_AVAILABLE, MIL.M_NULL) <> MIL.M_NO Then

                MIL.MdigControl(MilDigitizer, MIL.M_GC_FEATURE_BROWSER, MIL.M_OPEN + MIL.M_ASYNCHRONOUS)
                Console.WriteLine("Please setup your 3D camera using the feature browser.")
                Console.Write("Press <Enter> to start processing." + Constants.vbCr)
                Console.ReadKey()
            End If
            ' Do a first acquisition to determine what Is included in the type camera output.
            MIL.MdigGrab(MilDigitizer, MilContainerDisp)

            ' Print the acquired MIL Container detailed informations.'
            PrintContainerInfo(MilContainerDisp)

            ' If the grabbed Container has 3D data And Is Displayable And Processable.
            If (MIL.MbufInquireContainer(MilContainerDisp, MIL.M_CONTAINER, MIL.M_3D_DISPLAYABLE, MIL.M_NULL) <> MIL.M_NOT_DISPLAYABLE) And
               (MIL.MbufInquireContainer(MilContainerDisp, MIL.M_CONTAINER, MIL.M_3D_CONVERTIBLE, MIL.M_NULL) <> MIL.M_NOT_CONVERTIBLE) Then

                ' Display the Container on the 3D display.
                MIL.M3ddispSelect(MilDisplay, MilContainerDisp, MIL.M_DEFAULT, MIL.M_DEFAULT)

                ' Grab continuously on the 3D display And wait for a key press.
                MIL.MdigGrabContinuous(MilDigitizer, MilContainerDisp)

                Console.WriteLine("Live 3D acquisition in progress...")
                Console.WriteLine("Press <Enter> to start the processing.")
                Console.ReadKey()

                ' Halt continuous grab.
                MIL.MdigHalt(MilDigitizer)

                ' Allocate the grab Containers for processing.
                For MilGrabBufferListSize = 0 To BUFFERING_SIZE_MAX - 1
                    MIL.MbufAllocContainer(MilSystem, MIL.M_PROC Or MIL.M_GRAB, MIL.M_DEFAULT, MilGrabBufferList(MilGrabBufferListSize))
                Next

                ' Initialize the user's processing function data structure.
                UserHookData.MilDigitizer = MilDigitizer
                UserHookData.MilContainerDisp = MilContainerDisp
                UserHookData.ProcessedImageCount = 0

                ' get a handle to the HookDataStruct object in the managed heap, we will use this 
                ' handle to get the object back in the callback function
                Dim hUserData As GCHandle = GCHandle.Alloc(UserHookData)
                Dim ProcessingFunctionPtr As New MIL_DIG_HOOK_FUNCTION_PTR(AddressOf ProcessingFunction)

                ' Start the processing. The processing function is called with every frame grabbed.
                MIL.MdigProcess(MilDigitizer, MilGrabBufferList, MilGrabBufferListSize, MIL.M_START, MIL.M_DEFAULT, ProcessingFunctionPtr, CType(hUserData, IntPtr))


                ' Here the main() Is free to perform other tasks while the processing Is executing.
                ' ---------------------------------------------------------------------------------


                ' Print a message And wait for a key press after a minimum number of frames.
                Console.WriteLine()
                Console.WriteLine("Processing in progress...")
                Console.WriteLine("Press <Enter> to stop.")
                Console.WriteLine()
                Console.ReadKey()

                ' Stop the processing.
                MIL.MdigProcess(MilDigitizer, MilGrabBufferList, MilGrabBufferListSize, MIL.M_STOP, MIL.M_DEFAULT, ProcessingFunctionPtr, CType(hUserData, IntPtr))

                ' Free the GCHandle when no longer used.
                hUserData.Free()

                ' Print statistics.
                MIL.MdigInquire(MilDigitizer, MIL.M_PROCESS_FRAME_COUNT, ProcessFrameCount)
                MIL.MdigInquire(MilDigitizer, MIL.M_PROCESS_FRAME_RATE, ProcessFrameRate)
                Console.WriteLine()
                Console.WriteLine()
                Console.WriteLine("{0} 3D containers grabbed at {1:0.0} frames/sec ({2:0.0} ms/frame).", ProcessFrameCount, ProcessFrameRate, 1000.0 / ProcessFrameRate)
                Console.WriteLine("Press <Enter> to end.")
                Console.WriteLine()
                Console.ReadKey()

                ' Free the grab buffers.
                Do While MilGrabBufferListSize > 0
                    MilGrabBufferListSize -= 1
                    MIL.MbufFree(MilGrabBufferList(MilGrabBufferListSize))
                Loop
            Else
                Console.WriteLine("ERROR: The camera provides no (or more than one) 3D Component(s) of type Range or Disparity.")
                Console.WriteLine("Press <Enter> to end.")
                Console.WriteLine()
                Console.ReadKey()
            End If


            ' Free display buffer.
            MIL.MbufFree(MilContainerDisp)

            MIL.M3ddispFree(MilDisplay)

            MIL.MdigFree(MilDigitizer)
            MIL.MsysFree(MilSystem)
            MIL.MappFree(MilApplication)
        End Sub

        ' User's processing function called every time a grab container is ready.
        ' -----------------------------------------------------------------------

        ' Local defines.
        Private Const STRING_LENGTH_MAX As Integer = 20

        Private Shared Function ProcessingFunction(ByVal HookType As MIL_INT, ByVal HookId As MIL_ID, ByVal HookDataPtr As IntPtr) As MIL_INT
            Dim ModifiedBufferId As MIL_ID = MIL.M_NULL

            ' this is how to check if the user data is null, the IntPtr class
            ' contains a member, Zero, which exists solely for this purpose
            If (Not IntPtr.Zero.Equals(HookDataPtr)) Then
                ' get the handle to the DigHookUserData object back from the IntPtr
                Dim hUserData As GCHandle = CType(HookDataPtr, GCHandle)

                ' get a reference to the DigHookUserData object
                Dim UserData As HookDataStruct = CType(hUserData.Target, HookDataStruct)

                ' Retrieve the MIL_ID of the grabbed buffer.
                MIL.MdigGetHookInfo(HookId, MIL.M_MODIFIED_BUFFER + MIL.M_BUFFER_ID, ModifiedBufferId)

                ' Increment the frame counter.
                UserData.ProcessedImageCount += 1

                ' Print and draw the frame count (remove to reduce CPU usage).
                Console.Write("Processing frame #{0}." & Constants.vbCr, UserData.ProcessedImageCount)

                ' Execute the processing and update the display.
                MIL.MbufConvert3d(ModifiedBufferId, UserData.MilContainerDisp, MIL.M_NULL, MIL.M_DEFAULT, MIL.M_COMPENSATE)
         End If

            Return 0
        End Function

        'Utility function to print the MIL Container detailed informations.
        Private Shared Sub PrintContainerInfo(MilContainer As MIL_ID)
            Dim ComponentCount As MIL_INT = MIL.MbufInquire(MilContainer, MIL.M_COMPONENT_COUNT, MIL.M_NULL)

            Console.WriteLine("Container Information:")
            Console.WriteLine("----------------------")
            Console.WriteLine("Container:    Component Count: {0}", ComponentCount)
            Dim c As MIL_INT
            For c = 0 To ComponentCount - 1
                Dim ComponentId As MIL_ID
                MIL.MbufInquireContainer(MilContainer, MIL.M_COMPONENT_BY_INDEX(c), MIL.M_COMPONENT_ID, ComponentId)
                Dim ComponentName As StringBuilder = New StringBuilder()
                MIL.MbufInquire(ComponentId, MIL.M_COMPONENT_TYPE_NAME, ComponentName)
                Dim DataType As MIL_INT = MIL.MbufInquire(ComponentId, MIL.M_DATA_TYPE, MIL.M_NULL)
                Dim DataFormat As MIL_INT = (MIL.MbufInquire(ComponentId, MIL.M_DATA_FORMAT, MIL.M_NULL) And (MIL.M_PACKED Or MIL.M_PLANAR))
                Dim DataTypeStr As String
                If DataType = MIL.M_UNSIGNED Then
                    DataTypeStr = "u"
                ElseIf DataType = MIL.M_SIGNED Then
                    DataTypeStr = "s"
                ElseIf DataType = MIL.M_FLOAT Then
                    DataTypeStr = "f"
                Else
                    DataTypeStr = ""
                End If
                Dim BandStr As String
                If MIL.MbufInquire(ComponentId, MIL.M_SIZE_BAND, MIL.M_NULL) = 1 Then
                    BandStr = "Mono"
                ElseIf DataFormat = MIL.M_PLANAR Then
                    BandStr = "Planar"
                Else
                    BandStr = "Packed"
                End If
                Dim GroupId As Long
                Dim SourceId As Long
                Dim RegionId As Long
                MIL.MbufInquire(ComponentId, MIL.M_COMPONENT_GROUP_ID, GroupId)
                MIL.MbufInquire(ComponentId, MIL.M_COMPONENT_SOURCE_ID, SourceId)
                MIL.MbufInquire(ComponentId, MIL.M_COMPONENT_REGION_ID, RegionId)
                Console.WriteLine("Component[{0}]: {1,11}[{2}:{3}:{4}] Band: {5,1}, Size X: {6,4}, Size Y: {7,4}, Type: {8,2}{9} ({10,6})",
                c, ComponentName,
                GroupId,
                SourceId,
                RegionId,
                MIL.MbufInquire(ComponentId, MIL.M_SIZE_BAND, MIL.M_NULL),
                MIL.MbufInquire(ComponentId, MIL.M_SIZE_X, MIL.M_NULL),
                MIL.MbufInquire(ComponentId, MIL.M_SIZE_Y, MIL.M_NULL),
                MIL.MbufInquire(ComponentId, MIL.M_SIZE_BIT, MIL.M_NULL),
                DataTypeStr,
                BandStr)
            Next

            Console.WriteLine()
        End Sub

        '*****************************************************************************
        ' Allocates a 3D display and and a container and returns their MIL identifier. 
        '*****************************************************************************
        Private Shared Function Alloc3dDisplayAndContainer(MilSystem As MIL_ID, ByRef MilDisplay As MIL_ID, ByRef MilContainerDisp As MIL_ID) As Boolean
            ' First we check if the system Is local
            If MIL.MsysInquire(MilSystem, MIL.M_LOCATION, MIL.M_NULL) <> MIL.M_LOCAL Then
                Console.WriteLine("This example requires a 3D display which is not supported on a remote system.")
                Console.WriteLine("Please select a local system as the default.")
                Return False
            End If
            MIL.MappControl(MIL.M_DEFAULT, MIL.M_ERROR, MIL.M_PRINT_DISABLE)
            MIL.M3ddispAlloc(MilSystem, MIL.M_DEFAULT, "M_DEFAULT", MIL.M_DEFAULT, MilDisplay)
            MIL.MbufAllocContainer(MilSystem, MIL.M_PROC + MIL.M_GRAB + MIL.M_DISP, MIL.M_DEFAULT, MilContainerDisp)
            If MilContainerDisp = MIL.M_NULL Or MilDisplay = MIL.M_NULL Then
                Dim ErrorMessage As StringBuilder = New StringBuilder()
                Dim ErrorMessageSub1 As StringBuilder = New StringBuilder()
                MIL.MappGetError(MIL.M_DEFAULT, MIL.M_GLOBAL + MIL.M_MESSAGE, ErrorMessage)
                MIL.MappGetError(MIL.M_DEFAULT, MIL.M_GLOBAL_SUB_1 + MIL.M_MESSAGE, ErrorMessageSub1)
                Console.WriteLine()
                Console.WriteLine("The current system does not support the 3D display.")
                Console.WriteLine("   " + ErrorMessage.ToString())
                Console.WriteLine("   " + ErrorMessageSub1.ToString())
                Console.WriteLine()
                If MilDisplay <> MIL.M_NULL Then
                    MIL.M3ddispFree(MilDisplay)
                End If
                If MilContainerDisp <> MIL.M_NULL Then
                    MIL.MbufFree(MilContainerDisp)
                End If
                Return False
            End If
            MIL.MappControl(MIL.M_DEFAULT, MIL.M_ERROR, MIL.M_PRINT_ENABLE)
            Return True
        End Function
    End Class
End Namespace