OpenCV实现人脸识别

这学期上《数字图像处理》这门课,然后想做一个人脸识别系统,从博客和相关书籍中看到OpenCV实现人脸识别系统十分简单。下面就OpenCV的安装及实现人脸识别功能进行说明。由于时间限制,本程序各个步骤未封装成函数,后续更新将把每步更新成函数。本人水平有限,如有错误,欢迎改正。
参考书籍《深入理解OpenCV—实用计算机视觉项目解析》

一、环境搭建

我是在Ubuntu 18.04环境下安装OpenCV库的,看许多教程都是使用OpenCV+VS开发环境,我这里使用的是Qt+OpenCV进行开发。每个人电脑不同,所以安装时遇到的问题也就各种各样。第一部分仅做参考。
1.安装OpenCV和Cmake各种包
2.解压OpenCV和OpenCV_contrib文件。(其中人脸识别需要用到opencv_contrib中的face包,opencv包内含有人脸检测的文件)
3.在opencv中创建build文件夹,安装cmake,将opencv_contrib包链接起来。其shell命令为

cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local -D OPENCV_EXTRA_MODULES_PATH=/media/antoniodc/Linux_File/opencv_contrib/modules ..

如果显示不存在,将-D中间的空格删除。/usr/local为安装的目的文件夹。/media/antoniodc/Linux_File/opencv_contrib/modules …为我contrib包的位置,根据每个人电脑情况来做。勿忘最后空格加…
4.make成功后,配置环境变量。

二、人脸识别具体实现

其中引用的头文件为

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

#include "QDebug"

1.人脸检测

人脸检测是在图像中定位人脸区域的过程,这一步不关心人是谁,只是关心是不是人脸。
读入图像—>灰度图像转换—>直方图均衡化—>分类器进行对象检测
(1)当配置好OpenCV库与Qt Creater开发环境后,调用OpenCV的库函数,调用cv::Mat image = cv::imread(“1.jpg”, cv::IMREAD_COLOR);打开图片,其中:IMREAD_COLOR为图片的通道描述,将打开的图片以RGB三通道的方式读取
OpenCV实现人脸识别_第1张图片
(2)灰度图像转换
灰度图像转换一方面是为了简化矩阵,增强运算速度,一方面是OpenCV中简单的识别算法对于颜色的依赖性不强。

(3)直方图均衡化
使用直方图均衡化改善图像对比图和亮度

OpenCV实现人脸识别_第2张图片
(4)分类器
OpenCV提供了对象检测器,通过LBP的特征检测器可以能通过训练从大的图像集中找到人脸,这些图像存在于XML文件中。一般情况下图像分类检测器通常至少使用1000个独特的人脸图像和10000个非人脸图像作为训练才能产生训练数据。OpenCV自带训练好的额LBP检测器供用户使用,只要加载不同级联的分类器的XML文件给对象检测器就可以检测正面人脸、轮廓的面孔,眼睛或鼻子。为了便于后面的人脸图像预处理。在这里检测出人脸和人眼。加载OpenCV给定的XML数据集,使用矩形窗标记出人脸和眼睛。其中人脸的数据集在其中haarcascade_frontalface_alt.xml文件中,由于个人佩戴眼镜,所以选择眼睛所在的数据集为haarcascade_eye_tree_eyeglasses.xml,打开数据集会发现大量训练好的数据。检测时是灰色图像检测,我最终在彩色图像上显示效果图如下所示。
OpenCV实现人脸识别_第3张图片

人脸检测代码

    //step 1 -----read picture
    cv::Mat printImg;
    cv::Mat image = cv::imread("3.jpg", cv::IMREAD_COLOR);   //read picture
    if(image.empty())
        qDebug()<<"init photo is empty";

    resize(image,printImg,Size(600,800));
    //cv::imshow("1",printImg);

    cv::Mat image_gray;
    cv::Mat Myface;

    //step 2 -----convert gray image
    cv::cvtColor(image, image_gray,cv::COLOR_BGR2GRAY);       
    resize(image_gray,printImg,Size(600,800));
    //cv::imshow("2",printImg);

    //step 3 ------equalizeHis
    cv::equalizeHist(image_gray, image_gray);                   //to improve contrast ratio and brightness
    resize(image_gray,printImg,Size(600,800));
    //cv::imshow("3",printImg);

    //step 4-----detection face
    cv::CascadeClassifier face_cascade;
    if (!face_cascade.load("haarcascade_frontalface_alt.xml"))  //需要将xml文档放在自己指定的路径下
    {
        qDebug()<< "Load haarcascade_face.xml failed!";
    }
     std::vector faceRect;
     face_cascade.detectMultiScale(image_gray, faceRect, 1.1, 3, 0 | CASCADE_SCALE_IMAGE, Size(30, 30));      //detect face
     //if you only want get one face pelase   set  CASCADE_FIND_BIGGEST_OBJECT |CASCADE_DO_ROUGH_SEARCH;
      for (size_t i = 0; i < faceRect.size(); i++)
      {
         cv::Mat faceROI = image(faceRect[i]);

         if(faceROI.cols > 100)
         {
             resize(faceROI, Myface, Size(500, 500));            //调整图像大小为500 * 500
             //imshow("4", Myface);
         }
         rectangle(image, faceRect[i], Scalar(0, 0, 255));      //用矩形画出检测到的位置
      }
    //cv::imshow("5",image);                                    //show the image of detect face

