• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

c#Winform使用Opencvsharp4实现简易人脸识别

武飞扬头像
Iawfy_
帮助1

     环境配置: vs2019 , .Net FrameWork 4.8  Opencvsharp4

      在Nuget内下载最新的Opencvsharp4即可,

学新通

       大概说一下我所理解的人脸识别的原理吧,就是先给训练器一些训练数据,就是我们告诉训练器这些数据分别对应的是哪些人,然后训练器就记住这些图像的特征以及对应的人名,然后在识别时找出识别图像的特征,再与已经训练好的特征进行比对,找出训练集中与之最相近的那个特征,并给出其对应的名字,和相似程度,也就是得分。

      在Opencvsharp4内,有相应的模块,在OpenCvSharp.Face 里面,FaceRecognizer 就是我们需要的训练器,注意,添加训练集时,我们给的训练数据是,图像以及对应的ID数字,转换成名字时我们可以在添加训练集时把对应的名字自己保存下来,在给出训练结果时,用给出的ID数字去找出对应的名字即可;还有添加训练集时,同一张人脸的照片放在同一个目录下,也就是说一个名字可以对应多张不同的人脸,但一个人脸只能有一个名字;添加训练集和进行人脸识别时,图像大小要一样;请至少添加两组训练图像。

     界面实现:

    简单的Winform界面 ,控件都是自带即可

学新通

     我把添加训练图像也放在界面上了,当然也可以直接在本地训练集里面进行修改,但是要注意图像的大小要保持一致

大概的运行结果

学新通

 主要代码:

首先 ,定义一个训练器

  public static FaceRecognizer faceRecongnizer = FisherFaceRecognizer.Create();

再定义一个自定义的类 即把图像与ID 绑定起来

  1.  
    class yImgs
  2.  
    {
  3.  
    // 图像
  4.  
    public Mat Image { set; get; }
  5.  
    // 编号
  6.  
    public int ImageGroupId { set; get; }
  7.  
    }

       用一个List<yImgs> 来存放训练数据,我不使用字典的原因是,1,如果编号是Key 图像是Value的话,一个编号 也就是一个人 就只能添加一张图像,用 List<Mat>作为字典的Value的话,也感觉麻烦,还不如直接使用一个List方便,2,如果使用图像作为Key,编号作为Value的话,图像就不能重复了,即一个人就不能添加两张一样的图片作为训练图片,而使用List<yImgs> 的话,就能添加两张一样的图片作为训练图片。

      有了这个List之后,我们再定义一个字典,来绑定编号和人的名字,这里就不考虑重名的。

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

     然后我们就可以添加训练图像了 我目前都是往本地添加图像,设置成想要的格式,然后在训练前,把所有的图像信息从本地读出来

  1.  
    /// <summary>
  2.  
    /// 添加训练集图片 即把指定图片调整为指定大小 并保存在训练集路径中
  3.  
    /// 同一人的照片都放在一个文件夹内 该文件夹的名字为 名字ID_名字,如:1_Lena
  4.  
    /// 图片名字是按顺序自己生成的 1.jpg 2.jpg....
  5.  
    /// </summary>
  6.  
    /// <param name="src">训练图像</param>
  7.  
    /// <param name="size">图像大小</param>
  8.  
    /// <param name="groupId">编号,与名字一个一个地对应</param>
  9.  
    /// <param name="name">名字</param>
  10.  
    public static void AddTrainImg(Mat src, OpenCvSharp.Size size,int groupId,string name)
  11.  
    {
  12.  
    string path0 = groupId.ToString() "_" name;
  13.  
    string path = yVars.path "\\" path0 "\\";
  14.  
    // 判断图像是否可以作为训练图像添加
  15.  
    // 即 图像包含人脸 且只有一张人脸
  16.  
    if (yMethods.TrainImgISOK(src) == false)
  17.  
    {
  18.  
    return;
  19.  
    }
  20.  
    try
  21.  
    {
  22.  
    if (!Directory.Exists(path))
  23.  
    {
  24.  
    Directory.CreateDirectory(path);
  25.  
    }
  26.  
    }
  27.  
    catch (Exception ex)
  28.  
    {
  29.  
    YXH._01.yMessagebox.ShowDialogCN("路径创建失败:" ex.Message);
  30.  
    return;
  31.  
    }
  32.  
    DirectoryInfo _path = new DirectoryInfo(path);
  33.  
    int i = 0;
  34.  
    do
  35.  
    {
  36.  
    i ;
  37.  
    } while (File.Exists(path i.ToString() ".jpg"));
  38.  
    string picname = path i.ToString() ".jpg";
  39.  
    try
  40.  
    {
  41.  
    Cv2.Resize(src, src, size);
  42.  
    src.SaveImage(picname);
  43.  
    yVars.TrainAgain = true;
  44.  
    YXH._01.yMessagebox.ShowDialogCN("训练图像添加成功");
  45.  
    }
  46.  
    catch (Exception ex)
  47.  
    {
  48.  
    YXH._01.yMessagebox.ShowDialogCN("训练图像添加失败:" ex.Message);
  49.  
    }
  50.  
    }
