Dlib+OpenCV深度学习人脸识别的方法示例


Posted in Python onMay 14, 2019

前言

人脸识别在LWF(Labeled Faces in the Wild)数据集上人脸识别率现在已经99.7%以上,这个识别率确实非常高了,但是真实的环境中的准确率有多少呢?我没有这方面的数据,但是可以确信的是真实环境中的识别率并没有那么乐观。现在虽然有一些商业应用如员工人脸识别管理系统、海关身份验证系统、甚至是银行人脸识别功能,但是我们可以仔细想想员工人脸识别管理,海关身份证系统的应用场景对身份的验证功能其实并没有商家吹嘘的那么重要,打个比方说员工上班的时候刷脸如果失败了会怎样,是不是重新识别一下,如果还是误识别,或是识别不出,是不是就干脆刷卡或是其他方式登记上班,然后骂一句他娘的,本人那么帅居然没识别出来!那银行柜员机上人脸识别系统呢,你看它敢不敢让你连密码也不输直接刷脸转账,是不是关掉了人脸识别、指纹识别机器还可以正常运作。所以说真实环境中在各种光照因素、年龄因素、网红因素(化妆)、甚至是作弊因素等各种因素条件下的识别率有多少只有产品厂家自己知道,我相信每个厂家针对这些情况都有做优化,比如外围硬件的辅助,针对特定场景的各种约束等等,通过各个厂家自己在各个方面对系统的优化,是可以提升自身产品的综合体验的。

前面扯远了,本文的目的是实现一个人脸识别的最简单实际应用,即用摄像头捕捉动态人脸,然后和已经存储在数据库中的128D人脸特征进行比较识别出相应的人脸信息(名字等)。工程是基于VS2015+简单的MFC对话框实现的,代码存放在:http://git.oschina.net/wjiang/face_recognition

在这个系统中我预先存储了下面几位明星的正面头像的128D人脸特征,当然你可以存储和导入更多的人脸。

Dlib+OpenCV深度学习人脸识别的方法示例

然后经过人脸检测、人脸图像处理,和人脸识别等步骤识别出相应的人脸信息,识别效果如下(怕大家被丑到所以用了明星的图片,没有用真实的人脸 ? 没有做活体检测):

Dlib+OpenCV深度学习人脸识别的方法示例

Dlib+OpenCV深度学习人脸识别的方法示例

当然这只是一个简单的应用,真正用到生产的系统,还需运用活体检测等技术,防止运用照片或是手机视频等方式欺骗过人脸识别系统,安全级别要求更高的应用领域例如支付、转账等系统活体检测可能仍不够安全,这时还可以通过人脸识别+验证密码等方式加强安全性能。

人脸数据库导入

人脸数据导入,也就是说我在系统启动之初,需要导入我的人脸数据库,也就是前面的那些明星的正面照。装载的开始阶段,因为要检测静态人脸图片的人脸部位,首先需要用dlib的人脸检测器,用get_frontal_face_detector()获得。然后需要将68点人脸标记模型导入shape_predictor sp,目的就是要对其人脸到一个标准的姿势,接着就是装载DNN模型。然后取每张人脸照片的特征,并将特征和姓名等相关的信息放入FACE_DESC结构中,最后将每张人脸信息结构放入face_desc_vec容器中,这里我只装载了9个明星的人脸信息。

int FACE_RECOGNITION::load_db_faces(void) 
{ 
  intrc = -1; 
  longhFile = 0; 
  struct_finddata_tfileinfo; 
  
  frontal_face_detectordetector =get_frontal_face_detector(); 
  // We will also use a face landmarking model to align faces to a standard pose: (see face_landmark_detection_excpp for an introduction) 
  deserialize("shape_predictor_68_face_landmarksdat") >>sp; 
  
  // And finally we load the DNN responsible for face recognition 
  deserialize("dlib_face_recognition_resnet_model_vdat") >>net; 
  
  if ((hFile =_findfirst("\\faces\\*jpg", &fileinfo)) != -1) 
  { 
    do 
    { 
      if ((fileinfoattrib &_A_ARCH)) 
      { 
        if (strcmp(fileinfoname,"") != 0 && strcmp(fileinfoname,"") != 0) 
        { 
          if (!strcmp(strstr(fileinfoname,"") + 1 , "jpg")) 
          { 
            cout <<"This file is an image file!" <<fileinfoname <<endl; 
            matrix<rgb_pixel>img; 
            charpath[260]; 
            sprintf_s(path,"\\faces\\%s",fileinfoname); 
            load_image(img,path); 
            image_windowwin(img); 
  
            for (autoface :detector(img)) 
            { 
              autoshape =sp(img,face); 
              matrix<rgb_pixel>face_chip; 
              extract_image_chip(img,get_face_chip_details(shape, 150, 25),face_chip); 
              //Record the all this face's information 
              FACE_DESCsigle_face; 
              sigle_faceface_chip =face_chip; 
              sigle_facename =fileinfoname; 
  
              std::vector<matrix<rgb_pixel>>face_chip_vec; 
              std::vector<matrix<float, 0, 1>>face_all; 
  
              face_chip_vecpush_back(move(face_chip)); 
              //Asks the DNN to convert each face image in faces into a 128D vector 
              face_all =net(face_chip_vec); 
  
              //Get the feature of this person 
              std::vector<matrix<float, 0, 1>>::iteratoriter_begin = face_allbegin(), 
                iter_end =face_allend(); 
              if (face_allsize() > 1)break; 
              sigle_faceface_feature = *iter_begin; 
  
              //all the person description into vector 
              face_desc_vecpush_back(sigle_face); 
  
              winadd_overlay(face); 
            } 
          } 
          else 
          { 
            cout <<"This file is not image file!" <<fileinfoname <<endl; 
          }           
        } 
      } 
      else 
      { 
        //filespush_back(passign(path)append("\\")append(fileinfoname)); 
      } 
    } while (_findnext(hFile, &fileinfo) == 0); 
    _findclose(hFile); 
  } 
  returnrc; 
}

