2

我正在使用OpenCV進行臉部和眼部檢測。首先,我測試了OpenCV/Samples/c/facedetect.cpp中的示例程序。我給這個facedetect.exe輸入了兩個圖像 - 一個是滿的,另一個是同一個人的裁剪面。現在,facedetect.cpp可以很好地處理完整圖像,但它甚至不會檢測到剪切圖像作爲輸入的人臉。facedetect.cpp檢測裁剪圖像中的眼睛

儘管裁剪後的圖像只包含使用OpenCV人臉檢測器裁剪的人臉,但在某些不好的情況下,我只會得到嘴巴或嘴脣或只是人臉的一部分。所以我的要求是檢查雙眼是否存在於圖像中。

下面是兩個示例圖片一個充滿圖像,我得到正確的輸出: enter image description here

下面是我需要使用facedetect.cpp檢測心目中的形象:

enter image description here

所以我的問題是如何檢測裁剪圖像中的眼睛?

的下面是樣品facedetect.cpp

#include "opencv2/objdetect/objdetect.hpp" 
#include "opencv2/highgui/highgui.hpp" 
#include "opencv2/imgproc/imgproc.hpp" 

#include <iostream> 
#include <stdio.h> 

using namespace std; 
using namespace cv; 

static void help() 
{ 
    cout << "\nThis program demonstrates the cascade recognizer. Now you can use Haar or LBP features.\n" 
      "This classifier can recognize many ~rigid objects, it's most known use is for faces.\n" 
      "Usage:\n" 
      "./facedetect [--cascade=<cascade_path> this is the primary trained classifier such as frontal face]\n" 
       " [--nested-cascade[=nested_cascade_path this an optional secondary classifier such as eyes]]\n" 
       " [--scale=<image scale greater or equal to 1, try 1.3 for example>\n" 
       " [filename|camera_index]\n\n" 
      "see facedetect.cmd for one call:\n" 
      "./facedetect --cascade=\"../../data/haarcascades/haarcascade_frontalface_alt.xml\" --nested-cascade=\"../../data/haarcascades/haarcascade_eye.xml\" --scale=1.3 \n" 
      "Hit any key to quit.\n" 
      "Using OpenCV version " << CV_VERSION << "\n" << endl; 
} 

void detectAndDraw(Mat& img, 
        CascadeClassifier& cascade, CascadeClassifier& nestedCascade, 
        double scale); 

String cascadeName = "../../data/haarcascades/haarcascade_frontalface_alt.xml"; 
String nestedCascadeName = "../../data/haarcascades/haarcascade_eye_tree_eyeglasses.xml"; 

