smiledetect.cpp 7.02 KB
Newer Older
1 2 3
#include "opencv2/objdetect.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
4 5 6 7 8 9 10 11 12 13
#include <iostream>

using namespace std;
using namespace cv;

static void help()
{
    cout << "\nThis program demonstrates the smile detector.\n"
            "Usage:\n"
            "./smiledetect [--cascade=<cascade_path> this is the frontal face classifier]\n"
14 15
            "   [--smile-cascade=[<smile_cascade_path>]]\n"
            "   [--scale=<image scale greater or equal to 1, try 2.0 for example. The larger the faster the processing>]\n"
16
            "   [--try-flip]\n"
17
            "   [video_filename|camera_index]\n\n"
18
            "Example:\n"
19
            "./smiledetect --cascade=\"../../data/haarcascades/haarcascade_frontalface_alt.xml\" --smile-cascade=\"../../data/haarcascades/haarcascade_smile.xml\" --scale=2.0\n\n"
20 21 22 23 24 25 26 27
            "During execution:\n\tHit any key to quit.\n"
            "\tUsing OpenCV version " << CV_VERSION << "\n" << endl;
}

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

ValeryTyumen's avatar
ValeryTyumen committed
28 29
string cascadeName;
string nestedCascadeName;
30 31 32

int main( int argc, const char** argv )
{
33 34
    VideoCapture capture;
    Mat frame, image;
35
    string inputName;
ValeryTyumen's avatar
ValeryTyumen committed
36
    bool tryflip;
37 38 39 40

    help();

    CascadeClassifier cascade, nestedCascade;
ValeryTyumen's avatar
ValeryTyumen committed
41 42 43 44 45 46 47
    double scale;
    cv::CommandLineParser parser(argc, argv,
        "{help h||}{scale|1|}"
        "{cascade|../../data/haarcascades/haarcascade_frontalface_alt.xml|}"
        "{smile-cascade|../../data/haarcascades/haarcascade_smile.xml|}"
        "{try-flip||}{@input||}");
    if (parser.has("help"))
48
    {
ValeryTyumen's avatar
ValeryTyumen committed
49 50
        help();
        return 0;
51
    }
ValeryTyumen's avatar
ValeryTyumen committed
52 53 54 55 56 57 58 59 60 61 62 63
    cascadeName = parser.get<string>("cascade");
    nestedCascadeName = parser.get<string>("smile-cascade");
    tryflip = parser.has("try-flip");
    inputName = parser.get<string>("@input");
    scale = parser.get<int>("scale");
    if (!parser.check())
    {
        help();
        return 1;
    }
    if (scale < 1)
        scale = 1;
64 65
    if( !cascade.load( cascadeName ) )
    {
66 67 68 69 70 71 72
        cerr << "ERROR: Could not load face cascade" << endl;
        help();
        return -1;
    }
    if( !nestedCascade.load( nestedCascadeName ) )
    {
        cerr << "ERROR: Could not load smile cascade" << endl;
73 74 75
        help();
        return -1;
    }
ValeryTyumen's avatar
ValeryTyumen committed
76
    if( inputName.empty() || (isdigit(inputName[0]) && inputName.size() == 1) )
77
    {
ValeryTyumen's avatar
ValeryTyumen committed
78
        int c = inputName.empty() ? 0 : inputName[0] - '0' ;
79 80
        if(!capture.open(c))
            cout << "Capture from camera #" <<  c << " didn't work" << endl;
81 82 83
    }
    else if( inputName.size() )
    {
84 85
        if(!capture.open( inputName ))
            cout << "Could not read " << inputName << endl;
86 87
    }

88
    if( capture.isOpened() )
89
    {
90
        cout << "Video capturing has been started ..." << endl;
91
        cout << endl << "NOTE: Smile intensity will only be valid after a first smile has been detected" << endl;
92

93 94
        for(;;)
        {
95
            capture >> frame;
96 97 98
            if( frame.empty() )
                break;

99 100
            Mat frame1 = frame.clone();
            detectAndDraw( frame1, cascade, nestedCascade, scale, tryflip );
101

102 103 104
            int c = waitKey(10);
            if( c == 27 || c == 'q' || c == 'Q' )
                break;
105 106 107 108
        }
    }
    else
    {
109 110 111
        cerr << "ERROR: Could not initiate capture" << endl;
        help();
        return -1;
112 113 114 115 116 117 118 119 120 121
    }

    return 0;
}

