OpenCV Programming on Windows

OpenCVを用いて顔+目+笑顔を認識する (cv::CascadeClassifier)


2016.07.19: created by

前提として理解しておくべき知識


顔認識

顔認識によって得られた矩形領域の下半分をROI (Region of Interest)として取り出して、 その領域に笑顔の認識を適用します。 笑顔の度合い $intensityZeroOne$ (0.0 〜 1.0) は次の式で計算されます。
$int \quad smile\_neighbors = 発見された矩形領域の数$
$int \quad max\_neighbors=-1;$
$static \quad int\quad min\_neighbors=-1;$
$if \quad (min\_neighbors == -1) \quad min\_neighbors = smile\_neighbors;$
$max\_neighbors = MAX(max\_neighbors, smile\_neighbors);$
$\displaystyle float \quad intensityZeroOne = \frac{smile\_neighbors - min\_neighbors}{max\_neighbors - min\_neighbors + 1}$


プログラム作成の手順

  1. OpenCVを用いて顔と目を認識する (cv::CascadeClassifier)」 の Visual Studio 2015 のプロジェクトを用いて作成します。
  2. OpenCV と共に配布されている 「笑顔認識用の xml ファイル」 haarcascade_smile.xml を main.cpp と同じフォルダにコピーします。 そして、プロジェクトに追加します。
  3. これで、プロジェクト内の xml ファイルは3つになります。

  4. main.cppの内容を以下のように変更します。
  5. main.cpp (赤字の部分)
    #include <iostream>
    #include <sstream>
    
    #include <opencv2/opencv.hpp>
    
    using namespace std;
    
    void doJob() {
      string path = "";
      string cascadeName = "haarcascade_frontalface_alt.xml";
      string cascadeName2 = "haarcascade_eye.xml";
      string cascadeName3 = "haarcascade_smile.xml";
      cv::CascadeClassifier cascade, cascade2, cascade3;
      if (!cascade.load(path + cascadeName)) throw runtime_error(cascadeName + " not found");
      if (!cascade2.load(path + cascadeName2)) throw runtime_error(cascadeName2 + " not found");
      if (!cascade3.load(path + cascadeName3)) throw runtime_error(cascadeName3 + " not found");
    
      cv::VideoCapture cap(0);
      if (!cap.isOpened()) throw runtime_error("VideoCapture open failed");  
      cv::Mat image;
      cv::Mat gray;
      while (1) {
        cap >> image;
        cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
        equalizeHist(gray, gray);
        vector<cv::Rect> founds, founds2, founds3;
        cascade.detectMultiScale(gray, founds, 1.1, 2, 0 | cv::CASCADE_SCALE_IMAGE, cv::Size(30, 30));
        for (auto faceRect: founds) {
          cv::rectangle(image, faceRect, cv::Scalar(0, 0, 255), 2);
          cv::Mat roi = gray(faceRect);
          cascade2.detectMultiScale(roi, founds2, 1.1, 2, 0 | cv::CASCADE_SCALE_IMAGE, cv::Size(30, 30));
          for (auto eyeRect: founds2) {
            cv::Rect rect(faceRect.x + eyeRect.x, faceRect.y + eyeRect.y, eyeRect.width, eyeRect.height);
            cv::rectangle(image, rect, cv::Scalar(0, 255, 0), 2);
          }
          cv::Rect halfRect = faceRect;
          halfRect.y += faceRect.height/2;
          halfRect.height = faceRect.height/2 - 1;  // under half of face
          cv::Mat roi2 = gray(halfRect);
          cascade3.detectMultiScale(roi2, founds3, 1.1, 0, 0 | cv::CASCADE_SCALE_IMAGE, cv::Size(30, 30));
          const int smile_neighbors = (int)founds3.size();
          static int max_neighbors=-1;
          static int min_neighbors=-1;
          if (min_neighbors == -1) min_neighbors = smile_neighbors;
          max_neighbors = MAX(max_neighbors, smile_neighbors);
          float intensityZeroOne = ((float)smile_neighbors - min_neighbors) / (max_neighbors - min_neighbors + 1);
          cv::Rect meter(faceRect.x, faceRect.y-20, (int)100*intensityZeroOne, 20);
          cv::rectangle(image, meter, cv::Scalar(255, 0, 0), -1);
          cv::Rect meterFull(faceRect.x, faceRect.y-20, 100, 20);
          cv::rectangle(image, meterFull, cv::Scalar(255, 0, 0), 1);
        }
        cv::imshow("video", image);
        auto key = cv::waitKey(1);
        if (key == 'q') break;
      }
      cv::destroyAllWindows();
    }
    
    int main(int argc, char** argv) {
      try {
        doJob();
      }
      catch (exception &ex) {
        cout << ex.what() << endl;
        string s;
        cin >> s;
      }
      return 0;
    }
    
    
  6. プログラムを実行するとRGB画像が表示されます。"q"キーで終了します。
  7. 顔認識した矩形領域の上に幅最大100ドットで青い横棒で、笑いの度合いが表示されます。 左の画面は笑顔の度合いが非常に低く、右の画面は笑顔の度合いがかなり高くなっています。


      

  8. サンプルのプロジェクトはこちら


http://nw.tsuda.ac.jp/