侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

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

技术  /  管理员 发布于 7年前   222

前言

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

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

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

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

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

人脸数据库导入

人脸数据导入,也就是说我在系统启动之初,需要导入我的人脸数据库,也就是前面的那些明星的正面照。装载的开始阶段,因为要检测静态人脸图片的人脸部位,首先需要用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是比较考验系统运算能力的,举个例子,现在支付宝账户应该已经是上亿级别的用户,如果你在就餐的时候选择使用支付宝人脸支付,也许在半个小时内服务器也没有找你的脸,这下就悲催,当然在真实应用场景可能是还需要你输入你的名字,这下可能就快多了,毕竟全国可能和你重名的也就了不的几千上万个吧,一搜索,人脸识别再一验证即可。

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

matrix<rgb_pixel> face_cap;//save the capture in the project directoryload_image(face_cap, ".\\cap.jpg");//Display the raw image on the screenimage_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 vectorstd::vector<matrix<float, 0, 1>> face_cap_desc = face_recognize.net(vect_faces);//Browse the face feature from the database, and find the match onestd::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_DEBUGchar 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 faceif (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();

异常处理

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

对于这个问题,我们可以先用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 formatIplImage face_Img;face_Img = IplImage(face);//save the capture face to the project directorycvSaveImage("./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_DEBUGMessageBox(CString(e.what()), NULL, MB_YESNO);#endifgoto CAPTURE;}

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

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

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

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


  • 上一条:
    使用selenium模拟登录解决滑块验证问题的实现
    下一条:
    OpenCV图像颜色反转算法详解
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 2024.07.09日OpenAI将终止对中国等国家和地区API服务(0个评论)
    • 2024/6/9最新免费公益节点SSR/V2ray/Shadowrocket/Clash节点分享|科学上网|免费梯子(1个评论)
    • 国外服务器实现api.openai.com反代nginx配置(0个评论)
    • 2024/4/28最新免费公益节点SSR/V2ray/Shadowrocket/Clash节点分享|科学上网|免费梯子(1个评论)
    • 近期文章
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2017-07
    • 2017-08
    • 2017-09
    • 2018-01
    • 2018-07
    • 2018-08
    • 2018-09
    • 2018-12
    • 2019-01
    • 2019-02
    • 2019-03
    • 2019-04
    • 2019-05
    • 2019-06
    • 2019-07
    • 2019-08
    • 2019-09
    • 2019-10
    • 2019-11
    • 2019-12
    • 2020-01
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-08
    • 2020-09
    • 2020-10
    • 2020-11
    • 2021-04
    • 2021-05
    • 2021-06
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-12
    • 2022-01
    • 2022-02
    • 2022-03
    • 2022-04
    • 2022-05
    • 2022-06
    • 2022-07
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-08
    • 2023-09
    • 2023-10
    • 2023-12
    • 2024-02
    • 2024-04
    • 2024-05
    • 2024-06
    • 2025-02
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客