因为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