基于OpenCVSharp的人脸识别系统

一、概述

因为OpenCv对UI兼容不是很好,C#可以很好地写UI,有2个框架可供选择:1、EmguCv;2、OpenCvSharp。OpenCvSharp是一个日本人开发的开源项目,里面封装的类和方法和OpenCv非常相似,且底层协议用的是BSD,对商业友好,OpenCvSharp的下载量已经超过了EmguCv,之前用了EmguCv,现在改用OpenCvSharp来做OpenCv的程序开发。人脸识别系统大致可分为四部分:
第一、人脸检测;
第二、数据准备及人脸识别器的训练;
第三、人脸识别并显示结果;
第四、添加人脸数据

二、人脸检测

加载haarcascade_frontalface_alt.xml(一个已经训练好的用于检测人脸的级联分类器文件,在OpenCv库下的Source文件夹下有很多这样额级联分类器资源),DetectMultiScale方法用于检测人脸,返回人脸矩形框,关键代码如下:

CascadeClassifier cascade = new CascadeClassifier(@"..\..\FaceCascade\haarcascade_frontalface_alt.xml");

            Rect[] faces = cascade.DetectMultiScale(
                image: grayImage,
                scaleFactor: 1.1,
                minNeighbors: 2,
                flags: HaarDetectionType.DoRoughSearch | HaarDetectionType.ScaleImage,
                minSize: new OpenCvSharp.Size(30, 30)
            );

三、数据准备及人脸识别器的训练

需要准备好经过灰度化和直方图均衡化的人脸图像以及它对应的ID(每张人脸只有一个ID,一个ID可以对应多张人脸),为每个人建立一个文件夹,文件夹下存储多张该人脸图片(本系统设定只存3张)。创建一个识别器,OpenCV提供了三种人脸识别器:FisherFaceRecognizer,LBPHFaceRecognizer,EigenFaceRecognizer,可任选一种识别器创建。将人脸数据和ID导入到人脸识别器进行训练,至此,训练好的识别器具有预测功能。关键代码如下:

public void GetFaceRecognizer()
        {
            //获得已有的人脸信息库,信息存在images 和 faceDic中
            GetImageInfos();

            //使用了FisherFaceRecognizer类型的人脸识别器
            faceRecognizer = FisherFaceRecognizer.Create();
            
            //进行人脸数据的训练 每张图片及它的标识Id,该函数的作用相当于以下注释的代码
            faceRecognizer.Train(Images.Select(x => x.Image), Images.Select(x => x.ImageGroupId));
            
        	 //List mats = new List();
            //List labs = new List();
            //for(int i = 0; i < images.Count; i++)
            //{
            //    mats.Add(images[i].Image);
            //    labs.Add(images[i].ImageGroupId);
            //}
            //faceRecognizer.Train(mats, labs);
        }

需要注意的是:faceRecognizer.Train()函数,要求每个ID对应的人脸头像数量必须相等,否则会报错。

四、人脸识别并显示结果

得到的人脸识别器有个Predict函数,它能根据人脸头像预测出该头像的ID,进而可以根据ID得到姓名(首先得预存ID对应的姓名)。将姓名和检测的人脸框写到原始图片上,然后显示给给用户。关键代码如下:

//识别人脸
public List PredictFace(List curFaces)
        {
            List names = new List();
            for(int i = 0; i < curFaces.Count; i++)
            {
                int groupId = -1;
                groupId = faceRecognizer.Predict(curFaces[i]);
                string desName;
                FaceDic.TryGetValue(groupId,out desName);
                names.Add(desName);
            }
            return names;
        }
        //展现结果
        public void ShowFaceRects(Rect[] faces,Bitmap grab,List names)
        {
            Graphics g = Graphics.FromImage(grab);
            //Bitmap resBitmap = new Bitmap(100,100);
            //Random rnd = new Random();
            int count = 0;
            Font font = new Font("宋体", 16, GraphicsUnit.Pixel);
            SolidBrush fontLine = new SolidBrush(Color.Yellow);
            foreach (Rect face in faces)
            {
                g.DrawRectangle(new Pen(Color.YellowGreen,2),face.X,face.Y,face.Width,face.Height);
                //OpenCv自带的PutText函数不能显示中文
                //Scalar color = new Scalar(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255));
                //Cv2.Rectangle(grab, face, color);
                //Cv2.PutText(grab,names[count++],face.TopLeft,HersheyFonts.HersheySimplex,0.8, new Scalar(255, 23, 0));
                float xPos = face.X + (face.Width / 2 - (names[count].Length * 14) / 2);
                float yPos = face.Y - 21;
                g.DrawString(names[count++], font, fontLine, xPos, yPos);
            }
        }

五、添加人脸数据

由于系统需求,要添加人脸数据,我通过截取一帧人脸图片,经过灰度化和直方图均衡化后存放到本地,并在系统的运行内存变量记录该信息,方便之后进行人脸识别器的训练,保存3张该人脸信息之后,再进行识别器的训练,训练之后就能正确识别人脸了。
关键代码:

 /// 
        /// 更新人脸信息库
        /// 
        /// 
        /// 
        public void UpdateImagesInfos(string name,Mat face)
        {
            //信息库存在这个人名
            if (FaceDic.ContainsValue(name))
            {
                int groupId = FaceDic.FirstOrDefault(q => q.Value.Equals(name)).Key;
                string dir = "..\\..\\Images\\" + groupId + "_" + name;
                int count = 0;
                foreach (FileInfo  file in new DirectoryInfo(dir).GetFiles())
                {
                    count++;
                }
                if (count < 3)
                {
                    face.SaveImage(dir + "\\" + count + ".png");
                    images.Add(new ImageInfo
                    {
                        Image = face,
                        ImageGroupId = groupId,
                        ImageId = count,

                    });
                }
                else
                {
                    MessageBox.Show("样本提取完成,不需要重复提取该人脸图像");
                    return;
                }
            }
            //不存在,则加入该人脸,并更新images中的数据和faceDic
            else
            {
                string path = "..\\..\\Images\\";
                string[] list = System.IO.Directory.GetDirectories(path);
                int dirLen = list.Length;
                Directory.CreateDirectory(path+ dirLen + "_"+name);
                face.SaveImage(path + dirLen + "_" + name+"\\"+0+".png");
                faceDic.Add(dirLen, name);
                images.Add(new ImageInfo
                {
                    Image = face,
                    ImageGroupId = dirLen,
                    ImageId = 0,

                });
            }
            MessageBox.Show("更新成功");
        }

六、心得

做系统之前,需要想好哪些需求,根据需求写出相应的类和方法,类写好之后,可以更好地集成系统,使系统复杂度降低许多,将每个模块写好的来,整个系统也就可以慢慢完善好。本系统使用的是传统的人脸识别算法,现在深度学习就可以很容易去实现它,但是,它们各有优劣,传统的人脸识别算法识别效率高,但是识别正确率较低;而基于深度学习的人脸识别的识别准确率高,但是识别效率低,不适合用于实时人脸识别。整个项目花了将近4天的时间,对于我来说还是挺满意的,希望以后在计算机视觉这条路上越走越远。

这是本系统的主要代码,可供大家参考:
https://download.csdn.net/download/it_boy__/11239578

你可能感兴趣的:(OpenCV)