人脸检测

人脸检测在人脸识别的应用系统中我认为是至关重要的一环,因为人脸检测的好坏直接影响最终的识别率,如果在人脸检测阶段能做到尽量好的话,系统的识别率会有一个比较大的提升。下面的是人脸检测的具体代码实现(很简陋莫怪),尝试了用Dlib人脸检测,OpenCV人脸检测,还有于仕琪的libfacedetection,比较发现于仕琪的libfacedetection是做人脸检测最好的一个,速度快,并且检测图像效果也很好。

intcapture_face(Matframe,Mat&out)
{
  Matgray;
  Matface;
  intrc = -1;
 
  if (frame.empty() || !frame.data)return -1;
 
  cvtColor(frame,gray,CV_BGR2GRAY);
  int *pResults =NULL;
 
  unsignedchar *pBuffer = (unsignedchar *)malloc(DETECT_BUFFER_SIZE);
  if (!pBuffer)
  {
    fprintf(stderr,"Can not alloc buffer.\n");
    return -1;
  }
  //pResults = facedetect_frontal_tmp((unsigned char*)(gray.ptr(0)), gray.cols, gray.rows, gray.step,
  //  1.2f, 5, 24);
  pResults =facedetect_multiview_reinforce(pBuffer, (unsignedchar*)(gray.ptr(0)),gray.cols,gray.rows, (int)gray.step,
    1.2f, 2, 48, 0, 1);
 
  //printf("%d faces detected.\n", (pResults ? *pResults : 0));//重复运行
                              //print the detection results
  if (pResults !=NULL)
  {
    for (inti = 0;i < (pResults ? *pResults : 0);i++)
    {
      short *p = ((short*)(pResults + 1)) + 6 *i;
      intx =p[0];
      inty =p[1];
      intw =p[2];
      inth =p[3];
      intneighbors =p[4];
 
      Rect_<float>face_rect =Rect_<float>(x,y,w, h);
      face =frame(face_rect);
 
      printf("face_rect=[%d, %d, %d, %d], neighbors=%d\n",x,y, w,h,neighbors);
      Pointleft(x,y);
      Pointright(x +w,y + h);
      cv::rectangle(frame,left,right, Scalar(230, 255, 0), 4);    
    }
    //imshow("frame", frame);
    if (face.empty() || !face.data)
    {
      face_detect_count = 0;
      return -1;
    }
    if (face_detect_count++ > 30)
    {
      imshow("face",face);
      out =face.clone();
      return 0;
    }
  }
  else
  {
    //face is moving, and reset the detect count
    face_detect_count = 0;
  }
 
  returnrc;
}

人脸识别

通过人脸检测函数capture_face()经过处理之后临时保存在工程目录下的cap.jpg,用get_face_chip_details()函数将检测到的目标图片标准化为150*150像素大小,并对人脸进行旋转居中,用extract_image_chip()取得图像的一个拷贝,然后将其存储到自己的图片face_chip中,把的到face_chip放入vect_faces容器中,传送给深度神经网络net,得到捕捉到人脸图片的128D向量特征。最后在事先导入的人脸数据库中遍历与此特征最相近的人脸即可识别出相应的人脸信息。

这种模式的应用,也就是我们所说的1:N应用,1对N是比较考验系统运算能力的,举个例子,现在支付宝账户应该已经是上亿级别的用户,如果你在就餐的时候选择使用支付宝人脸支付,也许在半个小时内服务器也没有找你的脸,这下就悲催,当然在真实应用场景可能是还需要你输入你的名字,这下可能就快多了,毕竟全国可能和你重名的也就了不的几千上万个吧,一搜索,人脸识别再一验证即可。