int main(int argc, const char** argv) 
{ 
    CvCapture* capture = 0; 
    Mat frame, frameCopy, image; 
    const String scaleOpt = "--scale="; 
    size_t scaleOptLen = scaleOpt.length(); 
    const String cascadeOpt = "--cascade="; 
    size_t cascadeOptLen = cascadeOpt.length(); 
    const String nestedCascadeOpt = "--nested-cascade"; 
    size_t nestedCascadeOptLen = nestedCascadeOpt.length(); 
    String inputName; 

    help(); 

    CascadeClassifier cascade, nestedCascade; 
    double scale = 1; 

    for(int i = 1; i < argc; i++) 
    { 
     cout << "Processing " << i << " " << argv[i] << endl; 
     if(cascadeOpt.compare(0, cascadeOptLen, argv[i], cascadeOptLen) == 0) 
     { 
      cascadeName.assign(argv[i] + cascadeOptLen); 
      cout << " from which we have cascadeName= " << cascadeName << endl; 
     } 
     else if(nestedCascadeOpt.compare(0, nestedCascadeOptLen, argv[i], nestedCascadeOptLen) == 0) 
     { 
      if(argv[i][nestedCascadeOpt.length()] == '=') 
       nestedCascadeName.assign(argv[i] + nestedCascadeOpt.length() + 1); 
      if(!nestedCascade.load(nestedCascadeName)) 
       cerr << "WARNING: Could not load classifier cascade for nested objects" << endl; 
     } 
     else if(scaleOpt.compare(0, scaleOptLen, argv[i], scaleOptLen) == 0) 
     { 
      if(!sscanf(argv[i] + scaleOpt.length(), "%lf", &scale) || scale < 1) 
       scale = 1; 
      cout << " from which we read scale = " << scale << endl; 
     } 
     else if(argv[i][0] == '-') 
     { 
      cerr << "WARNING: Unknown option %s" << argv[i] << endl; 
     } 
     else 
      inputName.assign(argv[i]); 
    } 

    if(!cascade.load(cascadeName)) 
    { 
     cerr << "ERROR: Could not load classifier cascade" << endl; 
     cerr << "Usage: facedetect [--cascade=<cascade_path>]\n" 
      " [--nested-cascade[=nested_cascade_path]]\n" 
      " [--scale[=<image scale>\n" 
      " [filename|camera_index]\n" << endl ; 
     return -1; 
    } 

    if(inputName.empty() || (isdigit(inputName.c_str()[0]) && inputName.c_str()[1] == '\0')) 
    { 
     capture = cvCaptureFromCAM(inputName.empty() ? 0 : inputName.c_str()[0] - '0'); 
     int c = inputName.empty() ? 0 : inputName.c_str()[0] - '0' ; 
     if(!capture) cout << "Capture from CAM " << c << " didn't work" << endl; 
    } 
    else if(inputName.size()) 
    { 
     image = imread(inputName, 1); 
     if(image.empty()) 
     { 
      capture = cvCaptureFromAVI(inputName.c_str()); 
      if(!capture) cout << "Capture from AVI didn't work" << endl; 
     } 
    } 
    else 
    { 
     image = imread("lena.jpg", 1); 
     if(image.empty()) cout << "Couldn't read lena.jpg" << endl; 
    } 

    cvNamedWindow("result", 1); 

    if(capture) 
    { 
     cout << "In capture ..." << endl; 
     for(;;) 
     { 
      IplImage* iplImg = cvQueryFrame(capture); 
      frame = iplImg; 
      if(frame.empty()) 
       break; 
      if(iplImg->origin == IPL_ORIGIN_TL) 
       frame.copyTo(frameCopy); 
      else 
       flip(frame, frameCopy, 0); 

      detectAndDraw(frameCopy, cascade, nestedCascade, scale); 

      if(waitKey(10) >= 0) 
       goto _cleanup_; 
     } 

     waitKey(0); 

_cleanup_: 
     cvReleaseCapture(&capture); 
    } 
    else 
    { 
     cout << "In image read" << endl; 
     if(!image.empty()) 
     { 
      detectAndDraw(image, cascade, nestedCascade, scale); 
      waitKey(0); 
     } 
     else if(!inputName.empty()) 
     { 
      /* assume it is a text file containing the 
      list of the image filenames to be processed - one per line */ 
      FILE* f = fopen(inputName.c_str(), "rt"); 
      if(f) 
      { 
       char buf[1000+1]; 
       while(fgets(buf, 1000, f)) 
       { 
        int len = (int)strlen(buf), c; 
        while(len > 0 && isspace(buf[len-1])) 
         len--; 
        buf[len] = '\0'; 
        cout << "file " << buf << endl; 
        image = imread(buf, 1); 
        if(!image.empty()) 
        { 
         detectAndDraw(image, cascade, nestedCascade, scale); 
         c = waitKey(0); 
         if(c == 27 || c == 'q' || c == 'Q') 
          break; 
        } 
        else 
        { 
         cerr << "Aw snap, couldn't read image " << buf << endl; 
        } 
       } 
       fclose(f); 
      } 
     } 
    } 

    cvDestroyWindow("result"); 

    return 0; 
} 

