|
- using Emgu.CV;
- using Emgu.CV.Dnn;
- using Emgu.CV.Face;
- using Emgu.CV.Structure;
- using System.Drawing;
- using System.Text;
- using ToolsServices;
- using static System.Net.Mime.MediaTypeNames;
-
- namespace FaceRecognition
- {
- public class FaceRecognitionService
- {
- #region Variables privées statiques
- private static string NomFichierData = FichiersInternesService.ParamsFaceRecognition;
- #endregion
-
- #region Variables privées
- private readonly string _PathDataTrainSet = @"D:\_TrainingData\FaceRecognition\TrainSet";
- private readonly string _PathModel = @"Models";
-
- private readonly string _NomModelDetect = @"face_detection_yunet_2023mar.onnx";
- private readonly string _NomModel = @"model_reconnaissance.yml";
- private readonly string _FaceHaarXML = @"haarcascade_frontalface_alt.xml";
-
- private readonly int _ReSizeFaceImg = 300;
- private readonly int _ReSizeImage = 400;
- private readonly float _ScaleFactor = 0.9f;
- private readonly System.Drawing.Size _MinSizeDetect = new(30, 30);
-
- private readonly int _DistanceMini = 40;
-
- private readonly MCvScalar _MCvScalarColorGreen = new(0, 255, 0);
- private readonly MCvScalar _MCvScalarColorRed = new(0, 0, 255);
- private readonly int _SizeBorder = 2;
-
- private readonly LBPHFaceRecognizer _FaceRecognizer = new();
- private readonly FaceDetectorYN _FaceNet;
-
- private List<int> _LstLabels = new();
- private List<string> _LstLabelsString = new();
-
- private readonly System.Drawing.Size _InputSize = new(300, 300);
- #endregion
-
- #region Constructeur
- public FaceRecognitionService(bool isCuda = false)
- {
- var parametres = LoadParametres();
- _PathDataTrainSet = parametres.Path_TrainSet;
-
- // Créer un objet CascadeClassifier pour détecter les visages
- if (isCuda)
- {
- _FaceNet = new FaceDetectorYN(TrtFindFileFaceModelDetect(), "", _InputSize, backendId: Emgu.CV.Dnn.Backend.Cuda, targetId: Target.Cuda);
- }
- else
- {
- _FaceNet = new FaceDetectorYN(TrtFindFileFaceModelDetect(), "", _InputSize, backendId: Emgu.CV.Dnn.Backend.Default, targetId: Target.Cpu);
- }
- }
- #endregion
-
- #region Gestion des paramètres TrainSet et TestSet
- public static ParametresFaceRecognitionService LoadParametres()
- {
- LoggerService.LogInfo("ParametresFaceRecognitionService.LoadParametres");
-
- ParametresFaceRecognitionService SelectedItem = new();
-
- try
- {
- if (File.Exists(NomFichierData))
- {
-
- string[] lignes = File.ReadAllLines(NomFichierData);
-
- if (lignes.Length > 0)
- SelectedItem.Path_TrainSet = lignes[0];
-
- if (lignes.Length > 1)
- SelectedItem.Path_TestSet = lignes[1];
-
- }
- return SelectedItem;
-
- }
- catch
- {
- return new();
- }
- }
-
- public static bool SaveParametres(ParametresFaceRecognitionService selectedItem)
- {
- LoggerService.LogInfo("ParametresFaceRecognitionService.SaveParametres");
-
- try
- {
- StringBuilder sb = new();
- sb.AppendLine(selectedItem.Path_TrainSet);
- sb.AppendLine(selectedItem.Path_TestSet);
-
- File.WriteAllText(NomFichierData, sb.ToString());
-
- return true;
- }
- catch
- {
- return false;
- }
-
- }
- #endregion
-
- #region Méthodes publiques
- public bool CapturePhoto(string filename)
- {
- try
- {
- var inputImage = new Mat(filename);
- var inputClone = inputImage.Clone();
- var lstRetour = new List<string>();
-
- if (!File.Exists(filename))
- {
- throw new Exception($"Fichier manquant : {filename}.");
- }
- _FaceRecognizer.Read(TrtFindFileModel());
-
- // Détecter les visages dans l'image
- var w = (int)(_ReSizeImage * 3 * _ScaleFactor);
- var h = (int)(_ReSizeImage * 3 * inputImage.Height / inputImage.Width * _ScaleFactor);
- CvInvoke.Resize(inputImage, inputImage, new System.Drawing.Size(w, h));
- var faces = DetectMultiFaces(inputImage);
-
- if (faces.Length == 0)
- {
- inputImage = inputClone.Clone();
- w = (int)(_ReSizeImage * 2 * _ScaleFactor);
- h = (int)(_ReSizeImage * 2 * inputImage.Height / inputImage.Width * _ScaleFactor);
- CvInvoke.Resize(inputImage, inputImage, new System.Drawing.Size(w, h));
- faces = DetectMultiFaces(inputImage);
- }
-
- if (faces.Length == 0)
- {
- inputImage = inputClone.Clone();
- w = (int)(_ReSizeImage * _ScaleFactor);
- h = (int)(_ReSizeImage * inputImage.Height / inputImage.Width * _ScaleFactor);
- CvInvoke.Resize(inputImage, inputImage, new System.Drawing.Size(w, h));
- faces = DetectMultiFaces(inputImage);
- }
-
- // Parcourir chaque visage détecté
- foreach (var face in faces)
- {
- // Extraire la région du visage de l'image
- var faceImage = new Mat(inputImage, face);
-
- if (System.IO.File.Exists(filename))
- System.IO.File.Delete(filename);
-
- CvInvoke.Imwrite(filename, faceImage);
-
- }
-
- return true;
- }
- catch
- {
- return false;
-
- }
- }
-
- public (bool, string) Training(bool isPhotoVisible)
- {
- return TrtTraining(isPhotoVisible);
- }
-
- public List<string> Predict(string filename)
- {
- (var lst, _) = TrtPredict(new(filename));
- return lst;
- }
-
- public List<string> Predict(Mat inputImage)
- {
- (var lst, _) = TrtPredict(inputImage);
- return lst;
- }
-
- public List<string> PredictLiveNoShow()
- {
- List<string> lst = new();
- var videoCapture = new Emgu.CV.VideoCapture(0);
- try
- {
- int i = 0;
- while (true)
- {
- try
- {
- i++;
- Mat mat = new();
- videoCapture.Read(mat);
-
- // Préparation de l'affichage de l'image
- (lst, _) = TrtPredict(mat, false, true);
-
- if (i > 4)
- break;
- }
- catch (Exception ex)
- {
- Console.WriteLine($"Erreur : {ex.Message}");
- }
- }
- }
- catch
- {
-
- }
- return lst;
- }
-
- public void PredictLive()
- {
- var videoCapture = new Emgu.CV.VideoCapture(0);
- try
- {
- while (true)
- {
- try
- {
- Mat mat = new();
- Mat matOutput;
-
- videoCapture.Read(mat);
-
- // Préparation de l'affichage de l'image
- (_, matOutput) = TrtPredict(mat, false, true);
- CvInvoke.Imshow("frame", matOutput);
-
- // affichage et saisie d'un code clavier (Q ou ECHAP)
- if (CvInvoke.WaitKey(1) == (int)ConsoleKey.Q || CvInvoke.WaitKey(1) == (int)ConsoleKey.Escape)
- break;
- }
- catch (Exception ex)
- {
- Console.WriteLine($"Erreur : {ex.Message}");
-
- }
- }
- }
- catch
- {
-
- }
- finally
- {
- // Ne pas oublier de fermer le flux et la fenetre
- CvInvoke.WaitKey(0);
- CvInvoke.DestroyAllWindows();
- }
- }
- #endregion
-
- #region Méthodes privées
- private (bool, string) TrtTraining(bool isPhotoVisible)
- {
- bool isReturnOK = true;
- string msgReturn = "";
-
- try
- {
- // Créer des listes pour stocker les images de formation et les étiquettes correspondantes
- var trainingImages = TrtFindImagesAndLabels(true, isPhotoVisible);
-
- // Entraîner le reconnaiseur avec les images de formation et les étiquettes
- _FaceRecognizer.Train(trainingImages.ToArray(), _LstLabels.ToArray());
- for (int i = 0; i < _LstLabels.Count; i++)
- {
- _FaceRecognizer.SetLabelInfo(_LstLabels[i], _LstLabelsString[i]);
- }
-
-
- // Enregistrer le modèle entraîné
- _FaceRecognizer.Write(Path.Combine(_PathModel, _NomModel));
- }
-
- catch (Exception ex)
- {
- isReturnOK = false;
- msgReturn = ex.Message;
- }
-
- return (isReturnOK, msgReturn);
- }
-
- private (List<string>, Mat) TrtPredict(Mat inputImage, bool isAffichage = true, bool isOnlyFirstName=true)
- {
- var inputClone = inputImage.Clone();
- var lstRetour = new List<string>();
- string filename = TrtFindFileModel();
- if (!File.Exists(filename))
- {
- throw new Exception($"Fichier manquant : {filename}.");
- }
- _FaceRecognizer.Read(TrtFindFileModel());
-
- // Convertir l'image en niveaux de gris pour faciliter la détection des visages
- var grayImage = new Mat();
-
-
- // Détecter les visages dans l'image
- var w = (int)(_ReSizeImage * 3 * _ScaleFactor);
- var h = (int)(_ReSizeImage * 3 * inputImage.Height / inputImage.Width * _ScaleFactor);
- CvInvoke.Resize(inputImage, inputImage, new System.Drawing.Size(w, h));
- var faces = DetectMultiFaces(inputImage);
-
- if (faces.Length == 0)
- {
- inputImage = inputClone.Clone();
- w = (int)(_ReSizeImage * 2 * _ScaleFactor);
- h = (int)(_ReSizeImage * 2 * inputImage.Height / inputImage.Width * _ScaleFactor);
- CvInvoke.Resize(inputImage, inputImage, new System.Drawing.Size(w, h));
- faces = DetectMultiFaces(inputImage);
- }
-
- if (faces.Length == 0)
- {
- inputImage = inputClone.Clone();
- w = (int)(_ReSizeImage * _ScaleFactor);
- h = (int)(_ReSizeImage * inputImage.Height / inputImage.Width * _ScaleFactor);
- CvInvoke.Resize(inputImage, inputImage, new System.Drawing.Size(w, h));
- faces = DetectMultiFaces(inputImage);
- }
-
- CvInvoke.CvtColor(inputImage, grayImage, Emgu.CV.CvEnum.ColorConversion.Bgr2Gray);
-
- // Parcourir chaque visage détecté
- foreach (var face in faces)
- {
- // Extraire la région du visage de l'image en niveaux de gris
- var faceImage = new Mat(grayImage, face);
- CvInvoke.Resize(faceImage, faceImage, new System.Drawing.Size(_ReSizeFaceImg, _ReSizeFaceImg));
-
- // Reconnaître le visage à partir de l'image redimensionnée
- var predict = _FaceRecognizer.Predict(faceImage);
-
- if (predict.Distance < _DistanceMini)
- {
- var nameComplet = _FaceRecognizer.GetLabelInfo(predict.Label);
- var name = "";
- if (isOnlyFirstName)
- {
- name = nameComplet.Split('_')[0];
- }
- else
- {
- name = nameComplet.Replace("_", " ");
- }
- lstRetour.Add(name);
-
- CvInvoke.Rectangle(inputImage, face, _MCvScalarColorGreen, _SizeBorder);
- //CvInvoke.PutText(inputImage, name + " - " + predict.Distance.ToString("0.0"), new System.Drawing.Point(face.X, face.Y), Emgu.CV.CvEnum.FontFace.HersheySimplex, 1.0, _MCvScalarColor, _SizeBorder);
- CvInvoke.PutText(inputImage, name, new System.Drawing.Point(face.X, face.Y), Emgu.CV.CvEnum.FontFace.HersheySimplex, 1.0, _MCvScalarColorGreen, _SizeBorder);
- CvInvoke.PutText(inputImage, predict.Distance.ToString("0.0"), new System.Drawing.Point(face.X, face.Y + 30), Emgu.CV.CvEnum.FontFace.HersheySimplex, 0.75, _MCvScalarColorGreen, _SizeBorder);
- }
- else
- {
- //var name = "???";
- //lstRetour.Add(name);
- CvInvoke.Rectangle(inputImage, face, _MCvScalarColorRed, _SizeBorder);
- CvInvoke.PutText(inputImage, predict.Distance.ToString("0.0"), new System.Drawing.Point(face.X, face.Y), Emgu.CV.CvEnum.FontFace.HersheySimplex, 1.0, _MCvScalarColorRed, _SizeBorder);
- }
- }
-
- if (isAffichage)
- {
- CvInvoke.Imshow("reconnaissance", inputImage);
- CvInvoke.WaitKey(0);
- CvInvoke.DestroyAllWindows();
- }
- return (lstRetour, inputImage);
- }
-
- private List<Mat> TrtFindImagesAndLabels(bool isTraining, bool isPhotoVisible)
- {
- // Créer des listes pour stocker les images de formation et les étiquettes correspondantes
- var lstTrainingImages = new List<Mat>();
-
- if(System.IO.Directory.Exists(_PathDataTrainSet) == false)
- {
- throw new Exception($"Le dossier n'existe pas : {_PathDataTrainSet}");
- }
-
- try
- {
- _LstLabels = new();
- _LstLabelsString = new();
-
- var dirs = Directory.GetDirectories(_PathDataTrainSet);
- int numDir = 1;
- foreach (var dir in dirs)
- {
- var files = Directory.GetFiles(dir);
-
- foreach (var file in files)
- {
- var dernierDir = dir.Split(@"\");
- var nomDernierDir = dernierDir[^1];
-
- if (isTraining)
- {
- var image = new Mat(file);
- var grayImage = new Mat();
-
-
- //var w = (int)(image.Width * _ScaleFactor);
- //var h = (int)(image.Height * _ScaleFactor);
- var w = (int)(_ReSizeImage * _ScaleFactor);
- var h = (int)(_ReSizeImage * image.Height / image.Width * _ScaleFactor);
- CvInvoke.Resize(image, image, new System.Drawing.Size(w, h));
- var faces = DetectMultiFaces(image);
-
- CvInvoke.CvtColor(image, grayImage, Emgu.CV.CvEnum.ColorConversion.Bgr2Gray);
- if (faces.Length > 0)
- {
- var face = new Mat(grayImage, faces[0]);
- CvInvoke.Resize(face, face, new System.Drawing.Size(_ReSizeFaceImg, _ReSizeFaceImg));
- lstTrainingImages.Add(face);
-
- _LstLabels.Add(numDir);
- _LstLabelsString.Add(nomDernierDir);
-
- if (isPhotoVisible)
- {
- CvInvoke.PutText(image, nomDernierDir, new System.Drawing.Point(faces[0].X, faces[0].Y), Emgu.CV.CvEnum.FontFace.HersheySimplex, 1.0, _MCvScalarColorGreen, _SizeBorder);
- CvInvoke.Rectangle(image, faces[0], _MCvScalarColorGreen, _SizeBorder);
- }
- }
- else
- {
- Console.WriteLine("Visage non détecté");
- }
- if (isPhotoVisible)
- {
- CvInvoke.Imshow("reconnaissance", image);
- CvInvoke.WaitKey(0);
- CvInvoke.DestroyAllWindows();
- }
- }
- else
- {
- _LstLabels.Add(numDir);
- _LstLabelsString.Add(nomDernierDir);
- }
-
- }
- numDir++;
- }
-
- }
-
-
- catch (Exception ex)
- {
- throw new Exception(ex.Message);
- }
-
- return lstTrainingImages;
- }
-
- public Rectangle[] DetectMultiFaces(Mat img)
- {
- var lstRect = new List<Rectangle>();
-
- _FaceNet.InputSize = new System.Drawing.Size(img.Width, img.Height);
-
- var outputFaces = new Mat();
-
- _FaceNet.Detect(img, outputFaces);
-
- var detectionArray = outputFaces.GetData();
- if (detectionArray is null)
- {
- return new Rectangle[0];
- }
-
- var max = detectionArray.GetLength(0);
-
- Parallel.For(0, max, i =>
- {
- var confidence = (float)((Single)detectionArray.GetValue(i, 14)!);
- if (confidence > 0.5)
- {
- // Coordonnées 2 points qui tracent un rectangle englobe le visage
- var x1 = (int)((Single)detectionArray.GetValue(i, 0)!);
- var y1 = (int)((Single)detectionArray.GetValue(i, 1)!);
- var x2 = (int)((Single)detectionArray.GetValue(i, 2)!);
- var y2 = (int)((Single)detectionArray.GetValue(i, 3)!);
-
- lstRect.Add(new System.Drawing.Rectangle(x1, y1, x2, y2));
- }
- });
-
- return lstRect.ToArray();
- }
-
- private string TrtFindFileFaceHaarXML()
- {
- return Path.Combine(_PathModel, _FaceHaarXML);
- }
-
- private string TrtFindFileFaceModelDetect()
- {
- return Path.Combine(_PathModel, _NomModelDetect);
- }
-
- private string TrtFindFileModel()
- {
- return Path.Combine(_PathModel, _NomModel);
- }
- #endregion
- }
- }
|