前面的这些还没有考虑安全的因素,比如说双胞胎啊,化妆啊(网红的年代啊),还有年龄的因素,环境的因素还包括光照、角度等导致的误识别或是识别不出,识别不出的情况还好,如果是误识别对于支付等对于安全性要求极其严苛的应用来说简直就是灾难。所以人脸识别还有很大的局限性 ? 额,好像扯远了。

matrix<rgb_pixel> face_cap;
	//save the capture in the project directory
	load_image(face_cap, ".\\cap.jpg");

	//Display the raw image on the screen
	image_window win1(face_cap);

	frontal_face_detector detector = get_frontal_face_detector();
	std::vector<matrix<rgb_pixel>> vect_faces;

	for (auto face : detector(face_cap))
	{
		auto shape = face_recognize.sp(face_cap, face);
		matrix<rgb_pixel> face_chip;
		extract_image_chip(face_cap, get_face_chip_details(shape, 150, 0.25), face_chip);
		vect_faces.push_back(move(face_chip));
		win1.add_overlay(face);
	}

	if (vect_faces.size() != 1)
	{
		cout <<"Capture face error! face number "<< vect_faces.size() << endl;
		cap.release();
		goto CAPTURE;
	}

	//Use DNN and get the capture face's feature with 128D vector
	std::vector<matrix<float, 0, 1>> face_cap_desc = face_recognize.net(vect_faces);
	//Browse the face feature from the database, and find the match one
	std::pair<double,std::string> candidate_face;
	std::vector<double> len_vec;

	std::vector<std::pair<double, std::string>> candi_face_vec;
	candi_face_vec.reserve(256);

	for (size_t i = 0; i < face_recognize.face_desc_vec.size(); ++i)
	{
		auto len = length(face_cap_desc[0] - face_recognize.face_desc_vec[i].face_feature);
	  if (len < 0.45)
		{
			len_vec.push_back(len);
			candidate_face.first = len;
			candidate_face.second = face_recognize.face_desc_vec[i].name.c_str();
			candi_face_vec.push_back(candidate_face);

#ifdef _FACE_RECOGNIZE_DEBUG
			char buffer[256] = {0};
			sprintf_s(buffer, "Candidate face %s Euclid length %f",
				face_recognize.face_desc_vec[i].name.c_str(),
				len);
			MessageBox(CString(buffer), NULL, MB_YESNO);
#endif
		}
		else
		{
			cout << "This face from database is not match the capture face, continue!" << endl;
		}
	}

	//Find the most similar face
	if (len_vec.size() != 0)
	{
		shellSort(len_vec);

		int i(0);
		for (i = 0; i != len_vec.size(); i++)
		{
			if (len_vec[0] == candi_face_vec[i].first)
				break;
		}

		char buffer[256] = { 0 };
		sprintf_s(buffer, "The face is %s -- Euclid length %f",
			candi_face_vec[i].second.c_str(), candi_face_vec[i].first);
		if (MessageBox(CString(buffer), NULL, MB_YESNO) == IDNO)
		{
			face_record();
		}
	}
	else
	{
		if (MessageBox(CString("Not the similar face been found"), NULL, MB_YESNO) == IDYES)
		{
			face_record();
		}
	}	

	face_detect_count = 0;
	frame.release();
	face.release();

异常处理

当人脸或是物体快速的在摄像头前活动时,会导致系统异常抛出,异常提示如下:

Dlib+OpenCV深度学习人脸识别的方法示例

对于这个问题,我们可以先用C++捕获异常的工具,try和catch工具来捕获异常:

Mat frame;
	Mat face;
	VideoCapture cap(0);
	if (!cap.isOpened()) {
		AfxMessageBox(_T("Please check your USB camera's interface num."));
	}

	try
	{
		while (1)
		{
			check_close(cap);
			cap >> frame;
			if (!frame.empty())
			{
				if (capture_face(frame, face) == 0)
				{
					//convert to IplImage format and then save with .jpg format
					IplImage face_Img;
					face_Img = IplImage(face);
					//save the capture face to the project directory
					cvSaveImage("./cap.jpg", &face_Img);
					break;
				}
				imshow("view", frame);
			}
	
			int c = waitKey(10);
			if ((char)c == 'c') { break; }
		}
	}
	catch (exception& e)
	{
		cout << "\nexception thrown!" << endl;
		cout << e.what() << endl;
#ifdef _CAPTURE_DEBUG
		MessageBox(CString(e.what()), NULL, MB_YESNO);
#endif
		goto CAPTURE;
	}

