C# Emgu CV Gesichtserkennung

In diesem Beitrag zeige ich euch wie man in Visual Studio mit C# und Emgu CV eine Gesichtserkennungssoftware programmieren kann.

Dieses Programm kann nicht nur Gesichter erkennen, sondern es kann auch Gesichter wiedererkennen. Dieses wird oftmals verwechselt, aber es sind zwei unterschiedliche Dinge. So können am Ende mit dem Programm Gesichter erkannt werden und mit einem Namen versehen werden, damit sie danach vom Programm wiedererkannt werden.

Bitte das fertige Projekt hier herunterladen: Win_FaceDetection.zip und danach entpacken.

Als IDE verwende ich Visual Studio Community 2019. Bei der initialen Installation sollte das Installationspaket „.NET-Desktopentwicklung“ ausgewählt werden, da dieses Projekt auf .NET Framework basiert.

Um das Projekt zu öffnen bitte die „Win_FaceDetection.sln“ Datei anklicken. Danach sollte sich Visual Studio starten und dieses Projekt laden.

!!! Wichtig !!! Nach dem öffnen des Projektes müssen noch 3 NuGet Pakete installiert werden. Dabei ist es egal ob man sich die Projektvorlage herunterlädt oder ein ganz neues Projekt erstellen möchte. Zum Installieren einfach in der Menüleiste Projekt -> NuGet Pakete verwalten… klicken.

Im Durchsuchen Tab, des neu geöffneten Fensters nach „emgu cv“ suchen und folgende Pakete installieren(falls die Pakete schon als „Installiert“ angezeigt werden, bitte diese „Deinstallieren“ und danach wieder installieren):

Zum Debuggen und Kompilieren bitte die „x64“ Plattform auswählen.

Hier noch der gesamte Quellcode, falls man selber ein neues C# Projekt anlegen will.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.CV.CvEnum;
using Emgu.CV.Util;

using System.IO;


namespace Win_FaceDetection
{
    public partial class frmFaceDetection : Form
    {
        Image<Bgr, byte> img;
        VideoCapture vid;
        int indexofSelectedFace = 0;
        FaceRecognizer recognizer = new EigenFaceRecognizer(80, double.PositiveInfinity);
        Bitmap[] detectedFaces;
        string[] faceNames;
        List<int> faceIDs = new List<int>();

        public frmFaceDetection()
        {
            InitializeComponent();

            stopToolStripMenuItem.Enabled = false;
            buttonZurueck.Enabled = false;
            buttonVor.Enabled = false;
            buttonVerwerfen.Enabled = false;
            buttonHinzufuegen.Enabled = false;
        }

        private void drawText(Image<Bgr, Byte> img, Rectangle rect, string text)
        {
            Bgr myTextColor = new Bgr(0, 255, 0);
            FontFace fontFacei = new FontFace();
            Point pointi = new Point();
            pointi.X = rect.Left + 18;
            pointi.Y = rect.Top + 38;
            int thickness = 2;
            LineType lineTypyi = new LineType();
            img.Draw(text, pointi, fontFacei, 1, myTextColor, thickness, lineTypyi);
        }
        public void GesichterErkennen()
        {
            Rectangle[] faceRectangles = null;
            detectedFaces = null;
            string facePath = Path.GetFullPath(@"data/haarcascade_frontalface_default.xml");
            CascadeClassifier classifierFace = new CascadeClassifier(facePath);
            var imgGray = img.Convert<Gray, byte>().Clone();
            faceRectangles = classifierFace.DetectMultiScale(imgGray, 1.1, 4);
            detectedFaces = new Bitmap[faceRectangles.Length];

            for (int i = 0; i < faceRectangles.Length; i++)
            {
                img.Draw(faceRectangles[i], new Bgr(255, 0, 0), 1);
                imgGray.ROI = faceRectangles[i];
                var detectedFace = imgGray.Copy();
                detectedFaces[i] = detectedFace.Resize(200, 200, Inter.Linear).ToBitmap();
            }
            pictureBoxInput.Image = img.ToBitmap();
        }

