facedetect.cpp 9.84 KB
Newer Older
1 2 3
#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
4

5
#include <cctype>
6
#include <iostream>
7
#include <iterator>
8
#include <stdio.h>
9 10 11 12

using namespace std;
using namespace cv;

13
static void help()
14
{
15
    cout << "\nThis program demonstrates the cascade recognizer. Now you can use Haar or LBP features.\n"
16 17
            "This classifier can recognize many kinds of rigid objects, once the appropriate classifier is trained.\n"
            "It's most known use is for faces.\n"
18 19 20
            "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"
21 22
               "   [--scale=<image scale greater or equal to 1, try 1.3 for example>]\n"
               "   [--try-flip]\n"
23
               "   [filename|camera_index]\n\n"
24
            "see facedetect.cmd for one call:\n"
25 26 27
            "./facedetect --cascade=\"../../data/haarcascades/haarcascade_frontalface_alt.xml\" --nested-cascade=\"../../data/haarcascades/haarcascade_eye.xml\" --scale=1.3\n\n"
            "During execution:\n\tHit any key to quit.\n"
            "\tUsing OpenCV version " << CV_VERSION << "\n" << endl;
28 29
}

30 31 32
void detectAndDraw( Mat& img, CascadeClassifier& cascade,
                    CascadeClassifier& nestedCascade,
                    double scale, bool tryflip );
33

34 35
string cascadeName = "../../data/haarcascades/haarcascade_frontalface_alt.xml";
string nestedCascadeName = "../../data/haarcascades/haarcascade_eye_tree_eyeglasses.xml";
36

37
int main( int argc, const char** argv )
38
{
39 40
    CvCapture* capture = 0;
    Mat frame, frameCopy, image;
41
    const string scaleOpt = "--scale=";
42
    size_t scaleOptLen = scaleOpt.length();
43
    const string cascadeOpt = "--cascade=";
44
    size_t cascadeOptLen = cascadeOpt.length();
45
    const string nestedCascadeOpt = "--nested-cascade";
46
    size_t nestedCascadeOptLen = nestedCascadeOpt.length();
47 48 49 50
    const string tryFlipOpt = "--try-flip";
    size_t tryFlipOptLen = tryFlipOpt.length();
    string inputName;
    bool tryflip = false;
51 52 53

    help();

54
    CascadeClassifier cascade, nestedCascade;
55 56 57 58
    double scale = 1;

    for( int i = 1; i < argc; i++ )
    {
59
        cout << "Processing " << i << " " <<  argv[i] << endl;
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
        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;
        }
78 79 80 81 82
        else if( tryFlipOpt.compare( 0, tryFlipOptLen, argv[i], tryFlipOptLen ) == 0 )
        {
            tryflip = true;
            cout << " will try to flip image horizontally to detect assymetric objects\n";
        }
83 84 85 86 87 88 89
        else if( argv[i][0] == '-' )
        {
            cerr << "WARNING: Unknown option %s" << argv[i] << endl;
        }
        else
            inputName.assign( argv[i] );
    }
90 91 92 93

    if( !cascade.load( cascadeName ) )
    {
        cerr << "ERROR: Could not load classifier cascade" << endl;
94
        help();
95 96 97 98
        return -1;
    }

    if( inputName.empty() || (isdigit(inputName.c_str()[0]) && inputName.c_str()[1] == '\0') )
99
    {
100
        capture = cvCaptureFromCAM( inputName.empty() ? 0 : inputName.c_str()[0] - '0' );
101
        int c = inputName.empty() ? 0 : inputName.c_str()[0] - '0' ;
102
        if(!capture) cout << "Capture from CAM " <<  c << " didn't work" << endl;
103
    }
104 105 106 107
    else if( inputName.size() )
    {
        image = imread( inputName, 1 );
        if( image.empty() )
108
        {
109
            capture = cvCaptureFromAVI( inputName.c_str() );
110
            if(!capture) cout << "Capture from AVI didn't work" << endl;
111
        }
112
    }
113 114 115 116 117
    else
    {
        image = imread( "lena.jpg", 1 );
        if(image.empty()) cout << "Couldn't read lena.jpg" << endl;
    }
118 119 120 121 122

    cvNamedWindow( "result", 1 );

    if( capture )
    {
123
        cout << "In capture ..." << endl;
124 125 126 127 128 129 130 131 132 133 134
        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 );

135
            detectAndDraw( frameCopy, cascade, nestedCascade, scale, tryflip );
136 137 138 139 140 141

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

        waitKey(0);
142

143 144 145 146 147
_cleanup_:
        cvReleaseCapture( &capture );
    }
    else
    {
148
        cout << "In image read" << endl;
149 150
        if( !image.empty() )
        {
151
            detectAndDraw( image, cascade, nestedCascade, scale, tryflip );
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
            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() )
                    {
172
                        detectAndDraw( image, cascade, nestedCascade, scale, tryflip );
173 174 175 176
                        c = waitKey(0);
                        if( c == 27 || c == 'q' || c == 'Q' )
                            break;
                    }
177 178
                    else
                    {
179
                        cerr << "Aw snap, couldn't read image " << buf << endl;
180
                    }
181 182 183 184 185 186 187 188 189 190 191
                }
                fclose(f);
            }
        }
    }

    cvDestroyWindow("result");

    return 0;
}

192 193 194
void detectAndDraw( Mat& img, CascadeClassifier& cascade,
                    CascadeClassifier& nestedCascade,
                    double scale, bool tryflip )
195 196 197
{
    int i = 0;
    double t = 0;
198
    vector<Rect> faces, faces2;
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
    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) );
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
    if( tryflip )
    {
        flip(smallImg, smallImg, 1);
        cascade.detectMultiScale( smallImg, faces2,
                                 1.1, 2, 0
                                 //|CV_HAAR_FIND_BIGGEST_OBJECT
                                 //|CV_HAAR_DO_ROUGH_SEARCH
                                 |CV_HAAR_SCALE_IMAGE
                                 ,
                                 Size(30, 30) );
        for( vector<Rect>::const_iterator r = faces2.begin(); r != faces2.end(); r++ )
        {
            faces.push_back(Rect(smallImg.cols - r->x - r->width, r->y, r->width, r->height));
        }
    }
236 237 238 239 240 241 242 243 244
    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;
245

246 247 248 249 250 251 252 253 254 255 256 257
        double aspect_ratio = (double)r->width/r->height;
        if( 0.75 < aspect_ratio && aspect_ratio < 1.3 )
        {
            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 );
        }
        else
            rectangle( img, cvPoint(cvRound(r->x*scale), cvRound(r->y*scale)),
                       cvPoint(cvRound((r->x + r->width-1)*scale), cvRound((r->y + r->height-1)*scale)),
                       color, 3, 8, 0);
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
        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 );
        }
276 277
    }
    cv::imshow( "result", img );
278
}