在catch中将捕获到的异常信息打印出来:

Dlib+OpenCV深度学习人脸识别的方法示例

可以看到,可能是由于摄像头捕获响应速率跟不上的原因,在cap >>frame;的时候得到的frame出现了格式错误,如上图的对话框所示error(-215) 0 < roi.x,也就是说opencv感兴趣区域的x坐标出现了一个负数,而这显然必须是要非负数的地方出现了一个负数的输入,导致OpenCV异常抛出。

没关系我们我们不理会这个异常的frame输入就可以,在异常抛出的catch屏蔽掉对话框的显示,我们即可流畅的采集图像。不理会这个错误的帧输入也就是说直接丢弃这一帧。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python开发之str.format()用法实例分析
Feb 22 Python
Python中字符串的格式化方法小结
May 03 Python
Python实现的将文件每一列写入列表功能示例【测试可用】
Mar 19 Python
python实现log日志的示例代码
Apr 28 Python
解决Shell执行python文件,传参空格引起的问题
Oct 30 Python
Python3使用TCP编写一个简易的文件下载器功能
May 08 Python
Python通过cv2读取多个USB摄像头
Aug 28 Python
tensorflow 实现数据类型转换
Feb 17 Python
Python多线程:主线程等待所有子线程结束代码
Apr 25 Python
解决Pytorch中关于model.eval的问题
May 22 Python
matplotlib画混淆矩阵与正确率曲线的实例代码
Jun 01 Python
python 使用pandas读取csv文件的方法
Dec 24 Python
Python发展简史 Python来历
May 14 #Python
Python基础知识点 初识Python.md
May 14 #Python
Python应用领域和就业形势分析总结
May 14 #Python
python文件写入write()的操作
May 14 #Python
python时间序列按频率生成日期的方法
May 14 #Python
python的依赖管理的实现
May 14 #Python
详解Python3序列赋值、序列解包
May 14 #Python
You might like
php 信息采集程序代码
2009/03/17 PHP
PHP实现会员账号单唯一登录的方法分析
2019/03/07 PHP
jquery(live)中File input的change方法只起一次作用的解决办法
2011/10/21 Javascript
javascript学习笔记(十) js对象 继承
2012/06/19 Javascript
jQuery中对未来的元素绑定事件用bind、live or on
2014/04/17 Javascript
浅析jQuery中调用ajax方法时在不同浏览器中遇到的问题
2014/06/11 Javascript
JavaScript使表单中的内容显示在屏幕上的方法
2015/06/29 Javascript
深入探秘jquery瀑布流的实现
2016/01/30 Javascript
举例讲解jQuery中可见性过滤选择器的使用
2016/04/18 Javascript
AngularJS基础 ng-mousemove 指令简单示例
2016/08/02 Javascript
3种不同的ContextMenu右键菜单实现代码
2016/11/03 Javascript
详解利用exif.js解决ios手机上传竖拍照片旋转90度问题
2016/11/04 Javascript
基于bootstrap-datetimepicker.js不支持IE8的快速解决方法
2016/11/07 Javascript
Vue中自定义全局组件的实现方法
2017/12/08 Javascript
详解一个基于react+webpack的多页面应用配置
2019/01/21 Javascript
简单易扩展可控性强的Jquery转盘抽奖程序
2019/03/16 jQuery
微信小程序登录数据解密及状态维持实例详解
2019/05/06 Javascript
vxe-table vue table 表格组件功能
2019/05/26 Javascript
原生js实现抽奖小游戏
2019/06/27 Javascript
Vue解决echart在element的tab切换时显示不正确问题
2020/08/03 Javascript
浅谈Python的list中的选取范围
2018/11/12 Python
Python基于yield遍历多个可迭代对象
2020/03/12 Python
Python3+SQLAlchemy+Sqlite3实现ORM教程
2021/02/16 Python
关于HTML5你必须知道的28个新特性,新技巧以及新技术
2012/05/28 HTML / CSS
美国最值得信赖的宠物药房:Allivet
2019/03/23 全球购物
龟牌英国商店:Turtle Wax Brand Store UK
2019/07/02 全球购物
物流专业大学生求职信范文
2013/10/28 职场文书
大学生应聘导游自荐信
2014/06/02 职场文书
小学班主任事迹材料
2014/12/17 职场文书
驳回起诉裁定书
2015/05/19 职场文书
2016个人廉洁自律承诺书
2016/03/25 职场文书
导游词之沈阳植物园
2019/11/30 职场文书
如何有效防止sql注入的方法
2021/05/25 SQL Server
Java面试题冲刺第十九天--数据库(4)
2021/08/07 Java/Android
基于PyQt5制作一个群发邮件工具
2022/04/08 Python
Vue OpenLayer测距功能的实现
2022/04/20 Vue.js