        public void GesichterWiedererkennen()
        {
            Rectangle[] faceRectangles = null;

            string facePath = Path.GetFullPath(@"data/haarcascade_frontalface_default.xml");
            CascadeClassifier classifierFace = new CascadeClassifier(facePath);
            var imgGray = img.Convert<Gray, byte>().Clone();
            faceRectangles = classifierFace.DetectMultiScale(imgGray, 1.1, 4);

            for (int i = 0; i < faceRectangles.Length; i++)
            {
                int foundfaceID = 0;
                imgGray.ROI = faceRectangles[i];
                var detectedFace = imgGray.Copy();
                detectedFace = detectedFace.Resize(200, 200, Inter.Linear);
                var result = recognizer.Predict(detectedFace);
                /*Die Dictance ist ein Wert, der angibt wie viel Ähnlichkeit das
                  aktuelle Gesicht mit allen anderen Gesichtern in der Datenbank
                  hat. Je kleiner der Wert desto ähnlicher ist das aktuelle Gesicht
                  zu einem anderem Gesicht in der Datenbank.*/
                var distance = result.Distance;
                foundfaceID = result.Label;

                if (distance < 3000)
                {
                    string foundfaceName = "";
                    int j = 0;
                    foreach (var faceID in faceIDs)
                    {
                        if (faceID == foundfaceID)
                        {
                            foundfaceName = faceNames[j];
                            break;
                        }
                        j++;
                    }
                    /*Methode drawText wird aufgerufen um den Namen des 
                    wiedererkannten Gesichts in die PictureBox zu schreiben.*/
                    drawText(img, faceRectangles[i], foundfaceName);
                    img.Draw(faceRectangles[i], new Bgr(0, 255, 0), 1);
                }
            }
            pictureBoxInput2.Image = img.ToBitmap();
        }

        private void öffnenToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "Image files (*.jpg, *.jpeg, *.jpe, *.jfif, *.png)" +
                " | *.jpg; *.jpeg; *.jpe; *.jfif; *.png";
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                img = new Image<Bgr, byte>(ofd.FileName);
                int index = tabControl1.SelectedIndex;

