65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 EmugCV 轻松检测和识别面部的应用程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (6投票s)

2016年9月7日

CPOL

3分钟阅读

viewsIcon

53175

downloadIcon

5204

在本文中,我将展示如何使用 EmugCV 创建一个基本的应用程序来检测和识别面部。

引言

本文的目的是演示使用 EmguCV(版本 3.1)实时检测和识别面部应用程序是多么容易。本文假设读者了解 Entity Framework 并必须拥有一台摄像头。

背景

EmguCV 是 OpenCV 库的 .NET 包装器,它允许您在 .NET 环境中使用相应的函数,在本例中是 C#。

EmguCV 使用 Viola-Jones 算法来有效且快速地解决面部检测问题。该算法使用 Haar 级联分类器、积分图像和 AdaBoost 学习算法等技术,通过比较图像中多个区域(矩形或方形)的现有对比度来解决面部检测问题。然而,该算法的创新之处在于其分类器级联(退化决策树),这加快了这一过程。

首先,准备工具

(仅用于使用 haar 文件)

  • 创建一个新项目;

(c# 语言和 Windows 窗体应用程序) 

  • 使用 NuGet 安装 emguCV;

Nuget

  • 将工具添加到工具箱

(在工具箱选项卡上,右键单击并选择“选择项”。然后搜索 emgucv 并将控件添加到工具箱)

其次,构建您的工作

我们的工具已准备就绪,现在让我们构建我们的解决方案。首先创建一个名为 HaarCascade 的文件夹,然后将 Haar 文件 (haarcascade_frontalface_default.xml) 添加到该文件夹。此文件位于您在本文开头下载的包的“bin”文件夹中,此 Haar 级联仅用于检测面部。您可以下载其他 Haar 文件来检测眼睛、全身以及对象。

第三,开始工作

为了教会系统识别面部,我们需要保存训练样本(图片)。在此应用程序中,我们将把拍摄的照片(样本)存储在 SQL 数据库中。为此,请创建一个本地数据库并添加实体数据模型。

添加一个名为 Faces 的新实体,其中包含四个标量属性:Id (Int32)、Username (string)、FaceSample (Binary) 和 UserID (Int32)。此实体/表将存储图片样本以训练系统。

向项目中添加一个窗体,并添加三个控件:ImageBox(新添加的控件)、文本框、按钮和一个间隔为 0.1 秒的计时器,此计时器用于运行检测用户面部的方法。

使用代码

窗体类代码

窗体类是应用业务逻辑和 UI 的地方。

逻辑很简单:检测面部,如果用户拍照(标记为用户名),应用程序将样本(图片)保存在数据库中并训练系统来预测他的面部。

为此,我们必须开始加载级联分类器,设置 EigenFace 方法,并使用预样本进行教学。

引用:来自维基百科

Eigenfaces 是在一组特征向量在计算机视觉的人脸识别问题中使用时赋予的名称。

private void start()
{
    cascadeClassifier = new CascadeClassifier(Application.StartupPath + @"\HaarCascade\haarcascade_frontalface_default.xml");
    capture = new Capture();
    //emugcv have others methods to reconize an object/face like FisherFaceRecognizer.
    //but i use the eigen because its flexible to noise
    faceRecognizer = new EigenFaceRecognizer(80, double.PositiveInfinity);
    learn();
    timer1.Enabled = true;
}

learn 方法旨在训练系统识别面部。

此方法将从数据库中检索所有样本,将它们从字节流转换为位图,并使用两个参数(位图和用户 ID)训练系统。

/// <summary>
/// Teach the system with samples to predict the face
/// </summary>
/// <returns></returns>
public bool learn()
{
  List<Faces> allFaces = da.GetTrainingSample();
  if (allFaces.Count > 0)
   {
     var faceImages = new Image<Gray, byte>[allFaces.Count];
     var faceLabels = new int[allFaces.Count];
     for (int i = 0; i < allFaces.Count; i++)
         {
           Stream stream = new MemoryStream();

           stream.Write(allFaces[i].FaceSample, 0, allFaces[i].FaceSample.Length);

           var faceImage = new Image<Gray, byte>(new Bitmap(stream));

           faceImages[i] = faceImage;
           faceLabels[i] = allFaces[i].UserID;
          }

    faceRecognizer.Train(faceImages, faceLabels);                
   }
 return true;
}

FaceRecognize 方法用于检测面部并在其上绘制方框,并在摄像头图像上显示用户名(如果面部未识别则为“john doe”)。

private void faceRecognize()
{
  string name;
  //take a frame
  using (var imageFrame = capture.QueryFrame().ToImage<Bgr, byte>())
   {
     if (imageFrame != null)
       {
         //convert to gray to improve the prediction
         var grayFrame = imageFrame.Convert<Gray, byte>();
                    
         //Finds rectangular regions (face); the second param its the scale > 1 slowest process but accurated prediction
         var faces = cascadeClassifier.DetectMultiScale(grayFrame, 1.3, 6, new Size((grayFrame.Size.Width / 4), (grayFrame.Size.Height / 4)));

         //Predict the frame taked. If theres a sample of the face in db it will return the username, if not will say John Doe
         name = (facePredict(grayFrame) > 0) ? da.GetUsername(facePredict(grayFrame)) : "John Doe";

         foreach (var face in faces)
            {                        
              //draw a box at the face
              imageFrame.Draw(face, new Bgr(Color.Green), 2);
              //put text bellow the box
              CvInvoke.PutText(imageFrame, name, new Point(face.Location.X + 10, face.Location.Y - 10), Emgu.CV.CvEnum.FontFace.HersheyComplex, 1.0, new Bgr(0, 255, 0).MCvScalar);
            }
         imageBox1.Image = imageFrame;
       }
   }
}

当用户单击按钮时,将样本添加到数据库。

同一个人越多的样本,系统的准确性就越高。请记住,光照也会影响识别,因此最好在不同类型的光照和不同时间拍摄照片。

private void button1_Click(object sender, EventArgs e)
{
  userPicture = capture.QueryFrame().ToImage<Gray, byte>();

  //Finds rectangular regions (face)
  var faces = cascadeClassifier.DetectMultiScale(userPicture, 1.3, 6, new Size((userPicture.Size.Width / 4), (userPicture.Size.Height / 4)));

  foreach (var face in faces)
   {
     //resize sample
     faceImageThumb = userPicture.Copy(face).Resize(64, 64, Emgu.CV.CvEnum.Inter.Cubic);
   }

  //add to db and notify user
  MessageBox.Show(da.AddSample(textBox1.Text, ConvertImageToByte(faceImageThumb)), "Face", MessageBoxButtons.OK);
}

DataAccess 类

这些类负责本地 SQL 数据库的 CRUD 操作。

AddSample 将在 faces 实体中搜索该用户的样本,如果存在,将保存新样本并赋予相同的用户 ID(否则将赋予新的用户 ID)。

  class DataAccess
    {
        /// <summary>
        /// add sample the more samples of the same person, the accurated the recognizing
        /// </summary>
        /// <param name="username"></param>
        /// <param name="faceBlob"></param>
        /// <returns></returns>
        public string AddSample(string username, byte[] faceBlob)
        {
            int userId = GetUserId(username);
            int rowChanged;
            Faces newFace = new Faces();
            try
            {
                using (RecModelContainer context = new RecModelContainer())
                {
                    newFace.FaceSample = faceBlob;
                    newFace.UserID = userId;
                    newFace.Username = username;
                    context.Faces.Add(newFace);
                    rowChanged = context.SaveChanges();

                    if (userId == 0)
                    {
                        newFace.UserID = newFace.Id;
                        context.SaveChanges();
                    }
                }
            }
            catch (DbUpdateException e)
            {
                return "Error " + e.InnerException;
            }
            return (rowChanged > 0) ? "Great a new face to recognize" : "Somehow, someway the face wasn't save.";
        }
    }

 

参考文献

历史

2016-09-06 : 发布

© . All rights reserved.