c#Winform uses Opencvsharp4 to realize simple face recognition

     Environment configuration: vs2019, .Net FrameWork 4.8 Opencvsharp4

      Just download the latest Opencvsharp4 in Nuget,

       Let me talk about the principle of face recognition as I understand it, that is, first give the trainer some training data, that is, we tell the trainer who these data correspond to, and then the trainer remembers the characteristics of these images and the corresponding The name of the person, and then find out the features of the recognition image during recognition, and then compare it with the trained features, find out the feature that is closest to it in the training set, and give its corresponding name, and similarity, that is Score.

      In Opencvsharp4, there are corresponding modules. In OpenCvSharp.Face, FaceRecognizer is the trainer we need. Note that when adding the training set, the training data we give is the image and the corresponding ID number. When converting it into a name, we can When adding the training set, save the corresponding name by yourself. When giving the training result, use the given ID number to find the corresponding name; and when adding the training set, put the photo of the same face in the In the same directory, that is to say, a name can correspond to multiple different faces, but a face can only have one name; when adding a training set and performing face recognition, the image size should be the same; please add at least two sets of training image.

     Interface implementation:

    Simple Winform interface, all controls are self-contained

     I also put the added training image on the interface. Of course, it can also be modified directly in the local training set, but it should be noted that the size of the image should be consistent

Approximate running results

 Main code:

First, define a trainer

  public static FaceRecognizer faceRecongnizer = FisherFaceRecognizer.Create();

Then define a custom class that binds the image to the ID

    class yImgs
    {
        // 图像
        public Mat Image { set; get; }
        // 编号 
        public int ImageGroupId { set; get; }
    }

       Use a List<yImgs> to store training data. The reason why I don’t use a dictionary is, 1. If the number is the Key and the image is the Value, one number, that is, one person can only add one image, and use List<Mat> as The value of the dictionary is also troublesome. It is better to use a List directly. 2. If you use the image as the Key and the number as the Value, the image cannot be repeated, that is, one person cannot add two identical pictures as training pictures. , and using List<yImgs>, you can add two same pictures as training pictures.

      After having this List, we define a dictionary to bind the number and the name of the person, and the duplicate name is not considered here.

 public static Dictionary<int, string> namesDatas = new Dictionary<int, string>();

     Then we can add training images. I currently add images locally, set them to the desired format, and then read all the image information from the local before training.

        /// <summary>
        /// 添加训练集图片 即把指定图片调整为指定大小 并保存在训练集路径中
        /// 同一人的照片都放在一个文件夹内 该文件夹的名字为 名字ID_名字,如:1_Lena 
        /// 图片名字是按顺序自己生成的  1.jpg  2.jpg....
        /// </summary>
        /// <param name="src">训练图像</param>
        /// <param name="size">图像大小</param>
        /// <param name="groupId">编号,与名字一一对应</param>
        /// <param name="name">名字</param>
        public static void AddTrainImg(Mat src, OpenCvSharp.Size size,int groupId,string name)
        {
            string path0 = groupId.ToString() + "_" + name;
            string path = yVars.path + "\\" + path0 + "\\";
            // 判断图像是否可以作为训练图像添加 
            // 即 图像包含人脸 且只有一张人脸 
            if (yMethods.TrainImgISOK(src) == false)
            {
                return;
            }
            try
            {
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
            }
            catch (Exception ex)
            {
                YXH._01.yMessagebox.ShowDialogCN("路径创建失败:" + ex.Message);
                return;
            }
            DirectoryInfo _path = new DirectoryInfo(path);
            int i = 0;
            do
            {
                i++;
            } while (File.Exists(path + i.ToString() + ".jpg"));
            string picname = path + i.ToString() + ".jpg";
            try
            {
                Cv2.Resize(src, src, size);
                src.SaveImage(picname);
                yVars.TrainAgain = true;
                YXH._01.yMessagebox.ShowDialogCN("训练图像添加成功");
            }
            catch (Exception ex)
            {
                YXH._01.yMessagebox.ShowDialogCN("训练图像添加失败:" + ex.Message);
            }
        }