                //"Wiedererkennen" Tab im Fokus
                if (index == 1)
                {
                    pictureBoxInput2.Image = img.ToBitmap();
                    Trainieren();
                    GesichterWiedererkennen();
                }
                //"Erkennen" Tab im Fokus
                else
                {
                    pictureBoxInput.Image = img.ToBitmap();
                    GesichterErkennen();
                }
            }
        }

        private void startToolStripMenuItem_Click(object sender, EventArgs e)
        {
            startToolStripMenuItem.Enabled = false;
            öffnenToolStripMenuItem.Enabled = false;
            stopToolStripMenuItem.Enabled = true;

            try
            {
                vid = new VideoCapture();
                vid.FlipHorizontal = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

            int index = tabControl1.SelectedIndex;
            //"Wiedererkennen" Tab im Fokus
            if (index == 1)
            {
                Trainieren();
                Application.Idle += ProcessFrameWiedererkennen;
            }
            //"Erkennen" Tab im Fokus
            else
            {
                Application.Idle += ProcessFrameErkennen;
            }
        }

        private void ProcessFrameWiedererkennen(object sender, EventArgs e)
        {
            img = vid.QueryFrame().ToImage<Bgr, byte>();
            GesichterWiedererkennen();
        }

        private void ProcessFrameErkennen(object sender, EventArgs e)
        {
            img = vid.QueryFrame().ToImage<Bgr, byte>();
            GesichterErkennen();
        }

        private void stopToolStripMenuItem_Click(object sender, EventArgs e)
        {
            startToolStripMenuItem.Enabled = true;
            öffnenToolStripMenuItem.Enabled = true;
            stopToolStripMenuItem.Enabled = false;
            Application.Idle -= ProcessFrameWiedererkennen;
            Application.Idle -= ProcessFrameErkennen;
            vid.Dispose();
        }


        private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
        {
            /*Wenn man bei einer laufenden Videoaufnahme den Tab wechselt, 
              wird die Videoaufnahme gestoppt.*/
            stopToolStripMenuItem.PerformClick();
        }

        private void buttonExtrahieren_Click(object sender, EventArgs e)
        {
            if (pictureBoxInput.Image != null)
            {
                buttonExtrahieren.Enabled = false;
                buttonVerwerfen.Enabled = true;
                buttonHinzufuegen.Enabled = true;
                //Wenn Videostream noch läuft, wird dieser angehalten.
                stopToolStripMenuItem.PerformClick();
                /*Erstes erkanntes Gesicht wird in 
                  die kleine PictureBox geladen.*/
                if (detectedFaces.Length > 0)
                {
                    if (detectedFaces.Length > 1)
                    {
                        buttonVor.Enabled = true;
                    }
                    pictureBoxFace.Image = detectedFaces[0];
                }
            }
        }

        private void buttonVerwerfen_Click(object sender, EventArgs e)
        {
            /*Verworfenes Bild aus dem Bitmap Array entfernen und 
              das nächste Bild einblenden lassen.*/
            textBoxName.Text = "";
            for (int i = indexofSelectedFace; i < detectedFaces.Length - 1; i++)
            {
                detectedFaces[i] = detectedFaces[i + 1];
            }
            detectedFaces = detectedFaces.Reverse().Skip(1).Reverse().ToArray();
            if (detectedFaces.Length - 1 > 0)
            {
                buttonZurueck.Enabled = false;
                pictureBoxFace.Image = detectedFaces[0];
                indexofSelectedFace = 0;
            }
            else if (detectedFaces.Length - 1 == 0)
            {
                buttonVor.Enabled = false;
                buttonZurueck.Enabled = false;
                pictureBoxFace.Image = detectedFaces[0];
                indexofSelectedFace = 0;
            }
            else
            {
                buttonExtrahieren.Enabled = true;
                buttonVerwerfen.Enabled = false;
                buttonHinzufuegen.Enabled = false;
                pictureBoxFace.Image = null;
                pictureBoxInput.Image = null;
            }
        }

        private void buttonHinzufuegen_Click(object sender, EventArgs e)
        {
            if (textBoxName.Text == "")
            {
                MessageBox.Show("Keinen Namen vergeben.");
            }
            else
            {
                //Abspeichern des ausgewählten Gesichts
                Random rand = new Random(DateTime.Now.Millisecond);
                int random = Math.Abs(rand.Next() * 1000);
                pictureBoxFace.Image.Save(@"Face Collection\Face_" + (textBoxName.Text) + "_" + random + ".bmp");


                /*Abgespeichertes Bild aus dem Bitmap Array entfernen und 
                  das nächste Bild einblenden lassen.*/
                textBoxName.Text = "";
                for (int i = indexofSelectedFace; i < detectedFaces.Length-1; i++)
                {
                    detectedFaces[i] = detectedFaces[i + 1];
                }
                detectedFaces = detectedFaces.Reverse().Skip(1).Reverse().ToArray();
                if (detectedFaces.Length - 1 > 0)
                {
                    buttonZurueck.Enabled = false;
                    pictureBoxFace.Image = detectedFaces[0];
                    indexofSelectedFace = 0;  
                }
                else if (detectedFaces.Length - 1 == 0)
                {
                    buttonVor.Enabled = false;
                    buttonZurueck.Enabled = false;
                    pictureBoxFace.Image = detectedFaces[0];
                    indexofSelectedFace = 0;
                }
                else
                {
                    buttonExtrahieren.Enabled = true;
                    buttonVerwerfen.Enabled = false;
                    buttonHinzufuegen.Enabled = false;
                    pictureBoxFace.Image = null;
                    pictureBoxInput.Image = null;
                }
            }
        }

        private void buttonZurueck_Click(object sender, EventArgs e)
        {
            indexofSelectedFace--;
            if (indexofSelectedFace == 0)
            {
                buttonZurueck.Enabled = false;
                buttonVor.Enabled = true;
                pictureBoxFace.Image = detectedFaces[indexofSelectedFace];
            }
            else
            {
                buttonVor.Enabled = true;
                pictureBoxFace.Image = detectedFaces[indexofSelectedFace];
            }
        }

        private void buttonVor_Click(object sender, EventArgs e)
        {
            indexofSelectedFace++;
            if (indexofSelectedFace == detectedFaces.Length - 1)
            {
                buttonVor.Enabled = false;
                buttonZurueck.Enabled = true;
                pictureBoxFace.Image = detectedFaces[indexofSelectedFace];
            }
            else
            {
                buttonZurueck.Enabled = true;
                pictureBoxFace.Image = detectedFaces[indexofSelectedFace];
            }
        }


        private void Trainieren()
        {
            string[] filePaths = Directory.GetFiles(@"Face Collection\", "*.bmp");
            var faceImages = new Image<Gray, byte>[filePaths.Count()];
            faceNames = new string[filePaths.Count()];
            if (filePaths.Count() == 0)
            {
                MessageBox.Show("Keine Bilder in der Datenbank vorhanden");
            }
            else
            {
                
                List<Mat> TrainingFaces = new List<Mat>();

                for (int i = 0; i < filePaths.Count(); i++)
                {
                    var faceImage = new Image<Gray, byte>(filePaths[i]);
                    Mat matfaceImage = faceImage.Mat;

                    string faceName = filePaths[i].Split('_')[1];

                    int faceID = Convert.ToInt32(filePaths[i].Split('_')[2].TrimEnd('p','m','b','.'));

                    TrainingFaces.Add(matfaceImage);
                    faceNames[i] = faceName;
                    faceIDs.Add(faceID);
                }
                /*Der EigenFaceRecognizer wird mit allen Bildern gefüttert 
                  die im Ordner "Face Collection" sind. Datenbank als Textdatei wird auch 
                  erstellt und danach wieder eingelesen.*/
                recognizer.Train(new VectorOfMat(TrainingFaces.ToArray()), new VectorOfInt(faceIDs.ToArray()));

                recognizer.Write(@"Face Collection\database.txt");
                recognizer.Read(@"Face Collection\database.txt");
                MessageBox.Show("Algorithmus wurde mit den gespeicherten Bildern trainiert.");
            }
        }

    }
}
Veröffentlicht am
Kategorisiert in Allgemein