2.人脸预处理

脸预处理是调整人脸图像,使其看起来更加清楚,也更加符合人识别人脸的过程。
人眼检测对于人脸的预处理非常有用,对于人脸而言,面部表情、光照条件、摄像机的属性、与相机的距离等都会发生变化,但是人眼是水平的,并且对称分布在人脸上,两只眼睛在人脸的位置和大小是相当标准的。当人脸检测器将别的对象当成人脸时,可以通过眼部检测丢弃这种误判。人脸检测完成后,截取出人脸所在的区域,为了保持人脸在水平线上,将检测到眼睛中心位置的x为准线,将图像进行旋转和平移操作,以使眼睛能被对齐,保证训练图像和测试图像在同一位置。由于人脸识别的特征都在虹膜、鼻翼、嘴角等面像五官轮廓的大小、位置、距离等属性。对于截取的人脸图像在经过删除额头、下巴、耳朵和北京的操作,对于图像中含有噪声的影响,使用双边滤波器进行平滑操作减少噪声。最后使用椭圆掩码将图像中剩余的头发和人脸的背景去掉。
检测人眼代码为

 cv::CascadeClassifier eye_cascade;
    if (!eye_cascade.load("haarcascade_eye_tree_eyeglasses.xml"))  //wear glass
    {
        qDebug()<< "Load haarcascade_face.xml failed!";
    }
     std::vector eyeRect;
     eye_cascade.detectMultiScale(Myface, eyeRect, 1.1, 4, 0 | CASCADE_SCALE_IMAGE, Size(30, 30));      //detect eye  //4 depends the correct rate of eye,the num is bigger,the correct rate is higher
      int eye_num = 0;
      cv::Point Myeye[2];                                         //eye center
      for (size_t i = 0; i < eyeRect.size(); i++)
      {
         rectangle(Myface, eyeRect[i], Scalar(0, 255, 255));      //用矩形画出检测到的位置
         Myeye[eye_num] =getCenterPoint(eyeRect[i]);
         cv::circle(Myface,Myeye[eye_num],4,cv::Scalar(255,0,255));
         eye_num = eye_num+1;
      }

      resize(Myface,printImg,Size(500,500));
      //cv::imshow("6",printImg);

找到正方形中心代码为

cv::Point getCenterPoint(Rect rect)
{
    cv::Point cpt;
    cpt.x = rect.x + cvRound(rect.width/2.0);
    cpt.y = rect.y + cvRound(rect.height/2.0);
    return cpt;
}

