天天看點

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 <opencv2/core.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/face.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/core/utility.hpp>
#include <opencv2/core/ocl.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/calib3d.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/flann.hpp>
#include <opencv2/xobjdetect.hpp>
#include <opencv2/face/predict_collector.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/ml.hpp>
#include <opencv2/objdetect.hpp>

#include <iostream>
#include <iostream>
#include <sstream>

#include "QDebug"
           

1.人臉檢測

人臉檢測是在圖像中定位人臉區域的過程,這一步不關心人是誰,隻是關心是不是人臉。

讀入圖像—>灰階圖像轉換—>直方圖均衡化—>分類器進行對象檢測

(1)當配置好OpenCV庫與Qt Creater開發環境後,調用OpenCV的庫函數,調用cv::Mat image = cv::imread(“1.jpg”, cv::IMREAD_COLOR);打開圖檔,其中:IMREAD_COLOR為圖檔的通道描述,将打開的圖檔以RGB三通道的方式讀取

OpenCV實作人臉識别一、環境搭建二、人臉識别具體實作

(2)灰階圖像轉換

灰階圖像轉換一方面是為了簡化矩陣,增強運算速度,一方面是OpenCV中簡單的識别算法對于顔色的依賴性不強。

(3)直方圖均衡化

使用直方圖均衡化改善圖像對比圖和亮度

OpenCV實作人臉識别一、環境搭建二、人臉識别具體實作

(4)分類器

OpenCV提供了對象檢測器,通過LBP的特征檢測器可以能通過訓練從大的圖像集中找到人臉,這些圖像存在于XML檔案中。一般情況下圖像分類檢測器通常至少使用1000個獨特的人臉圖像和10000個非人臉圖像作為訓練才能産生訓練資料。OpenCV自帶訓練好的額LBP檢測器供使用者使用,隻要加載不同級聯的分類器的XML檔案給對象檢測器就可以檢測正面人臉、輪廓的面孔,眼睛或鼻子。為了便于後面的人臉圖像預處理。在這裡檢測出人臉和人眼。加載OpenCV給定的XML資料集,使用矩形窗标記出人臉和眼睛。其中人臉的資料集在其中haarcascade_frontalface_alt.xml檔案中,由于個人佩戴眼鏡,是以選擇眼睛所在的資料集為haarcascade_eye_tree_eyeglasses.xml,打開資料集會發現大量訓練好的資料。檢測時是灰色圖像檢測,我最終在彩色圖像上顯示效果圖如下所示。

OpenCV實作人臉識别一、環境搭建二、人臉識别具體實作

人臉檢測代碼

//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<cv::Rect> 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<cv::Rect> 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實作人臉識别一、環境搭建二、人臉識别具體實作

識别出人眼截取掉額頭和下巴資訊,對人臉進行平滑濾波。

處理代碼如下

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<double>(0,2) += ex;
     rot_mat.at<double>(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<FaceRecognizer> 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 "<<faceResult;
           

最後預測結果為

OpenCV實作人臉識别一、環境搭建二、人臉識别具體實作

其中打開xml檔案中的訓練資料為

OpenCV實作人臉識别一、環境搭建二、人臉識别具體實作