void detectAndDraw(Mat& img, 
        CascadeClassifier& cascade, CascadeClassifier& nestedCascade, 
        double scale) 
{ 
    int i = 0; 
    double t = 0; 
    vector<Rect> faces; 
    const static Scalar colors[] = { CV_RGB(0,0,255), 
     CV_RGB(0,128,255), 
     CV_RGB(0,255,255), 
     CV_RGB(0,255,0), 
     CV_RGB(255,128,0), 
     CV_RGB(255,255,0), 
     CV_RGB(255,0,0), 
     CV_RGB(255,0,255)} ; 
    Mat gray, smallImg(cvRound (img.rows/scale), cvRound(img.cols/scale), CV_8UC1); 

    cvtColor(img, gray, CV_BGR2GRAY); 
    resize(gray, smallImg, smallImg.size(), 0, 0, INTER_LINEAR); 
    equalizeHist(smallImg, smallImg); 

    t = (double)cvGetTickCount(); 
    cascade.detectMultiScale(smallImg, faces, 
     1.1, 2, 0 
     //|CV_HAAR_FIND_BIGGEST_OBJECT 
     //|CV_HAAR_DO_ROUGH_SEARCH 
     |CV_HAAR_SCALE_IMAGE 
     , 
     Size(30, 30)); 
    t = (double)cvGetTickCount() - t; 
    printf("detection time = %g ms\n", t/((double)cvGetTickFrequency()*1000.)); 
    for(vector<Rect>::const_iterator r = faces.begin(); r != faces.end(); r++, i++) 
    { 
     Mat smallImgROI; 
     vector<Rect> nestedObjects; 
     Point center; 
     Scalar color = colors[i%8]; 
     int radius; 
     center.x = cvRound((r->x + r->width*0.5)*scale); 
     center.y = cvRound((r->y + r->height*0.5)*scale); 
     radius = cvRound((r->width + r->height)*0.25*scale); 
     circle(img, center, radius, color, 3, 8, 0); 
     if(nestedCascade.empty()) 
      continue; 
     smallImgROI = smallImg(*r); 
     nestedCascade.detectMultiScale(smallImgROI, nestedObjects, 
      1.1, 2, 0 
      //|CV_HAAR_FIND_BIGGEST_OBJECT 
      //|CV_HAAR_DO_ROUGH_SEARCH 
      //|CV_HAAR_DO_CANNY_PRUNING 
      |CV_HAAR_SCALE_IMAGE 
      , 
      Size(30, 30)); 
     for(vector<Rect>::const_iterator nr = nestedObjects.begin(); nr != nestedObjects.end(); nr++) 
     { 
      center.x = cvRound((r->x + nr->x + nr->width*0.5)*scale); 
      center.y = cvRound((r->y + nr->y + nr->height*0.5)*scale); 
      radius = cvRound((nr->width + nr->height)*0.25*scale); 
      circle(img, center, radius, color, 3, 8, 0); 
     } 
    } 
    cv::imshow("result", img); 
} 

回答

2

的代碼的原始例如通過cascade.detectMultiScale首先檢測的面孔,則通過nestedCascade.detectMultiScale發現眼睛在檢測到的面部。

如果您只需要檢測眼睛,請在完整圖像上使用nestedCascade.detectMultiScale

2

如果您有檢測到的臉部(正面),則使用人體測量關係估計左右眼區域的粗略位置,如下所示。 enter image description hereenter image description here

+0

非常感謝您的回覆。 +1的方式提出。我的意圖不是定位眼睛,而是確認裁剪圖像中眼睛的存在。我打算這樣做,以確認裁剪是否已通過我的臉部檢測程序正確完成。希望你現在得到我?再次爲您的迴應偉大的th ... ... – 2vision2

+0

之後,你可以做一些圓檢測或橢圓檢測。 – isrish

0

更改級聯分類名稱:

String cascadeName = "../../data/haarcascades/haarcascade_frontalface_alt.xml"; 

改變這

String cascadeName = "../../data/haarcascades/haarcascade_eye_tree_eyeglasses.xml"; 

,並通過圖像作爲參數的位置。