samples/facedetect.cpp#

This program demonstrates usage of the Cascade classifier class

  1#include "opencv2/xobjdetect.hpp"
  2#include "opencv2/highgui.hpp"
  3#include "opencv2/imgproc.hpp"
  4#include "opencv2/videoio.hpp"
  5#include <iostream>
  6
  7using namespace std;
  8using namespace cv;
  9
 10static void help(const char** argv)
 11{
 12    cout << "\nThis program demonstrates the use of cv::CascadeClassifier class to detect objects (Face + eyes). You can use Haar or LBP features.\n"
 13            "This classifier can recognize many kinds of rigid objects, once the appropriate classifier is trained.\n"
 14            "It's most known use is for faces.\n"
 15            "Usage:\n"
 16        <<  argv[0]
 17        <<  "   [--cascade=<cascade_path> this is the primary trained classifier such as frontal face]\n"
 18            "   [--nested-cascade[=nested_cascade_path this an optional secondary classifier such as eyes]]\n"
 19            "   [--scale=<image scale greater or equal to 1, try 1.3 for example>]\n"
 20            "   [--try-flip]\n"
 21            "   [filename|camera_index]\n\n"
 22            "example:\n"
 23        <<  argv[0]
 24        <<  " --cascade=\"data/haarcascades/haarcascade_frontalface_alt.xml\" --nested-cascade=\"data/haarcascades/haarcascade_eye_tree_eyeglasses.xml\" --scale=1.3\n\n"
 25            "During execution:\n\tHit any key to quit.\n"
 26            "\tUsing OpenCV version " << CV_VERSION << "\n" << endl;
 27}
 28
 29void detectAndDraw( Mat& img, CascadeClassifier& cascade,
 30                    CascadeClassifier& nestedCascade,
 31                    double scale, bool tryflip );
 32
 33string cascadeName;
 34string nestedCascadeName;
 35
 36int main( int argc, const char** argv )
 37{
 38    VideoCapture capture;
 39    Mat frame, image;
 40    string inputName;
 41    bool tryflip;
 42    CascadeClassifier cascade, nestedCascade;
 43    double scale;
 44
 45    cv::CommandLineParser parser(argc, argv,
 46        "{help h||}"
 47        "{cascade|data/haarcascades/haarcascade_frontalface_alt.xml|}"
 48        "{nested-cascade|data/haarcascades/haarcascade_eye_tree_eyeglasses.xml|}"
 49        "{scale|1|}{try-flip||}{@filename||}"
 50    );
 51    if (parser.has("help"))
 52    {
 53        help(argv);
 54        return 0;
 55    }
 56    cascadeName = parser.get<string>("cascade");
 57    nestedCascadeName = parser.get<string>("nested-cascade");
 58    scale = parser.get<double>("scale");
 59    if (scale < 1)
 60        scale = 1;
 61    tryflip = parser.has("try-flip");
 62    inputName = parser.get<string>("@filename");
 63    if (!parser.check())
 64    {
 65        parser.printErrors();
 66        return 0;
 67    }
 68    if (!nestedCascade.load(samples::findFileOrKeep(nestedCascadeName)))
 69        cerr << "WARNING: Could not load classifier cascade for nested objects" << endl;
 70    if (!cascade.load(samples::findFile(cascadeName)))
 71    {
 72        cerr << "ERROR: Could not load classifier cascade" << endl;
 73        help(argv);
 74        return -1;
 75    }
 76    if( inputName.empty() || (isdigit(inputName[0]) && inputName.size() == 1) )
 77    {
 78        int camera = inputName.empty() ? 0 : inputName[0] - '0';
 79        if(!capture.open(camera))
 80        {
 81            cout << "Capture from camera #" <<  camera << " didn't work" << endl;
 82            return 1;
 83        }
 84    }
 85    else if (!inputName.empty())
 86    {
 87        image = imread(samples::findFileOrKeep(inputName), IMREAD_COLOR);
 88        if (image.empty())
 89        {
 90            if (!capture.open(samples::findFileOrKeep(inputName)))
 91            {
 92                cout << "Could not read " << inputName << endl;
 93                return 1;
 94            }
 95        }
 96    }
 97    else
 98    {
 99        image = imread(samples::findFile("lena.jpg"), IMREAD_COLOR);
100        if (image.empty())
101        {
102            cout << "Couldn't read lena.jpg" << endl;
103            return 1;
104        }
105    }
106
107    if( capture.isOpened() )
108    {
109        cout << "Video capturing has been started ..." << endl;
110
111        for(;;)
112        {
113            capture >> frame;
114            if( frame.empty() )
115                break;
116
117            Mat frame1 = frame.clone();
118            detectAndDraw( frame1, cascade, nestedCascade, scale, tryflip );
119
120            char c = (char)waitKey(10);
121            if( c == 27 || c == 'q' || c == 'Q' )
122                break;
123        }
124    }
125    else
126    {
127        cout << "Detecting face(s) in " << inputName << endl;
128        if( !image.empty() )
129        {
130            detectAndDraw( image, cascade, nestedCascade, scale, tryflip );
131            waitKey(0);
132        }
133        else if( !inputName.empty() )
134        {
135            /* assume it is a text file containing the
136            list of the image filenames to be processed - one per line */
137            FILE* f = fopen( inputName.c_str(), "rt" );
138            if( f )
139            {
140                char buf[1000+1];
141                while( fgets( buf, 1000, f ) )
142                {
143                    int len = (int)strlen(buf);
144                    while( len > 0 && isspace(buf[len-1]) )
145                        len--;
146                    buf[len] = '\0';
147                    cout << "file " << buf << endl;
148                    image = imread( buf, IMREAD_COLOR );
149                    if( !image.empty() )
150                    {
151                        detectAndDraw( image, cascade, nestedCascade, scale, tryflip );
152                        char c = (char)waitKey(0);
153                        if( c == 27 || c == 'q' || c == 'Q' )
154                            break;
155                    }
156                    else
157                    {
158                        cerr << "Aw snap, couldn't read image " << buf << endl;
159                    }
160                }
161                fclose(f);
162            }
163        }
164    }
165
166    return 0;
167}
168
169void detectAndDraw( Mat& img, CascadeClassifier& cascade,
170                    CascadeClassifier& nestedCascade,
171                    double scale, bool tryflip )
172{
173    double t = 0;
174    vector<Rect> faces, faces2;
175    const static Scalar colors[] =
176    {
177        Scalar(255,0,0),
178        Scalar(255,128,0),
179        Scalar(255,255,0),
180        Scalar(0,255,0),
181        Scalar(0,128,255),
182        Scalar(0,255,255),
183        Scalar(0,0,255),
184        Scalar(255,0,255)
185    };
186    Mat gray, smallImg;
187
188    cvtColor( img, gray, COLOR_BGR2GRAY );
189    double fx = 1 / scale;
190    resize( gray, smallImg, Size(), fx, fx, INTER_LINEAR_EXACT );
191    equalizeHist( smallImg, smallImg );
192
193    t = (double)getTickCount();
194    cascade.detectMultiScale( smallImg, faces,
195        1.1, 2, 0
196        //|CASCADE_FIND_BIGGEST_OBJECT
197        //|CASCADE_DO_ROUGH_SEARCH
198        |CASCADE_SCALE_IMAGE,
199        Size(30, 30) );
200    if( tryflip )
201    {
202        flip(smallImg, smallImg, 1);
203        cascade.detectMultiScale( smallImg, faces2,
204                                 1.1, 2, 0
205                                 //|CASCADE_FIND_BIGGEST_OBJECT
206                                 //|CASCADE_DO_ROUGH_SEARCH
207                                 |CASCADE_SCALE_IMAGE,
208                                 Size(30, 30) );
209        for( vector<Rect>::const_iterator r = faces2.begin(); r != faces2.end(); ++r )
210        {
211            faces.push_back(Rect(smallImg.cols - r->x - r->width, r->y, r->width, r->height));
212        }
213    }
214    t = (double)getTickCount() - t;
215    printf( "detection time = %g ms\n", t*1000/getTickFrequency());
216    for ( size_t i = 0; i < faces.size(); i++ )
217    {
218        Rect r = faces[i];
219        Mat smallImgROI;
220        vector<Rect> nestedObjects;
221        Point center;
222        Scalar color = colors[i%8];
223        int radius;
224
225        double aspect_ratio = (double)r.width/r.height;
226        if( 0.75 < aspect_ratio && aspect_ratio < 1.3 )
227        {
228            center.x = cvRound((r.x + r.width*0.5)*scale);
229            center.y = cvRound((r.y + r.height*0.5)*scale);
230            radius = cvRound((r.width + r.height)*0.25*scale);
231            circle( img, center, radius, color, 3, 8, 0 );
232        }
233        else
234            rectangle( img, Point(cvRound(r.x*scale), cvRound(r.y*scale)),
235                       Point(cvRound((r.x + r.width-1)*scale), cvRound((r.y + r.height-1)*scale)),
236                       color, 3, 8, 0);
237        if( nestedCascade.empty() )
238            continue;
239        smallImgROI = smallImg( r );
240        nestedCascade.detectMultiScale( smallImgROI, nestedObjects,
241            1.1, 2, 0
242            //|CASCADE_FIND_BIGGEST_OBJECT
243            //|CASCADE_DO_ROUGH_SEARCH
244            //|CASCADE_DO_CANNY_PRUNING
245            |CASCADE_SCALE_IMAGE,
246            Size(30, 30) );
247        for ( size_t j = 0; j < nestedObjects.size(); j++ )
248        {
249            Rect nr = nestedObjects[j];
250            center.x = cvRound((r.x + nr.x + nr.width*0.5)*scale);
251            center.y = cvRound((r.y + nr.y + nr.height*0.5)*scale);
252            radius = cvRound((nr.width + nr.height)*0.25*scale);
253            circle( img, center, radius, color, 3, 8, 0 );
254        }
255    }
256    imshow( "result", img );
257}