人脸预处理其步骤效果如图
OpenCV实现人脸识别_第4张图片
识别出人眼截取掉额头和下巴信息,对人脸进行平滑滤波。
处理代码如下

     cv::Point2f eyesCenter;
     eyesCenter.x = (Myeye[0].x+Myeye[1].x)*0.5f;
     eyesCenter.y = (Myeye[0].y+Myeye[1].y)*0.5f;

     double dy  = (Myeye[1].y - Myeye[0].y);
     double dx  = (Myeye[1].x - Myeye[0].x);
     double len = sqrt(dx*dx + dy*dy);

     double angle=atan2(dy,dx)*180.0/CV_PI;

     const double DESIRED_LEFT_EYE_X = 0.16;
     const double DESIRED_LEFT_EYE_Y = 0.14;
     const double DESIRED_RIGHT_EYE_X = (1.0f - 0.16);

     const int DESIRED_FACE_WIDTH = 150;
     const int DESIRED_FACE_HEIGHT= 150;

     double desiredLen = (DESIRED_RIGHT_EYE_X - 0.16);
     double scale = desiredLen * DESIRED_FACE_WIDTH /len;

     cv::Mat rot_mat = getRotationMatrix2D(eyesCenter,angle,scale);
     double ex = DESIRED_FACE_WIDTH * 0.5f - eyesCenter.x;
     double ey = DESIRED_FACE_HEIGHT* DESIRED_LEFT_EYE_Y - eyesCenter.y;

     rot_mat.at(0,2) += ex;
     rot_mat.at(1,2) += ey;

     cv::Mat warped = Mat(DESIRED_FACE_HEIGHT,DESIRED_FACE_WIDTH,CV_8U,Scalar(128));
     warpAffine(Myface,warped,rot_mat,warped.size());

      cv::Mat Smoothing_Myface;
      bilateralFilter(warped,Smoothing_Myface,0,20.0,2.0);
      resize(Smoothing_Myface,printImg,Size(500,500));
      //cv::imshow("smoothing processing",printImg);           //smoothing processing

      cv::Mat mask = Mat(warped.size(),CV_8UC1,Scalar(255));
      double dw = DESIRED_FACE_WIDTH;
      double dh = DESIRED_FACE_HEIGHT;

      Point faceCenter = Point(cvRound(dw*0.5),cvRound(dh*0.4));
      Size  size  = Size(cvRound(dw*0.5),cvRound(dh*0.8));
      ellipse(mask,faceCenter,size,0,0,360,Scalar(0),cv::FILLED);

      Smoothing_Myface.setTo(Scalar(0),mask);
      resize(Smoothing_Myface,printImg,Size(500,500));

      cv::Mat preprocess_IMG;
      resize(Smoothing_Myface,preprocess_IMG,Size(128,128));
      cv::cvtColor(preprocess_IMG,preprocess_IMG,cv::COLOR_BGR2GRAY);

      resize(mask,printImg,Size(500,500));
      //cv::imshow("mask",printImg);           //add mask processing

3.训练数据及实现人脸识别

这里用了OpenCV中PCA分类器。PCA通过方差信息实现降维操作。
其流程为:导入样本图片---->增加标签----->人脸分类器训练----->保存数据------>识别器预测
代码为

      //step 1------>input sample
      cv::Mat src_1 = imread("./cuiandong/1.JPG",cv::IMREAD_GRAYSCALE);         //Me
      cv::Mat src_2 = imread("./cuiandong/C1.jpg",cv::IMREAD_GRAYSCALE);
      cv::Mat src_3 = imread("./cuiandong/C2.jpg",cv::IMREAD_GRAYSCALE);
      cv::Mat src_4 = imread("./cuiandong/C3.jpg",cv::IMREAD_GRAYSCALE);
      cv::Mat src_5 = imread("./cuiandong/C4.jpg",cv::IMREAD_GRAYSCALE);
      cv::Mat src_6 = imread("./cuiandong/2.jpeg",cv::IMREAD_GRAYSCALE);        //Liu Shishi
      cv::Mat src_7 = imread("./cuiandong/3.jpeg",cv::IMREAD_GRAYSCALE);        //nomal man
      cv::Mat src_8 = imread("./cuiandong/4.jpg",cv::IMREAD_GRAYSCALE);         //Liu Yifei
      cv::Mat src_9 = imread("./cuiandong/5.jpg",cv::IMREAD_GRAYSCALE);         //nomal woman

      //modify size
      cv::resize(src_1,src_1,Size(128,128));
      cv::resize(src_2,src_2,Size(128,128));
      cv::resize(src_3,src_3,Size(128,128));
      cv::resize(src_4,src_4,Size(128,128));
      cv::resize(src_5,src_5,Size(128,128));
      cv::resize(src_6,src_6,Size(128,128));
      cv::resize(src_7,src_7,Size(128,128));
      cv::resize(src_8,src_8,Size(128,128));
      cv::resize(src_9,src_9,Size(128,128));

      images.push_back(src_1);
      images.push_back(src_2);
      images.push_back(src_3);
      images.push_back(src_4);
      images.push_back(src_5);
      images.push_back(src_6);
      images.push_back(src_7);
      images.push_back(src_8);
      images.push_back(src_9);

      //step 2 -----add labels
      labels.push_back(1);
      labels.push_back(1);
      labels.push_back(1);
      labels.push_back(1);
      labels.push_back(1);
      labels.push_back(2);
      labels.push_back(3);
      labels.push_back(4);
      labels.push_back(5);


      //step 3 -----faceRecognzer
      Ptr fisherClass = FisherFaceRecognizer::create();

      fisherClass->train(images,labels);
      //step 4-----save xml
      fisherClass->save("fisherClass.xml");

      //step 5-----predict
      int faceResult = fisherClass->predict(preprocess_IMG);
      qDebug()<<"the face result is "<

最后预测结果为
OpenCV实现人脸识别_第5张图片

其中打开xml文件中的训练数据为
OpenCV实现人脸识别_第6张图片

你可能感兴趣的:(数字图像处理,人脸识别,OpenCV)