'**************************************************************************************
' File name: MPat.vb
' Location: See Matrox Example Launcher in the MIL Control Center
' 
'
' Synopsis:  This program contains 4 examples of the pattern matching module:
'
'            The first example defines a model and then searches for it in a shifted
'            version of the image (without rotation).
'
'            The second example defines a regular model and then searches for it in a
'            rotated version of the image using search angle range.
'
'            The third example defines a rotated model at certain angle and then
'            searches for it in a rotated version of the image.
'
'            The fourth example automatically allocates a model in a wafer image and 
'            finds its horizontal and vertical displacement.
'
' Copyright (C) Matrox Electronic Systems Ltd., 1992-2020.
' All Rights Reserved
'**************************************************************************************
Imports Microsoft.VisualBasic
Imports System
Imports System.Collections.Generic
Imports System.Text

Imports Matrox.MatroxImagingLibrary

Namespace MPat
    Friend Class Program
        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.

            Console.Write(Constants.vbLf + "GRAYSCALE PATTERN MATCHING:" + Constants.vbLf)
            Console.Write("---------------------------" + Constants.vbLf + Constants.vbLf)

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

            ' Run the search at 0 degree example.
            SearchModelExample(MilSystem, MilDisplay)

            ' Run the search over 360 degrees example.
            SearchModelAngleRangeExample(MilSystem, MilDisplay)

            ' Run the search rotated model example.
            SearchModelAtAngleExample(MilSystem, MilDisplay)

            ' Run the automatic model allocation example.
            AutoAllocationModelExample(MilSystem, MilDisplay)

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

        '****************************************************************************
        ' Find model in shifted version of the image example.

        ' Source image file name.
        Private Const FIND_IMAGE_FILE As String = MIL.M_IMAGE_PATH & "CircuitsBoard.mim"

        ' Model position and size.
        Private Const FIND_MODEL_X_POS As Integer = 153
        Private Const FIND_MODEL_Y_POS As Integer = 132
        Private Const FIND_MODEL_WIDTH As Integer = 128
        Private Const FIND_MODEL_HEIGHT As Integer = 128
        Private Const FIND_MODEL_X_CENTER As Double = (FIND_MODEL_X_POS + (FIND_MODEL_WIDTH - 1) / 2.0)
        Private Const FIND_MODEL_Y_CENTER As Double = (FIND_MODEL_Y_POS + (FIND_MODEL_HEIGHT - 1) / 2.0)

        ' Target image shifting values.
        Private Const FIND_SHIFT_X As Double = 4.5
        Private Const FIND_SHIFT_Y As Double = 7.5

        ' Minimum match score to determine acceptability of model (default).
        Private Const FIND_MODEL_MIN_MATCH_SCORE As Double = 70.0

        ' Minimum accuracy for the search.
        Private Const FIND_MODEL_MIN_ACCURACY As Double = 0.1

        Private Shared Sub SearchModelExample(ByVal MilSystem As MIL_ID, ByVal MilDisplay As MIL_ID)
            Dim MilImage As MIL_ID = MIL.M_NULL     ' Image buffer identifier.
            Dim GraphicList As MIL_ID = MIL.M_NULL  ' Graphic list identifier.
            Dim ContextId As MIL_ID = MIL.M_NULL    ' Model identifier.
            Dim Result As MIL_ID = MIL.M_NULL       ' Result identifier.
            Dim NumResults As MIL_INT = 0           ' Number of results found.
            Dim XOrg As Double = 0.0                ' Original model position.
            Dim YOrg As Double = 0.0
            Dim x As Double = 0.0                   ' Model position.
            Dim y As Double = 0.0
            Dim ErrX As Double = 0.0                ' Model error position.
            Dim ErrY As Double = 0.0
            Dim Score As Double = 0.0               ' Model correlation score.
            Dim Time As Double = 0.0                ' Model search time.
            Dim AnnotationColor As Double = MIL.M_COLOR_GREEN ' Drawing color.

            ' Restore source image into an automatically allocated image buffer.
            MIL.MbufRestore(FIND_IMAGE_FILE, MilSystem, MilImage)

            ' Display the image buffer.
            MIL.MdispSelect(MilDisplay, MilImage)

            ' Allocate a graphic list to hold the subpixel annotations to draw.
            MIL.MgraAllocList(MilSystem, MIL.M_DEFAULT, GraphicList)

            ' Associate the graphic list to the display for annotations.
            MIL.MdispControl(MilDisplay, MIL.M_ASSOCIATED_GRAPHIC_LIST_ID, GraphicList)

            ' Allocate a normalized pattern matching context.
            MIL.MpatAlloc(MilSystem, MIL.M_NORMALIZED, MIL.M_DEFAULT, ContextId)

            ' Define a regular model.
            MIL.MpatDefine(ContextId, MIL.M_REGULAR_MODEL, MilImage, FIND_MODEL_X_POS, _
                            FIND_MODEL_Y_POS, FIND_MODEL_WIDTH, FIND_MODEL_HEIGHT, MIL.M_DEFAULT)

            ' Set the search accuracy to high.
            MIL.MpatControl(ContextId, MIL.M_DEFAULT, MIL.M_ACCURACY, MIL.M_HIGH)

            ' Set the search model speed to high.
            MIL.MpatControl(ContextId, MIL.M_DEFAULT, MIL.M_SPEED, MIL.M_HIGH)

            ' Preprocess the model.
            MIL.MpatPreprocess(ContextId, MIL.M_DEFAULT, MilImage)

            ' Draw a box around the model in the model image.
            MIL.MgraColor(MIL.M_DEFAULT, AnnotationColor)
            MIL.MpatDraw(MIL.M_DEFAULT, ContextId, GraphicList, MIL.M_DRAW_BOX + MIL.M_DRAW_POSITION, _
                                                                           MIL.M_DEFAULT, MIL.M_ORIGINAL)

            ' Pause to show the original image and model position.
            Console.Write(Constants.vbLf + "A {0}x{1} model was defined in the source image." + Constants.vbLf, FIND_MODEL_WIDTH, FIND_MODEL_HEIGHT)
            Console.Write("It will be found in an image shifted by {0:0.00} in X and {1:0.00} in Y." + Constants.vbLf, FIND_SHIFT_X, FIND_SHIFT_Y)
            Console.Write("Press <Enter> to continue." + Constants.vbLf + Constants.vbLf)
            Console.ReadKey()

            ' Clear annotations.
            MIL.MgraClear(MIL.M_DEFAULT, GraphicList)

            ' Translate the image on a subpixel level.
            MIL.MimTranslate(MilImage, MilImage, FIND_SHIFT_X, FIND_SHIFT_Y, MIL.M_DEFAULT)

            ' Allocate result buffer.
            MIL.MpatAllocResult(MilSystem, MIL.M_DEFAULT, Result)

            ' Dummy first call for bench measure purpose only (bench stabilization, cache effect, etc...). This first call is NOT required by the application.
            MIL.MpatFind(ContextId, MilImage, Result)
            MIL.MappTimer(MIL.M_DEFAULT, MIL.M_TIMER_RESET + MIL.M_SYNCHRONOUS, CType(MIL.M_NULL, IntPtr))

            ' Find the model in the target buffer.
            MIL.MpatFind(ContextId, MilImage, Result)

            ' Read the time spent in MpatFindModel.
            MIL.MappTimer(MIL.M_DEFAULT, MIL.M_TIMER_READ + MIL.M_SYNCHRONOUS, Time)

            ' If one model was found above the acceptance threshold.
            MIL.MpatGetResult(Result, MIL.M_GENERAL, MIL.M_NUMBER + MIL.M_TYPE_MIL_INT, NumResults)
            If NumResults = 1 Then
                ' Read results and draw a box around the model occurrence.
                MIL.MpatGetResult(Result, MIL.M_DEFAULT, MIL.M_POSITION_X, x)
                MIL.MpatGetResult(Result, MIL.M_DEFAULT, MIL.M_POSITION_Y, y)
                MIL.MpatGetResult(Result, MIL.M_DEFAULT, MIL.M_SCORE, Score)
                MIL.MgraColor(MIL.M_DEFAULT, AnnotationColor)
                MIL.MpatDraw(MIL.M_DEFAULT, Result, GraphicList, MIL.M_DRAW_BOX + MIL.M_DRAW_POSITION, MIL.M_DEFAULT, MIL.M_DEFAULT)

                ' Calculate the position errors in X and Y and inquire original model position.
                ErrX = Math.Abs((FIND_MODEL_X_CENTER + FIND_SHIFT_X) - x)
                ErrY = Math.Abs((FIND_MODEL_Y_CENTER + FIND_SHIFT_Y) - y)
                MIL.MpatInquire(ContextId, MIL.M_DEFAULT, MIL.M_ORIGINAL_X, XOrg)
                MIL.MpatInquire(ContextId, MIL.M_DEFAULT, MIL.M_ORIGINAL_Y, YOrg)

                ' Print out the search result of the model in the original image.
                Console.Write("Search results:" + Constants.vbLf)
                Console.Write("---------------------------------------------------" + Constants.vbLf)
                Console.Write("The model is found to be shifted by " & Constants.vbTab & "X:{0:0.00}, Y:{1:0.00}." + Constants.vbLf, x - XOrg, y - YOrg)
                Console.Write("The model position error is " & Constants.vbTab + Constants.vbTab & "X:{0:0.00}, Y:{1:0.00}" + Constants.vbLf, ErrX, ErrY)
                Console.Write("The model match score is " & Constants.vbTab + Constants.vbTab & "{0:0.0}" + Constants.vbLf, Score)
                Console.Write("The search time is " & Constants.vbTab + Constants.vbTab + Constants.vbTab & "{0:0.000} ms" + Constants.vbLf + Constants.vbLf, Time * 1000.0)

                ' Verify the results.
                If (Math.Abs((x - XOrg) - FIND_SHIFT_X) > FIND_MODEL_MIN_ACCURACY) OrElse (Math.Abs((y - YOrg) - FIND_SHIFT_Y) > FIND_MODEL_MIN_ACCURACY) OrElse (Score < FIND_MODEL_MIN_MATCH_SCORE) Then
                    Console.Write("Results verification error !" + Constants.vbLf)
                End If
            Else
                Console.Write("Model not found !" + Constants.vbLf)
            End If

            ' Wait for a key to be pressed.
            Console.Write("Press <Enter> to continue." + Constants.vbLf + Constants.vbLf)
            Console.ReadKey()

            ' Clear annotations.
            MIL.MgraClear(MIL.M_DEFAULT, GraphicList)

            ' Free all allocations.
            MIL.MgraFree(GraphicList)
            MIL.MpatFree(Result)
            MIL.MpatFree(ContextId)
            MIL.MbufFree(MilImage)
        End Sub

        '****************************************************************************
        ' Find rotated model example.

        ' Source image file name. 
        Private Const ROTATED_FIND_IMAGE_FILE As String = MIL.M_IMAGE_PATH & "CircuitsBoard.mim"

        ' Image rotation values.
        Private Const ROTATED_FIND_ROTATION_DELTA_ANGLE As Integer = 10
        Private Const ROTATED_FIND_ROTATION_ANGLE_STEP As Integer = 1
        Private Const ROTATED_FIND_RAD_PER_DEG As Double = 0.01745329251

        ' Model position and size.
        Private Const ROTATED_FIND_MODEL_X_POS As Integer = 153
        Private Const ROTATED_FIND_MODEL_Y_POS As Integer = 132
        Private Const ROTATED_FIND_MODEL_WIDTH As Integer = 128
        Private Const ROTATED_FIND_MODEL_HEIGHT As Integer = 128

        Private Const ROTATED_FIND_MODEL_X_CENTER As Double = ROTATED_FIND_MODEL_X_POS + (ROTATED_FIND_MODEL_WIDTH - 1) / 2.0
        Private Const ROTATED_FIND_MODEL_Y_CENTER As Double = ROTATED_FIND_MODEL_Y_POS + (ROTATED_FIND_MODEL_HEIGHT - 1) / 2.0

        ' Minimum accuracy for the search position.
        Private Const ROTATED_FIND_MIN_POSITION_ACCURACY As Double = 0.1

        ' Minimum accuracy for the search angle.
        Private Const ROTATED_FIND_MIN_ANGLE_ACCURACY As Double = 0.25

        ' Angle range to search.
        Private Const ROTATED_FIND_ANGLE_DELTA_POS As Integer = ROTATED_FIND_ROTATION_DELTA_ANGLE
        Private Const ROTATED_FIND_ANGLE_DELTA_NEG As Integer = ROTATED_FIND_ROTATION_DELTA_ANGLE

        Private Shared Sub SearchModelAngleRangeExample(ByVal MilSystem As MIL_ID, ByVal MilDisplay As MIL_ID)
            Dim MilSourceImage As MIL_ID = MIL.M_NULL   ' Model image buffer identifier.
            Dim MilTargetImage As MIL_ID = MIL.M_NULL   ' Target image buffer identifier.
            Dim MilDisplayImage As MIL_ID = MIL.M_NULL  ' Target image buffer identifier.
            Dim GraphicList As MIL_ID = MIL.M_NULL      ' Graphic list.
            Dim MilContextId As MIL_ID = MIL.M_NULL     ' Model identifier.
            Dim MilResult As MIL_ID = MIL.M_NULL        ' Result identifier.
            Dim RealX As Double = 0.0                   ' Model real position in x.
            Dim RealY As Double = 0.0                   ' Model real position in y.
            Dim RealAngle As Double = 0.0               ' Model real angle.
            Dim X As Double = 0.0                       ' Model position in x found.
            Dim Y As Double = 0.0                       ' Model position in y found.
            Dim Angle As Double = 0.0                   ' Model angle found.
            Dim Score As Double = 0.0                   ' Model correlation score.
            Dim Time As Double = 0.0                    ' Model search time.
            Dim ErrX As Double = 0.0                    ' Model error position in x.
            Dim ErrY As Double = 0.0                    ' Model error position in y.
            Dim ErrAngle As Double = 0.0                ' Model error angle.
            Dim SumErrX As Double = 0.0                 ' Model total error position in x.
            Dim SumErrY As Double = 0.0                 ' Model total error position in y.
            Dim SumErrAngle As Double = 0.0             ' Model total error angle.
            Dim SumTime As Double = 0.0                 ' Model total search time.
            Dim NumResults As Integer = 0               ' Number of results found.
            Dim NbFound As Integer = 0                  ' Number of models found.
            Dim AnnotationColor As Double = MIL.M_COLOR_GREEN ' Drawing color.

            ' Load target image into image buffers and display it.
            MIL.MbufRestore(ROTATED_FIND_IMAGE_FILE, MilSystem, MilSourceImage)
            MIL.MbufRestore(ROTATED_FIND_IMAGE_FILE, MilSystem, MilTargetImage)
            MIL.MbufRestore(ROTATED_FIND_IMAGE_FILE, MilSystem, MilDisplayImage)
            MIL.MdispSelect(MilDisplay, MilDisplayImage)

            ' Allocate a graphic list to hold the subpixel annotations to draw.
            MIL.MgraAllocList(MilSystem, MIL.M_DEFAULT, GraphicList)

            ' Associate the graphic list to the display for annotations.
            MIL.MdispControl(MilDisplay, MIL.M_ASSOCIATED_GRAPHIC_LIST_ID, GraphicList)

            ' Allocate a normalized pattern matching context.
            MIL.MpatAlloc(MilSystem, MIL.M_NORMALIZED, MIL.M_DEFAULT, MilContextId)

            ' Define a regular model.
            MIL.MpatDefine(MilContextId, MIL.M_REGULAR_MODEL + MIL.M_CIRCULAR_OVERSCAN, MilSourceImage, _
                       ROTATED_FIND_MODEL_X_POS, ROTATED_FIND_MODEL_Y_POS, _
                       ROTATED_FIND_MODEL_WIDTH, ROTATED_FIND_MODEL_HEIGHT, MIL.M_DEFAULT)

            ' Set the search model speed.
            MIL.MpatControl(MilContextId, MIL.M_DEFAULT, MIL.M_SPEED, MIL.M_MEDIUM)

            ' Set the position search accuracy.
            MIL.MpatControl(MilContextId, MIL.M_DEFAULT, MIL.M_ACCURACY, MIL.M_HIGH)

            ' Activate the search model angle mode.
            MIL.MpatControl(MilContextId, MIL.M_DEFAULT, MIL.M_SEARCH_ANGLE_MODE, MIL.M_ENABLE)

            ' Set the search model range angle.
            MIL.MpatControl(MilContextId, MIL.M_DEFAULT, MIL.M_SEARCH_ANGLE_DELTA_NEG, ROTATED_FIND_ANGLE_DELTA_NEG)
            MIL.MpatControl(MilContextId, MIL.M_DEFAULT, MIL.M_SEARCH_ANGLE_DELTA_POS, ROTATED_FIND_ANGLE_DELTA_POS)

            ' Set the search model angle accuracy.
            MIL.MpatControl(MilContextId, MIL.M_DEFAULT, MIL.M_SEARCH_ANGLE_ACCURACY, ROTATED_FIND_MIN_ANGLE_ACCURACY)

            ' Set the search model angle interpolation mode to bilinear.
            MIL.MpatControl(MilContextId, MIL.M_DEFAULT, MIL.M_SEARCH_ANGLE_INTERPOLATION_MODE, MIL.M_BILINEAR)

            ' Preprocess the model.
            MIL.MpatPreprocess(MilContextId, MIL.M_DEFAULT, MilSourceImage)

            ' Allocate a result buffer.
            MIL.MpatAllocResult(MilSystem, MIL.M_DEFAULT, MilResult)

            ' Draw the original model position.
            MIL.MpatDraw(MIL.M_DEFAULT, MilContextId, GraphicList, MIL.M_DRAW_BOX + MIL.M_DRAW_POSITION, _
                                                                   MIL.M_DEFAULT, MIL.M_ORIGINAL)

            ' Pause to show the original image and model position.
            Console.Write(Constants.vbLf + "A {0}x{1} model was defined in the source image." + Constants.vbLf, ROTATED_FIND_MODEL_WIDTH, ROTATED_FIND_MODEL_HEIGHT)
            Console.Write("It will be searched in images rotated from {0} degree to {1} degree." + Constants.vbLf, -ROTATED_FIND_ROTATION_DELTA_ANGLE, ROTATED_FIND_ROTATION_DELTA_ANGLE)
            Console.Write("Press <Enter> to continue." + Constants.vbLf + Constants.vbLf)
            Console.ReadKey()

            ' Dummy first call for bench measure purpose only (bench stabilization, cache effect, etc...). This first call is NOT required by the application.
            MIL.MpatFind(MilContextId, MilSourceImage, MilResult)

            ' If the model was found above the acceptance threshold.
            MIL.MpatGetResult(MilResult, MIL.M_DEFAULT, MIL.M_NUMBER + MIL.M_TYPE_MIL_INT, NumResults)
            If NumResults = 1 Then
                ' Search for the model in images at different angles.
                RealAngle = ROTATED_FIND_ROTATION_DELTA_ANGLE
                Do While RealAngle >= -ROTATED_FIND_ROTATION_DELTA_ANGLE
                    ' Rotate the image from the model image to target image.
                    MIL.MimRotate(MilSourceImage, MilTargetImage, RealAngle, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_BILINEAR + MIL.M_OVERSCAN_CLEAR)

                    ' Reset the timer.
                    MIL.MappTimer(MIL.M_DEFAULT, MIL.M_TIMER_RESET + MIL.M_SYNCHRONOUS, CType(MIL.M_NULL, IntPtr))

                    ' Find the model in the target image.
                    MIL.MpatFind(MilContextId, MilTargetImage, MilResult)

                    ' Read the time spent in MpatFindModel().
                    MIL.MappTimer(MIL.M_DEFAULT, MIL.M_TIMER_READ + MIL.M_SYNCHRONOUS, Time)

                    ' Clear the annotations.
                    MIL.MgraClear(MIL.M_DEFAULT, GraphicList)

                    ' If one model was found above the acceptance threshold.
                    MIL.MpatGetResult(MilResult, MIL.M_DEFAULT, MIL.M_NUMBER + MIL.M_TYPE_MIL_INT, NumResults)
                    If NumResults = 1 Then
                        ' Read results and draw a box around model occurrence.
                        MIL.MpatGetResult(MilResult, MIL.M_DEFAULT, MIL.M_POSITION_X, X)
                        MIL.MpatGetResult(MilResult, MIL.M_DEFAULT, MIL.M_POSITION_Y, Y)
                        MIL.MpatGetResult(MilResult, MIL.M_DEFAULT, MIL.M_ANGLE, Angle)
                        MIL.MpatGetResult(MilResult, MIL.M_DEFAULT, MIL.M_SCORE, Score)

                        MIL.MgraColor(MIL.M_DEFAULT, AnnotationColor)
                        MIL.MpatDraw(MIL.M_DEFAULT, MilResult, GraphicList, MIL.M_DRAW_BOX + MIL.M_DRAW_POSITION, MIL.M_DEFAULT, MIL.M_DEFAULT)

                        MIL.MbufCopy(MilTargetImage, MilDisplayImage)

                        ' Calculate the angle error and the position errors for statistics.
                        ErrAngle = CalculateAngleDist(Angle, RealAngle)

                        RotateModelCenter(MilSourceImage, RealX, RealY, RealAngle)
                        ErrX = Math.Abs(X - RealX)
                        ErrY = Math.Abs(Y - RealY)

                        SumErrAngle += ErrAngle
                        SumErrX += ErrX
                        SumErrY += ErrY
                        SumTime += Time
                        NbFound += 1

                        ' Verify the precision for the position and the angle.
                        If (ErrX > ROTATED_FIND_MIN_POSITION_ACCURACY) OrElse (ErrY > ROTATED_FIND_MIN_POSITION_ACCURACY) OrElse (ErrAngle > ROTATED_FIND_MIN_ANGLE_ACCURACY) Then
                            Console.Write("Model accuracy error at angle {0:0.0} !" + Constants.vbLf + Constants.vbLf, RealAngle)
                            Console.Write("Errors are X:{0:0.000}, Y:{1:0.000} and Angle:{2:0.00}" + Constants.vbLf + Constants.vbLf, ErrX, ErrY, ErrAngle)
                            Console.Write("Press <Enter> to continue." + Constants.vbLf + Constants.vbLf)
                            Console.ReadKey()
                        End If
                    Else
                        Console.Write("Model was not found at angle {0:0.0} !" + Constants.vbLf + Constants.vbLf, RealAngle)
                        Console.Write("Press <Enter> to continue." + Constants.vbLf + Constants.vbLf)
                        Console.ReadKey()
                    End If

                    RealAngle -= ROTATED_FIND_ROTATION_ANGLE_STEP
                Loop

                ' Print out the search result statistics
                ' of the models found in rotated images.
                Console.Write(Constants.vbLf + "Search statistics for the model found in the rotated images." + Constants.vbLf)
                Console.Write("------------------------------------------------------------" + Constants.vbLf)
                Console.Write("The average position error is " & Constants.vbTab + Constants.vbTab & "X:{0:0.000}, Y:{1:0.000}" + Constants.vbLf, SumErrX / NbFound, SumErrY / NbFound)
                Console.Write("The average angle error is " & Constants.vbTab + Constants.vbTab & "{0:0.000}" + Constants.vbLf, SumErrAngle / NbFound)
                Console.Write("The average search time is " & Constants.vbTab + Constants.vbTab & "{0:0.000} ms" + Constants.vbLf + Constants.vbLf, SumTime * 1000.0 / NbFound)
            Else
                Console.Write("Model was not found!" + Constants.vbLf + Constants.vbLf)
            End If

            ' Wait for a key to be pressed. 
            Console.Write("Press <Enter> to continue." + Constants.vbLf + Constants.vbLf)
            Console.ReadKey()

            ' Free all allocations.
            MIL.MgraFree(GraphicList)
            MIL.MpatFree(MilResult)
            MIL.MpatFree(MilContextId)
            MIL.MbufFree(MilTargetImage)
            MIL.MbufFree(MilSourceImage)
            MIL.MbufFree(MilDisplayImage)
        End Sub


        ' Calculate the rotated center of the model to compare the accuracy with
        ' the center of the occurrence found during pattern matching.
        Private Shared Sub RotateModelCenter(ByVal Buffer As MIL_ID, ByRef X As Double, ByRef Y As Double, ByVal Angle As Double)
            Dim BufSizeX As MIL_INT = MIL.MbufInquire(Buffer, MIL.M_SIZE_X, MIL.M_NULL)
            Dim BufSizeY As MIL_INT = MIL.MbufInquire(Buffer, MIL.M_SIZE_Y, MIL.M_NULL)

            Dim RadAngle As Double = Angle * ROTATED_FIND_RAD_PER_DEG
            Dim CosAngle As Double = Math.Cos(RadAngle)
            Dim SinAngle As Double = Math.Sin(RadAngle)

            Dim OffSetX As Double = CDbl(BufSizeX - 1) / 2.0
            Dim OffSetY As Double = CDbl(BufSizeY - 1) / 2.0

            X = (ROTATED_FIND_MODEL_X_CENTER - OffSetX) * CosAngle + (ROTATED_FIND_MODEL_Y_CENTER - OffSetY) * SinAngle + OffSetX
            Y = (ROTATED_FIND_MODEL_Y_CENTER - OffSetY) * CosAngle - (ROTATED_FIND_MODEL_X_CENTER - OffSetX) * SinAngle + OffSetY
        End Sub


        ' Calculate the absolute difference between the real angle 
        ' and the angle found.
        Private Shared Function CalculateAngleDist(ByVal Angle1 As Double, ByVal Angle2 As Double) As Double
            Dim dist As Double = Math.Abs(Angle1 - Angle2)

            Do While dist >= 360.0
                dist -= 360.0
            Loop

            If dist > 180.0 Then
                dist = 360.0 - dist
            End If

            Return dist
        End Function

        '****************************************************
        ' Find the rotated model in a rotated image example. 
        '****************************************************
        Shared Sub SearchModelAtAngleExample(ByVal MilSystem As MIL_ID, ByVal MilDisplay As MIL_ID)
            Dim MilSourceImage As MIL_ID = MIL.M_NULL ' Model image buffer identifier.
            Dim MilTargetImage As MIL_ID = MIL.M_NULL ' Target image buffer identifier.
            Dim MilOverlayImage As MIL_ID = MIL.M_NULL ' Overlay image buffer identifier.
            Dim ContextId As MIL_ID = MIL.M_NULL ' Context identifier.
            Dim GraphicList As MIL_ID = MIL.M_NULL ' Graphic list.
            Dim MilResult As MIL_ID = MIL.M_NULL ' Result identifier.
            Dim Time As Double = 0.0 ' Model search time.
            Dim NbFound As MIL_INT = 0 ' Number of models found.
            Dim AnnotationColor As Double = MIL.M_COLOR_GREEN ' Drawing color.

            ' Load the source image and display it.
            MIL.MbufRestore(ROTATED_FIND_IMAGE_FILE, MilSystem, MilSourceImage)
            MIL.MdispSelect(MilDisplay, MilSourceImage)

            ' Allocate the target image.
            MIL.MbufAlloc2d(MilSystem, MIL.MbufInquire(MilSourceImage, MIL.M_SIZE_X, MIL.M_NULL), _
                MIL.MbufInquire(MilSourceImage, MIL.M_SIZE_Y, MIL.M_NULL), _
                8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_PROC + MIL.M_DISP, MilTargetImage)

            ' Allocate a graphic list to hold the subpixel annotations to draw.
            MIL.MgraAllocList(MilSystem, MIL.M_DEFAULT, GraphicList)

            ' Associate the graphic list to the display for annotations.
            MIL.MdispControl(MilDisplay, MIL.M_ASSOCIATED_GRAPHIC_LIST_ID, GraphicList)

            ' Allocate a normalized grayscale model.
            MIL.MpatAlloc(MilSystem, MIL.M_NORMALIZED, MIL.M_DEFAULT, ContextId)

            ' Define the rotated model at -10 degrees from a child image.
            MIL.MpatDefine(ContextId, MIL.M_REGULAR_MODEL, MilSourceImage, _
                ROTATED_FIND_MODEL_X_POS, ROTATED_FIND_MODEL_Y_POS, _
                ROTATED_FIND_MODEL_WIDTH, ROTATED_FIND_MODEL_HEIGHT, MIL.M_DEFAULT)

            ' Activate the search model angle mode.
            MIL.MpatControl(ContextId, MIL.M_DEFAULT, MIL.M_SEARCH_ANGLE_MODE, MIL.M_ENABLE)

            ' Disable the search model range angle.
            MIL.MpatControl(ContextId, MIL.M_DEFAULT, MIL.M_SEARCH_ANGLE_DELTA_NEG, 0)
            MIL.MpatControl(ContextId, MIL.M_DEFAULT, MIL.M_SEARCH_ANGLE_DELTA_POS, 0)

            ' Set a specific angle.
            MIL.MpatControl(ContextId, 0, MIL.M_SEARCH_ANGLE, ROTATED_FIND_ROTATION_DELTA_ANGLE)

            ' Preprocess the model.
            MIL.MpatPreprocess(ContextId, MIL.M_DEFAULT, MilSourceImage)

            ' Allocate a result buffer.
            MIL.MpatAllocResult(MilSystem, MIL.M_DEFAULT, MilResult)

            ' Draw the original model position.
            MIL.MpatDraw(MIL.M_DEFAULT, ContextId, GraphicList, MIL.M_DRAW_BOX + MIL.M_DRAW_POSITION, _
                     MIL.M_DEFAULT, MIL.M_ORIGINAL)

            ' Pause to show the original image and model position.
            Console.Write(Constants.vbLf + "A {0}x{1} model was defined in the source image." + Constants.vbLf, _
                      ROTATED_FIND_MODEL_WIDTH, ROTATED_FIND_MODEL_HEIGHT)
            Console.Write("It will be searched in an image rotated at {0} degrees." + Constants.vbLf, _
                      -ROTATED_FIND_ROTATION_DELTA_ANGLE)

            Console.Write("Press <Enter> to continue." & Constants.vbLf)
            Console.ReadKey()

            ' Rotate the source image -10 degrees.
            MIL.MimRotate(MilSourceImage, MilTargetImage, ROTATED_FIND_ROTATION_DELTA_ANGLE, MIL.M_DEFAULT, _
                MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_DEFAULT, MIL.M_BILINEAR + MIL.M_OVERSCAN_CLEAR)

            MIL.MdispSelect(MilDisplay, MilTargetImage)

            ' Dummy first call for bench measure purpose only (bench stabilization, 
            ' cache effect, etc...). This first call is NOT required by the application.
            MIL.MpatFind(ContextId, MilTargetImage, MilResult)

            ' Reset the timer.
            MIL.MappTimer(MIL.M_DEFAULT, MIL.M_TIMER_RESET + MIL.M_SYNCHRONOUS, CType(MIL.M_NULL, IntPtr))

            MIL.MpatFind(ContextId, MilTargetImage, MilResult)

            ' Read the time spent in MpatFind().
            MIL.MappTimer(MIL.M_DEFAULT, MIL.M_TIMER_READ + MIL.M_SYNCHRONOUS, Time)

            ' Clear the annotations.
            MIL.MgraClear(MIL.M_DEFAULT, GraphicList)

            MIL.MpatGetResult(MilResult, MIL.M_DEFAULT, MIL.M_NUMBER + MIL.M_TYPE_MIL_INT, NbFound)
            If NbFound = 1 Then
                MIL.MgraColor(MIL.M_DEFAULT, AnnotationColor)
                MIL.MpatDraw(MIL.M_DEFAULT, MilResult, GraphicList, MIL.M_DRAW_BOX + MIL.M_DRAW_POSITION, MIL.M_DEFAULT, MIL.M_DEFAULT)
                Console.Write("A search model at a specific angle has been found in the rotated image." & Constants.vbLf)
                Console.Write("The search time is {0:F3} ms." & Constants.vbLf + Constants.vbLf, Time * 1000.0)
            Else
                Console.Write("Model was not found!" & Constants.vbLf + Constants.vbLf)
            End If

            Console.Write("Press <Enter> to continue." & Constants.vbLf + Constants.vbLf)
            Console.ReadKey()

            ' Disable the overlay display.
            MIL.MdispControl(MilDisplay, MIL.M_OVERLAY_SHOW, MIL.M_DISABLE)

            ' Free all allocations.
            MIL.MpatFree(MilResult)
            MIL.MpatFree(ContextId)
            MIL.MgraFree(GraphicList)
            MIL.MbufFree(MilTargetImage)
            MIL.MbufFree(MilSourceImage)
        End Sub

        '*****************************************************************************
        ' Automatic model allocation example.

        ' Source and target images file specifications.
        Private Const AUTO_MODEL_IMAGE_FILE As String = MIL.M_IMAGE_PATH & "Wafer.mim"
        Private Const AUTO_MODEL_TARGET_IMAGE_FILE As String = MIL.M_IMAGE_PATH & "WaferShifted.mim"

        ' Model width and height
        Private Const AUTO_MODEL_WIDTH As Integer = 64
        Private Const AUTO_MODEL_HEIGHT As Integer = 64

        Private Shared Sub AutoAllocationModelExample(ByVal MilSystem As MIL_ID, ByVal MilDisplay As MIL_ID)
            Dim MilImage As MIL_ID = MIL.M_NULL     ' Image buffer identifier.
            Dim MilSubImage As MIL_ID = MIL.M_NULL  ' Sub-image buffer identifier.
            Dim GraphicList As MIL_ID = MIL.M_NULL  ' Graphic list.
            Dim ContextId As MIL_ID = MIL.M_NULL    ' Model identifier.
            Dim Result As MIL_ID = MIL.M_NULL       ' Result buffer identifier.
            Dim AllocError As MIL_INT = 0           ' Allocation error variable.
            Dim NumResults As MIL_INT = 0           ' Number of results found.
            Dim ImageWidth As MIL_INT = 0           ' Target image dimensions
            Dim ImageHeight As MIL_INT = 0
            Dim OrgX As Double = 0.0                ' Original center of model.
            Dim OrgY As Double = 0.0
            Dim x As Double = 0.0                   ' Result variables.
            Dim y As Double = 0.0
            Dim Score As Double = 0.0
            Dim AnnotationColor As Double = MIL.M_COLOR_GREEN ' Drawing color.

            ' Load model image into an image buffer.
            MIL.MbufRestore(AUTO_MODEL_IMAGE_FILE, MilSystem, MilImage)

            ' Display the image.
            MIL.MdispSelect(MilDisplay, MilImage)

            ' Allocate a graphic list to hold the subpixel annotations to draw.
            MIL.MgraAllocList(MilSystem, MIL.M_DEFAULT, GraphicList)

            ' Associate the graphic list to the display for annotations.
            MIL.MdispControl(MilDisplay, MIL.M_ASSOCIATED_GRAPHIC_LIST_ID, GraphicList)

            ' Restrict the region to be processed to the bottom right corner of the image.
            MIL.MbufInquire(MilImage, MIL.M_SIZE_X, ImageWidth)
            MIL.MbufInquire(MilImage, MIL.M_SIZE_Y, ImageHeight)
            MIL.MbufChild2d(MilImage, ImageWidth / 2, ImageHeight / 2, ImageWidth / 2, ImageHeight / 2, MilSubImage)

            ' Add an offset to the drawings so they are aligned with the processed child image.
            MIL.MgraControl(MIL.M_DEFAULT, MIL.M_DRAW_OFFSET_X, CType(-(ImageWidth / 2), Double))
            MIL.MgraControl(MIL.M_DEFAULT, MIL.M_DRAW_OFFSET_Y, CType(-(ImageHeight / 2), Double))

            ' Automatically allocate a normalized grayscale type pattern matching context.
            MIL.MpatAlloc(MilSystem, MIL.M_NORMALIZED, MIL.M_DEFAULT, ContextId)

            ' Define a unique model
            MIL.MpatDefine(ContextId, MIL.M_AUTO_MODEL, MilSubImage, MIL.M_DEFAULT, MIL.M_DEFAULT, _
                       AUTO_MODEL_WIDTH, AUTO_MODEL_HEIGHT, MIL.M_DEFAULT)

            ' Set the search accuracy to high.
            MIL.MpatControl(ContextId, MIL.M_DEFAULT, MIL.M_ACCURACY, MIL.M_HIGH)

            ' Check for that model allocation was successful.
            MIL.MappGetError(MIL.M_DEFAULT, MIL.M_CURRENT, AllocError)
            If AllocError = 0 Then
                ' Draw a box around the model.
                MIL.MgraColor(MIL.M_DEFAULT, AnnotationColor)
                MIL.MpatDraw(MIL.M_DEFAULT, ContextId, GraphicList, MIL.M_DRAW_BOX + MIL.M_DRAW_POSITION, _
                                                                  MIL.M_DEFAULT, MIL.M_ORIGINAL)
                Console.Write("A model was automatically defined in the image." + Constants.vbLf)
                Console.Write("Press <Enter> to continue." + Constants.vbLf + Constants.vbLf)
                Console.ReadKey()

                ' Clear the annotations.
                MIL.MgraClear(MIL.M_DEFAULT, GraphicList)

                ' Load target image into an image buffer.
                MIL.MbufLoad(AUTO_MODEL_TARGET_IMAGE_FILE, MilImage)

                ' Allocate result.
                MIL.MpatAllocResult(MilSystem, MIL.M_DEFAULT, Result)

                ' Preprocess the model.
                MIL.MpatPreprocess(ContextId, MIL.M_DEFAULT, MilSubImage)

                ' Find model.
                MIL.MpatFind(ContextId, MilSubImage, Result)

                ' If one model was found above the acceptance threshold set.
                MIL.MpatGetResult(Result, MIL.M_DEFAULT, MIL.M_NUMBER + MIL.M_TYPE_MIL_INT, NumResults)
                If NumResults = 1 Then
                    ' Get results.
                    MIL.MpatGetResult(Result, MIL.M_DEFAULT, MIL.M_POSITION_X, x)
                    MIL.MpatGetResult(Result, MIL.M_DEFAULT, MIL.M_POSITION_Y, y)
                    MIL.MpatGetResult(Result, MIL.M_DEFAULT, MIL.M_SCORE, Score)

                    ' Draw a box around the occurrence.
                    MIL.MgraColor(MIL.M_DEFAULT, AnnotationColor)
                    MIL.MpatDraw(MIL.M_DEFAULT, Result, GraphicList, MIL.M_DRAW_BOX + MIL.M_DRAW_POSITION, MIL.M_DEFAULT, MIL.M_DEFAULT)

                    ' Analyze and print results.
                    MIL.MpatInquire(ContextId, MIL.M_DEFAULT, MIL.M_ORIGINAL_X, OrgX)
                    MIL.MpatInquire(ContextId, MIL.M_DEFAULT, MIL.M_ORIGINAL_Y, OrgY)
                    Console.Write("An image misaligned by 50 pixels in X and 20 pixels in Y was loaded." + Constants.vbLf + Constants.vbLf)
                    Console.Write("The image is found to be shifted by {0:0.00} in X, and {1:0.00} in Y." + Constants.vbLf, x - OrgX, y - OrgY)
                    Console.Write("Model match score is {0:0.0} percent." + Constants.vbLf, Score)
                    Console.Write("Press <Enter> to end." + Constants.vbLf + Constants.vbLf)
                    Console.ReadKey()
                Else
                    Console.Write("Error: Pattern not found properly." + Constants.vbLf)
                    Console.Write("Press <Enter> to end." + Constants.vbLf + Constants.vbLf)
                    Console.ReadKey()
                End If

                ' Free result buffer and model.
                MIL.MpatFree(Result)
                MIL.MpatFree(ContextId)
            Else
                Console.Write("Error: Automatic model definition failed." + Constants.vbLf)
                Console.Write("Press <Enter> to end." + Constants.vbLf + Constants.vbLf)
                Console.ReadKey()
            End If

            ' Remove the drawing offset.
            MIL.MgraControl(MIL.M_DEFAULT, MIL.M_DRAW_OFFSET_X, 0.0)
            MIL.MgraControl(MIL.M_DEFAULT, MIL.M_DRAW_OFFSET_Y, 0.0)

            ' Free the graphic list.
            MIL.MgraFree(GraphicList)

            ' Free child buffer and defaults.
            MIL.MbufFree(MilSubImage)
            MIL.MbufFree(MilImage)
        End Sub
    End Class
End Namespace