facedetect.cpp 10 KB
Newer Older
1
#include "opencv2/objdetect.hpp"
2
#include "opencv2/imgcodecs.hpp"
3
#include "opencv2/videoio.hpp"
4 5
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
6
#include "opencv2/core/utility.hpp"
7

8
#include "opencv2/videoio/videoio_c.h"
9 10
#include "opencv2/highgui/highgui_c.h"

11
#include <cctype>
12
#include <iostream>
13
#include <iterator>
14
#include <stdio.h>
15 16 17 18

using namespace std;
using namespace cv;

19
static void help()
20
{
21
    cout << "\nThis program demonstrates the cascade recognizer. Now you can use Haar or LBP features.\n"
22 23
            "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"
24 25 26
            "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"
27 28
               "   [--scale=<image scale greater or equal to 1, try 1.3 for example>]\n"
               "   [--try-flip]\n"
29
               "   [filename|camera_index]\n\n"
30
            "see facedetect.cmd for one call:\n"
31 32 33
            "./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;
34 35
}

36 37 38
void detectAndDraw( Mat& img, CascadeClassifier& cascade,
                    CascadeClassifier& nestedCascade,
                    double scale, bool tryflip );
39

40 41
string cascadeName = "../../data/haarcascades/haarcascade_frontalface_alt.xml";
string nestedCascadeName = "../../data/haarcascades/haarcascade_eye_tree_eyeglasses.xml";
42

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

    help();

60
    CascadeClassifier cascade, nestedCascade;
61 62 63 64
    double scale = 1;

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

    if( !cascade.load( cascadeName ) )
    {
        cerr << "ERROR: Could not load classifier cascade" << endl;
100
        help();
101 102 103 104
        return -1;
    }

    if( inputName.empty() || (isdigit(inputName.c_str()[0]) && inputName.c_str()[1] == '\0') )
105
    {
106
        capture = cvCaptureFromCAM( inputName.empty() ? 0 : inputName.c_str()[0] - '0' );
107
        int c = inputName.empty() ? 0 : inputName.c_str()[0] - '0' ;
108
        if(!capture) cout << "Capture from CAM " <<  c << " didn't work" << endl;
109
    }
110 111 112 113
    else if( inputName.size() )
    {
        image = imread( inputName, 1 );
        if( image.empty() )
114
        {
115
            capture = cvCaptureFromAVI( inputName.c_str() );
116
            if(!capture) cout << "Capture from AVI didn't work" << endl;
117
        }
118
    }
119 120
    else
    {
Dmitriy Anisimov's avatar
Dmitriy Anisimov committed
121 122
        image = imread( "../data/lena.jpg", 1 );
        if(image.empty()) cout << "Couldn't read ../data/lena.jpg" << endl;
123
    }
124 125 126 127 128

    cvNamedWindow( "result", 1 );

    if( capture )
    {
129
        cout << "In capture ..." << endl;
130 131 132
        for(;;)
        {
            IplImage* iplImg = cvQueryFrame( capture );
133
            frame = cv::cvarrToMat(iplImg);
134 135 136 137 138 139 140
            if( frame.empty() )
                break;
            if( iplImg->origin == IPL_ORIGIN_TL )
                frame.copyTo( frameCopy );
            else
                flip( frame, frameCopy, 0 );

141
            detectAndDraw( frameCopy, cascade, nestedCascade, scale, tryflip );
142 143 144 145 146 147

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

        waitKey(0);
148

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

    cvDestroyWindow("result");

    return 0;
}

198 199 200
void detectAndDraw( Mat& img, CascadeClassifier& cascade,
                    CascadeClassifier& nestedCascade,
                    double scale, bool tryflip )
201 202 203
{
    int i = 0;
    double t = 0;
204
    vector<Rect> faces, faces2;
205 206 207 208 209 210 211 212 213 214
    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 );

215
    cvtColor( img, gray, COLOR_BGR2GRAY );
216 217 218 219 220 221
    resize( gray, smallImg, smallImg.size(), 0, 0, INTER_LINEAR );
    equalizeHist( smallImg, smallImg );

    t = (double)cvGetTickCount();
    cascade.detectMultiScale( smallImg, faces,
        1.1, 2, 0
222 223 224
        //|CASCADE_FIND_BIGGEST_OBJECT
        //|CASCADE_DO_ROUGH_SEARCH
        |CASCADE_SCALE_IMAGE
225 226
        ,
        Size(30, 30) );
227 228 229 230 231
    if( tryflip )
    {
        flip(smallImg, smallImg, 1);
        cascade.detectMultiScale( smallImg, faces2,
                                 1.1, 2, 0
232 233 234
                                 //|CASCADE_FIND_BIGGEST_OBJECT
                                 //|CASCADE_DO_ROUGH_SEARCH
                                 |CASCADE_SCALE_IMAGE
235 236 237 238 239 240 241
                                 ,
                                 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));
        }
    }
242 243 244 245 246 247 248 249 250
    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;
251

252 253 254 255 256 257 258 259 260 261 262 263
        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);
264 265 266 267 268
        if( nestedCascade.empty() )
            continue;
        smallImgROI = smallImg(*r);
        nestedCascade.detectMultiScale( smallImgROI, nestedObjects,
            1.1, 2, 0
269 270 271 272
            //|CASCADE_FIND_BIGGEST_OBJECT
            //|CASCADE_DO_ROUGH_SEARCH
            //|CASCADE_DO_CANNY_PRUNING
            |CASCADE_SCALE_IMAGE
273 274 275 276 277 278 279 280 281
            ,
            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 );
        }
282 283
    }
    cv::imshow( "result", img );
284
}