学新通

添加在本地的格式 如下

学新通

     训练集图像添加在本地后,就读取本地信息,并对训练器进行训练。就是给我们最开始定义的List<yImgs>赋值,并用它对训练器训练。

  1.  
    // 读取本地信息
  2.  
    private static bool GetInfos()
  3.  
    {
  4.  
    // 把原先的信息清空
  5.  
    yVars.faceDatas.Clear(); // 训练数据 List<yImgs>
  6.  
    yVars.namesDatas.Clear(); // 字典 Dictionary<int, string>
  7.  
    DirectoryInfo _path = new DirectoryInfo(yVars.path); //训练集存放路径
  8.  
     
  9.  
    if (_path.GetDirectories().Length < 2)
  10.  
    {
  11.  
    YXH._01.yMessagebox.ShowDialogCN("本地训练集小于两组,请添加训练集");
  12.  
    return false;
  13.  
    }
  14.  
    if (yFiles.DirectoryHasTwoGroup(yVars.path) == false)
  15.  
    {
  16.  
    YXH._01.yMessagebox.ShowDialogCN("本地训练集小于两组,请添加训练集");
  17.  
    return false;
  18.  
    }
  19.  
     
  20.  
    foreach (DirectoryInfo var in _path.GetDirectories())
  21.  
    {
  22.  
    string[] tempstr = var.ToString().Split('_');
  23.  
    int groupID = 0;
  24.  
    int.TryParse(tempstr[0], out groupID);
  25.  
    foreach (FileInfo vv in var.GetFiles())
  26.  
    {
  27.  
    if (!vv.FullName.Contains(".jpg"))
  28.  
    continue;
  29.  
    yVars.faceDatas.Add(
  30.  
    new yImgs
  31.  
    {
  32.  
    Image = new Mat(vv.FullName, ImreadModes.Grayscale),
  33.  
    ImageGroupId = groupID,
  34.  
    });
  35.  
    }
  36.  
    yVars.namesDatas.Add(groupID, tempstr[1]);
  37.  
    }
  38.  
    return true;
  39.  
    }
学新通

训练的话就直接调用 训练器的训练方法即可

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

训练好之后就能进行识别了。

大概步骤如下:

1、从识别的图像中获取出所有的人脸图像,并将这些人脸图像调整为跟训练集一样的大小.(识别图像内可以有多张人脸,训练图像内只能有一张人脸)

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

2、得到上面所有的人脸的位置

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

3、对获取出来的所有人脸照片进行识别,得到其对应的名字

  1.  
    // 获取所有名字
  2.  
    private static List<string> GetNames(List<Mat> mm)
  3.  
    {
  4.  
    List<string> names = new List<string>();
  5.  
    for (int i = 0; i < mm.Count; i )
  6.  
    {
  7.  
    int groupId = -2;
  8.  
    //groupId = yVars.FaceDetect.faceRecongnizer.Predict(mm[i]);
  9.  
    double confidence = 0.0;
  10.  
    yVars.faceRecongnizer.Predict(mm[i], out groupId, out confidence);
  11.  
    string desName;
  12.  
    yVars.namesDatas.TryGetValue(groupId, out desName);
  13.  
    // names.Add(desName " " confidence.ToString("0.00"));
  14.  
    names.Add(desName);
  15.  
    }
  16.  
    return names;
  17.  
    }
学新通

4、最后把所有的名字在对应位置画出来就行

  1.  
    // 画出所有的框和名字
  2.  
    public static Mat ShowFaceRects(Mat mm, Rect[] faces, List<string> names)
  3.  
    {
  4.  
    Random rnd = new Random();
  5.  
    int i = -1;
  6.  
    foreach (Rect face in faces)
  7.  
    {
  8.  
    i ;
  9.  
    Scalar color = new Scalar(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255));
  10.  
    Cv2.Rectangle(mm, face, color);
  11.  
    // 无法显示中文 可以考虑用 System.Drawing.Graphics
  12.  
    Cv2.PutText(mm, names[i], face.TopLeft, HersheyFonts.HersheySimplex, 0.8, new Scalar(255, 23, 0));
  13.  
    }
  14.  
    return mm;
  15.  
    }
学新通

注意一下写名字的时候,如果用 Cv2.PutText 名字就不能是中文的

这样就实现了简单的人脸识别了。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhickjkh
系列文章
更多 icon
同类精品
更多 icon
继续加载