The format added locally is as follows

     After the training set images are added locally, the local information is read and the trainer is trained. It is to assign a value to the List<yImgs> we defined at the beginning, and use it to train the trainer.

        // 读取本地信息
        private static bool GetInfos()
        {
            // 把原先的信息清空 
            yVars.faceDatas.Clear(); // 训练数据 List<yImgs>
            yVars.namesDatas.Clear(); // 字典  Dictionary<int, string>
            DirectoryInfo _path = new DirectoryInfo(yVars.path); //训练集存放路径

            if (_path.GetDirectories().Length < 2)
            {
                YXH._01.yMessagebox.ShowDialogCN("本地训练集小于两组,请添加训练集");
                return false;
            }
            if (yFiles.DirectoryHasTwoGroup(yVars.path) == false)
            {
                YXH._01.yMessagebox.ShowDialogCN("本地训练集小于两组,请添加训练集");
                return false;
            } 

            foreach (DirectoryInfo var in _path.GetDirectories())
            {
                string[] tempstr = var.ToString().Split('_');
                int groupID = 0;
                int.TryParse(tempstr[0], out groupID);
                foreach (FileInfo vv in var.GetFiles())
                {
                    if (!vv.FullName.Contains(".jpg"))
                        continue;
                    yVars.faceDatas.Add(
                        new yImgs
                        {
                            Image = new Mat(vv.FullName, ImreadModes.Grayscale),
                            ImageGroupId = groupID,
                        });
                }
                yVars.namesDatas.Add(groupID, tempstr[1]);
            }
            return true;
        }

For training, just call the training method of the trainer directly

  yVars.faceRecongnizer.Train(yVars.faceDatas.Select(x => x.Image), yVars.faceDatas.Select(x => x.ImageGroupId));

It can be recognized after training.

The approximate steps are as follows:

1. Obtain all face images from the recognized image, and adjust these face images to the same size as the training set. (There can be multiple faces in the recognition image, and there can only be one face in the training image )

        // 从图片中获取所有的人脸图片 并调整为指定大小
        private static List<Mat> GetFaces(Mat mm, OpenCvSharp.Rect[] rects, OpenCvSharp.Size size)
        {
            List<Mat> faces = new List<Mat>();
            foreach (Rect rect in rects)
            {
                Mat m1 = new Mat(mm, rect);
                Cv2.CvtColor(m1, m1, ColorConversionCodes.BGR2GRAY);
                Cv2.Resize(m1, m1, size);
                // Cv2.EqualizeHist(m1, m1);
                faces.Add(m1);
            }
            return faces;
        }

2. Get the positions of all the faces above

        // 获取图像所有的人脸框
        private static OpenCvSharp.Rect[] GetRects(Mat mm)
        {
            Mat grayImage = new Mat();
            Cv2.CvtColor(mm, grayImage, ColorConversionCodes.BGR2GRAY);
            Cv2.EqualizeHist(grayImage, grayImage);
            string path = System.Windows.Forms.Application.StartupPath + "\\xml\\haarcascades\\" + "haarcascade_frontalface_alt.xml";
            CascadeClassifier face = new CascadeClassifier(path); 
            Rect[] faces = face.DetectMultiScale(mm);
            return faces;
        }

3. Recognize all the obtained face photos and get their corresponding names

        // 获取所有名字
        private static List<string> GetNames(List<Mat> mm)
        {
            List<string> names = new List<string>();
            for (int i = 0; i < mm.Count; i++)
            {
                int groupId = -2;
                //groupId = yVars.FaceDetect.faceRecongnizer.Predict(mm[i]);
                double confidence = 0.0;
                yVars.faceRecongnizer.Predict(mm[i], out groupId, out confidence);
                string desName;
                yVars.namesDatas.TryGetValue(groupId, out desName);
                // names.Add(desName + " " + confidence.ToString("0.00"));
                names.Add(desName);
            }
            return names;
        }

4. Finally, draw all the names in the corresponding positions.

        // 画出所有的框和名字
        public static Mat ShowFaceRects(Mat mm, Rect[] faces, List<string> names)
        {
            Random rnd = new Random();
            int i = -1;
            foreach (Rect face in faces)
            {
                i++;
                Scalar color = new Scalar(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255));
                Cv2.Rectangle(mm, face, color);
                // 无法显示中文 可以考虑用  System.Drawing.Graphics
                Cv2.PutText(mm, names[i], face.TopLeft, HersheyFonts.HersheySimplex, 0.8, new Scalar(255, 23, 0));
            }
            return mm;
        }

Note that when writing the name, if you use Cv2.PutText, the name cannot be in Chinese

In this way, simple face recognition is realized.

Guess you like

Origin blog.csdn.net/Iawfy_/article/details/125495355