Imports Luxand
Imports System.Drawing.Drawing2D
Public Class MainForm
    Dim cameraName As String
    Dim needClose As Boolean
    Dim userName As String
    Const TrackerMemoryFile = "tracker70.dat"
    Dim mouseX As Integer = 0
    Dim mouseY As Integer = 0

    ' Mouse coordinates in the original image coordinate system
    Dim mouseImgX As Integer = 0
    Dim mouseImgY As Integer = 0

    ' ID of the face selected by the user
    Dim selectedFaceId As Integer = -1

    ' Store the current frame for drawing
    Private currentFrame As Image = Nothing

    Dim tracker As Luxand.Tracker = Nothing

    ' If the FaceSDK library is activated
    Shared isActivated As Boolean = False

    Public Sub New()
        InitializeComponent()
        ' Subscribe to Paint event
        AddHandler PictureBox1.Paint, AddressOf PictureBox1_Paint
        ' Stretch pictureBox1 to fill the entire window
        PictureBox1.Dock = DockStyle.Fill
        ' Allow the window to be freely resizable
        Me.MinimumSize = New Size(640, 480)
    End Sub

    Private Sub MainForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        If Not isActivated AndAlso FSDK.FSDKE_OK <> FSDK.ActivateLibrary("INSERT THE LICENSE KEY HERE") Then
            MessageBox.Show("Please insert the license key in the FSDK.ActivateLibrary()", "Error activating FaceSDK", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Application.Exit()
            Return
        End If
        isActivated = True

        FSDK.InitializeLibrary()
        Luxand.Camera.InitializeCapturing()

        Dim cameraList() As String = FSDK.GetCameraList()

        If cameraList.Length = 0 Then
            MessageBox.Show("Please attach a camera", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Application.Exit()
            Return
        End If
        cameraName = cameraList(0)

        Dim formatList() As FSDK.VideoFormatInfo = Luxand.Camera.GetVideoFormatList(cameraName)

        ' Choose the best camera from the list
        Dim index As Integer = 0
        Dim formatIndex As Integer = 0
        Dim max_height As Integer = 0
        Dim max_width As Integer = 0
        For Each f In formatList
            If f.Width > max_width AndAlso f.Height > max_height Then
                max_width = f.Width
                max_height = f.Height
                formatIndex = index
            End If
            index += 1
        Next

        Luxand.Camera.SetVideoFormat(cameraName, formatList(formatIndex))

        StartBtn.Enabled = True
    End Sub
    Private Sub MainForm_FormClosing(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing
        needClose = True
    End Sub

    ' Converts mouse coordinates from pictureBox1 to image coordinates
    Private Sub UpdateMouseImageCoords()
        If currentFrame Is Nothing Then
            mouseImgX = 0
            mouseImgY = 0
            Return
        End If
        Dim imgW As Integer = currentFrame.Width
        Dim imgH As Integer = currentFrame.Height
        Dim boxW As Integer = PictureBox1.Width
        Dim boxH As Integer = PictureBox1.Height
        Dim ratio As Single = Math.Min(CSng(boxW) / imgW, CSng(boxH) / imgH)
        Dim drawW As Integer = CInt(imgW * ratio)
        Dim drawH As Integer = CInt(imgH * ratio)
        Dim offsetX As Integer = (boxW - drawW) \ 2
        Dim offsetY As Integer = (boxH - drawH) \ 2
        ' If the mouse is outside the image, coordinates are invalid
        If mouseX < offsetX OrElse mouseX >= offsetX + drawW OrElse mouseY < offsetY OrElse mouseY >= offsetY + drawH Then
            mouseImgX = -1
            mouseImgY = -1
        Else
            mouseImgX = CInt((mouseX - offsetX) / ratio)
            mouseImgY = CInt((mouseY - offsetY) / ratio)
        End If
    End Sub

    Private Sub CheckFaceBtn_Click(sender As Object, e As EventArgs) Handles CheckFaceBtn.Click
        Dim fileContent As String = String.Empty
        Dim filePath As String = String.Empty

        Using openFileDialog As New OpenFileDialog()
            openFileDialog.Filter = "image files (*.jpg;*.png)|*.jpg;*.png"
            openFileDialog.FilterIndex = 1
            openFileDialog.RestoreDirectory = True

            If openFileDialog.ShowDialog() <> DialogResult.OK Then
                Return
            End If

            'Get the path of specified file
            filePath = openFileDialog.FileName
        End Using

        Dim image As Luxand.CImage
        Try
            image = New Luxand.CImage(filePath)
        Catch ex2 As Exception
            MessageBox.Show(ex2.Message, "Error opening image file.")
            Return
        End Try

        Dim facePos As FSDK.TFacePosition
        Try
            facePos = image.DetectFace()
        Catch
            ' No faces found in the image
            MessageBox.Show("No faces found", "The image does not contain any detectable faces.")
            Return
        End Try

        Dim faceTemplate() As Byte = New Byte(FSDK.TemplateSize - 1) {}
        Try
            faceTemplate = image.GetFaceTemplateInRegion(facePos)
        Catch ex As Exception
            MessageBox.Show("Error extracting face template", $"Could not extract face template: {ex.Message}")
            Return
        End Try

        Dim matchThreshold As Single = 0.1F
        Dim id_similarity() As FSDK.IDSimilarity = tracker.MatchFaces(faceTemplate, matchThreshold)
        If id_similarity Is Nothing OrElse id_similarity.Length = 0 Then
            MessageBox.Show("No matches found", $"No faces found above threshold {matchThreshold}")
            Return
        End If

        Dim res As String = "The possible person(s):" & vbLf
        For Each idSim In id_similarity
            res &= vbLf & $"ID: {idSim.ID}"
            Dim name As String = ""
            Try
                name = tracker.GetName(idSim.ID)
            Catch
            End Try
            If name.Length <> 0 Then
                res &= $" ({name})"
            End If
            res &= $"; Similarity: {idSim.Similarity}"
        Next

        MessageBox.Show(res, "Face Recognition Results")
    End Sub

    Private Sub StartBtn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles StartBtn.Click
        StartBtn.Enabled = False
        needClose = False

        ' Initialize camera
        Dim camera As Luxand.Camera = Nothing
        Try
            camera = New Luxand.Camera(cameraName)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error opening camera")
            Application.Exit()
        End Try

        ' Creating tracker
        Try
            tracker = Luxand.Tracker.LoadMemoryFromFile(TrackerMemoryFile)
        Catch
            tracker = New Luxand.Tracker()
        End Try

        If tracker Is Nothing Then
            MessageBox.Show("Error creating tracker", "Error")
            Application.Exit()
        End If

        ' set realtime face detection parameters
        Dim errorPosition As Integer = 0
        tracker.SetMultipleParameters("HandleArbitraryRotations=false;DetermineFaceRotationAngle=false; InternalResizeWidth=100; FaceDetectionThreshold=5;", errorPosition)

        tracker.SetParameter("DetectLiveness", "true")
        tracker.SetParameter("LivenessFramesCount", "6")
        tracker.SetParameter("SmoothAttributeLiveness", "true")

        Me.CheckFaceBtn.Enabled = True

        While Not needClose
            Dim frame As CImage = camera.GrabFrame()
            If frame Is Nothing Then
                Application.DoEvents()
                Continue While
            End If

            Dim frameImage As Image = frame.ToCLRImage()

            ' Process the frame with the tracker
            Dim faceIds() As Long = Nothing
            tracker.FeedFrame(frame, faceIds)

            ' Make UI controls accessible (to find if the user clicked on a face)
            Application.DoEvents()

            If faceIds IsNot Nothing Then
                Dim gr As Graphics = Graphics.FromImage(frameImage)

                For Each faceId In faceIds
                    ' Get the face position and create an ellipse for it
                    Dim facePos As FSDK.TFacePosition
                    Try
                        facePos = tracker.GetFacePosition(0, faceId)
                    Catch
                        Continue For
                    End Try

                    ' Load face name
                    Dim facename As String = ""
                    Try
                        facename = tracker.GetName(faceId)
                    Catch
                    End Try

                    Dim w As Single = facePos.w
                    Dim h As Single = facePos.w * 1.4F

                    Dim left As Integer = CInt(facePos.xc - w * 0.5)
                    Dim top As Integer = CInt(facePos.yc - w * 0.7F)
                    Dim right As Integer = CInt(facePos.xc + w * 0.5)
                    Dim bottom As Integer = CInt(facePos.yc + w * 0.7F)

                    ' Get liveness
                    Dim liveness As Single = 0.0F
                    Dim livenessText As String = ""

                    Try
                        Dim livenessAttribute As String = ""
                        tracker.GetFacialAttribute(faceId, "Liveness", livenessAttribute)
                        If livenessAttribute IsNot Nothing AndAlso livenessAttribute <> "" Then
                            FSDK.GetValueConfidence(livenessAttribute, "Liveness", liveness)
                        End If
                    Catch
                    End Try

                    Dim pen As Pen = Pens.LightGreen

                    If liveness > 0.0F Then
                        If liveness < 0.5F Then
                            ' If liveness is low, mark the face as suspicious
                            pen = Pens.Red
                        Else
                            livenessText = $"Liveness: {liveness * 100.0F:0.00}%"
                        End If
                    End If

                    Dim labelText As String = facename
                    If facename <> "" AndAlso livenessText <> "" Then
                        labelText &= $" ({livenessText})"
                    End If

                    selectedFaceId = -1

                    ' Check if the mouse is inside the face bounding box considering scaling
                    UpdateMouseImageCoords()

                    If mouseImgX >= 0 AndAlso mouseImgY >= 0 AndAlso
                       mouseImgX >= left AndAlso mouseImgX <= right AndAlso
                       mouseImgY >= top AndAlso mouseImgY <= bottom Then
                        ' Highlight the face if the mouse is over it
                        pen = Pens.Blue
                        selectedFaceId = CInt(faceId)
                    End If

                    gr.DrawEllipse(pen, left, top, w, h)

                    ' Draw name
                    If labelText <> "" Then
                        Dim format As New StringFormat()
                        format.Alignment = StringAlignment.Center

                        gr.DrawString(labelText, New System.Drawing.Font("Arial", 12, FontStyle.Bold),
                            New System.Drawing.SolidBrush(pen.Color),
                            facePos.xc, bottom, format)
                    End If
                Next
            End If

            ' Save the current frame and redraw pictureBox1
            If currentFrame IsNot Nothing Then
                Dim old As Image = currentFrame
                currentFrame = Nothing
                old.Dispose()
            End If

            currentFrame = frameImage
            PictureBox1.Invalidate()
            frame.Dispose()
            GC.Collect()
        End While

        tracker.SaveMemoryToFile(TrackerMemoryFile)
        camera.Close()

        tracker.Dispose()
        tracker = Nothing

        FSDK.FinalizeCapturing()
        FSDK.FinalizeLibrary()
    End Sub

    ' Draw the frame with aspect ratio preserved
    Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs)
        If currentFrame Is Nothing Then
            Return
        End If

        Dim pb As PictureBox = PictureBox1
        Dim g As Graphics = e.Graphics
        g.Clear(pb.BackColor)

        Dim imgW As Integer = currentFrame.Width
        Dim imgH As Integer = currentFrame.Height
        Dim boxW As Integer = pb.Width
        Dim boxH As Integer = pb.Height

        Dim ratio As Single = Math.Min(CSng(boxW) / imgW, CSng(boxH) / imgH)
        Dim drawW As Integer = CInt(imgW * ratio)
        Dim drawH As Integer = CInt(imgH * ratio)
        Dim offsetX As Integer = (boxW - drawW) \ 2
        Dim offsetY As Integer = (boxH - drawH) \ 2

        g.DrawImage(currentFrame, New Rectangle(offsetX, offsetY, drawW, drawH))
    End Sub

    Private Sub PictureBox1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp
        If selectedFaceId >= 0 Then
            ' If the user clicked on a face, show the name input dialog
            userName = InputBox("Your name:", "Enter your name")
            tracker.SetName(selectedFaceId, userName)

            If userName Is Nothing OrElse userName.Length <= 0 Then
                tracker.PurgeID(selectedFaceId)
            End If
            selectedFaceId = -1 ' Reset selected face ID
        End If
    End Sub

    Private Sub PictureBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
        mouseX = e.X
        mouseY = e.Y
    End Sub

    Private Sub PictureBox1_MouseLeave(ByVal sender As Object, ByVal e As System.EventArgs) Handles PictureBox1.MouseLeave
        mouseX = 0
        mouseY = 0
    End Sub
End Class