void detectAndDraw( Mat& img, CascadeClassifier& cascade,
                    CascadeClassifier& nestedCascade,
                    double scale, bool tryflip)
{
    vector<Rect> faces, faces2;
122 123 124 125 126 127 128 129 130 131 132 133
    const static Scalar colors[] =
    {
        Scalar(255,0,0),
        Scalar(255,128,0),
        Scalar(255,255,0),
        Scalar(0,255,0),
        Scalar(0,128,255),
        Scalar(0,255,255),
        Scalar(0,0,255),
        Scalar(255,0,255)
    };
    Mat gray, smallImg;
134

135
    cvtColor( img, gray, COLOR_BGR2GRAY );
136 137 138

    double fx = 1 / scale;
    resize( gray, smallImg, Size(), fx, fx, INTER_LINEAR );
139 140 141 142
    equalizeHist( smallImg, smallImg );

    cascade.detectMultiScale( smallImg, faces,
        1.1, 2, 0
143 144
        //|CASCADE_FIND_BIGGEST_OBJECT
        //|CASCADE_DO_ROUGH_SEARCH
145
        |CASCADE_SCALE_IMAGE,
146 147 148 149 150 151
        Size(30, 30) );
    if( tryflip )
    {
        flip(smallImg, smallImg, 1);
        cascade.detectMultiScale( smallImg, faces2,
                                 1.1, 2, 0
152 153
                                 //|CASCADE_FIND_BIGGEST_OBJECT
                                 //|CASCADE_DO_ROUGH_SEARCH
154
                                 |CASCADE_SCALE_IMAGE,
155
                                 Size(30, 30) );
156
        for( vector<Rect>::const_iterator r = faces2.begin(); r != faces2.end(); ++r )
157 158 159 160
        {
            faces.push_back(Rect(smallImg.cols - r->x - r->width, r->y, r->width, r->height));
        }
    }
161

162
    for ( size_t i = 0; i < faces.size(); i++ )
163
    {
164
        Rect r = faces[i];
165 166 167 168 169 170
        Mat smallImgROI;
        vector<Rect> nestedObjects;
        Point center;
        Scalar color = colors[i%8];
        int radius;

171
        double aspect_ratio = (double)r.width/r.height;
172 173
        if( 0.75 < aspect_ratio && aspect_ratio < 1.3 )
        {
174 175 176
            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);
177 178 179
            circle( img, center, radius, color, 3, 8, 0 );
        }
        else
180 181
            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)),
182 183
                       color, 3, 8, 0);

184 185 186 187
        const int half_height=cvRound((float)r.height/2);
        r.y=r.y + half_height;
        r.height = half_height-1;
        smallImgROI = smallImg( r );
188 189
        nestedCascade.detectMultiScale( smallImgROI, nestedObjects,
            1.1, 0, 0
190 191 192
            //|CASCADE_FIND_BIGGEST_OBJECT
            //|CASCADE_DO_ROUGH_SEARCH
            //|CASCADE_DO_CANNY_PRUNING
193
            |CASCADE_SCALE_IMAGE,
194 195
            Size(30, 30) );

196 197
        // The number of detected neighbors depends on image size (and also illumination, etc.). The
        // following steps use a floating minimum and maximum of neighbors. Intensity thus estimated will be
198 199
        //accurate only after a first smile has been displayed by the user.
        const int smile_neighbors = (int)nestedObjects.size();
200 201 202 203 204 205 206 207
        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);

        // Draw rectangle on the left side of the image reflecting smile intensity
        float intensityZeroOne = ((float)smile_neighbors - min_neighbors) / (max_neighbors - min_neighbors + 1);
        int rect_height = cvRound((float)img.rows * intensityZeroOne);
208
        Scalar col = Scalar((float)255 * intensityZeroOne, 0, 0);
209
        rectangle(img, cvPoint(0, img.rows), cvPoint(img.cols/10, img.rows - rect_height), col, -1);
210
    }
211

212
    imshow( "result", img );
213
}