int 
main(int argc, char* argv[])
{
    LibraryInitializer li;

    // parse arguments

    CommandLineOptions opt;
    opt.addOption('v', "videosource",            true,  "Use video source");
    opt.addOption('i', "images",                 false, "Use images");
    opt.addOption('d', "face-detector",          true,  "face detector file");
    opt.addOption('e', "eye-detector",           true,  "eye detector file");
    opt.addOption('m', "mouth-detector",         true,  "mouth detector file");
    opt.addOption('l', "low-resolution",         false, "Use resolution of 320x240 pixels");
    opt.addOption('s', "search-regions",         false, "Show eye and mouth search regions");
    opt.addOption(     "fps",                    false, "Display frames per second");
    opt.addOption('r', "draw-search-rectangles", false, "Draw the search rectangles (i.e. head, eye, etc.)");

    if (argc < 3)
    {
        printf(help_str, opt.helpString().c_str());
        exit(1);
    }

    string fd_fn, ed_fn, md_fn, videosource;
    bool low_resolution, display_fps, use_videosource, use_images, search_regions, display_search_rectangles;
    vector<string> images;
    try
    {
        images                    = opt.parse(argc, argv);
        fd_fn                     = opt.getArgument<string>("face-detector");
        ed_fn                     = opt.getArgument<string>("eye-detector", "");
        md_fn                     = opt.getArgument<string>("mouth-detector", "");
        videosource               = opt.getArgument<string>("videosource", "");
        low_resolution            = opt.parameterSet("low-resolution") > 0;
        display_fps               = opt.parameterSet("fps") > 0;
        use_videosource           = opt.parameterSet("videosource") > 0;
        use_images                = opt.parameterSet("images") > 0;
        search_regions            = opt.parameterSet("search-regions") > 0;
        display_search_rectangles = opt.parameterSet("draw-search-rectangles") > 0;
    }
    catch (CommandLineOptionException e)
    {
        printf(help_str, opt.helpString().c_str());
        exit(1);
    }

    if (!use_videosource && !use_images)
    {
        printf("Either '-v' or '-i' must be given\n");
        printf(help_str, opt.helpString().c_str());
        exit(1);
    }

    // Set up detectors
    BinaryPatternFaceDetector fd(fd_fn);
    fd.setScaleStep(1.2);
    fd.setNeighborDist(1.3);
    auto_ptr<BinaryPatternEyeDetector> ed;
    if (!ed_fn.empty())
        ed.reset(new BinaryPatternEyeDetector(ed_fn));
    auto_ptr<BinaryPatternMouthDetector> md;
    if (!md_fn.empty())
        md.reset(new BinaryPatternMouthDetector(md_fn));

    // Eye center detector
    typedef IsophoteEyeCenterDetector<double> iecd_t; // can be instantiated as float and double (choice defines the algorithmic precision [vs. run-time])
    iecd_t iecd;

    /* default parameters */
    // ...
    double isophote_row_sigma = 1;
    double isophote_col_sigma = 1;
    // options
    bool set_auto_isophote_sigma = false;

    // Set up GUI
    GuiThread thread;
    ImageWindow* imgwin = new ImageWindow("Detector Demo (cam)");
    thread.addClient(imgwin);
    // parameter control 
    okapi::WidgetWindow* win_params = new okapi::WidgetWindow("Parameter Window", 350/*widget width*/, 250/*label width*/);
    thread.addClient(win_params);
    // start the GUI thread
    thread.start();
    // set initial values
    win_params->setSlider("isophote row sigma (sigma)",          isophote_row_sigma, 0, 5);
    win_params->setSlider("isophote col sigma (sigma)",          isophote_col_sigma, 0, 5);     
    win_params->setButton("set auto sigma",                      set_auto_isophote_sigma);     
    win_params->setButton("display search rectangles",           display_search_rectangles);

    double old_ts = -1;
    double fps = -1;

    // Set up VideoSource
    auto_ptr<VideoSource> src;
    if (use_videosource)
    {
        if (videosource.empty() || videosource == "")
        {
            if (low_resolution)
                src.reset(createDefaultCamera(320, 240));
            else
                //src.reset(createDefaultCamera(640, 480));
                src.reset(createDefaultCamera(1024,768));
                //src.reset(createDefaultCamera(1600,1200));
        }
        else
        {
            if (videosource == "")
                src.reset(createVideoSourceFromString(videosource));
            else
                src.reset(createVideoSource("/home/bschauer/devel/local/share/okapi/videoio/videosources.xml","grasshopper"));
        }

        if (!src.get())
        {
            printf("Error opening camera device, make sure cam is connected!\n");
            exit(1);
        }
    }

    while (imgwin->getWindowState() && (use_images || src->getNextFrame()))
    {
        if (use_images && images.empty())
        {
            sleep_ms(20);
            continue;
        }

        string fn;
        cv::Mat img;
        if (use_videosource)
           img = src->getImage();
        else
        {
            fn = images.front();
            images.erase(images.begin());
            img = loadImage(fn);
        }
        cv::Mat aimg = img.clone();
        cv::Mat gimg;
        cv::cvtColor(aimg,gimg,CV_RGB2GRAY);
        ImageDeco deco(aimg);
        deco.setAntiAlias(true);

        /* get current parameter values */
        // ...
        set_auto_isophote_sigma = win_params->getButton("set auto sigma");
        if (!set_auto_isophote_sigma)
        {
            win_params->toggleActivated("isophote row sigma (sigma)", true);
            win_params->toggleActivated("isophote col sigma (sigma)", true);
            isophote_row_sigma = win_params->getSlider("isophote row sigma (sigma)");
            isophote_col_sigma = win_params->getSlider("isophote col sigma (sigma)");
        }
        else
        {
            win_params->toggleActivated("isophote row sigma (sigma)", false);
            win_params->toggleActivated("isophote col sigma (sigma)", false);
        }
        display_search_rectangles = win_params->getButton("display search rectangles");

        // Detect faces
        OKAPI_TIMER_START("detect faces");
        vector<RectDetection> faces = fd.detectFaces(img);
        OKAPI_TIMER_STOP("detect faces");

        double fsize=0; //faces[0].box.width * faces[0].box.height;
        size_t i=0;
        for (size_t j=0; j<faces.size(); j++)
        {
            if ((faces[j].box.width * faces[j].box.height) > fsize)
                i = j;
        }

        //for (size_t i=0; i<faces.size(); ++i)
        if (faces.size() > 0)
        {
            EyeLocations eyes(cv::Point(-1, -1), cv::Point(-1, -1));

            // Detect Mouth
            if (md.get())
            {
                cv::Point2f mouth = fd.getMeanMouthCenter(faces[i].box);
                double mouth_width = fd.getMeanMouthWidth(faces[i].box);

                OKAPI_TIMER_START("detect mouth");
                mouth = md->detectMouth(img, mouth, mouth_width);
                OKAPI_TIMER_STOP("detect mouth");

                if (mouth.x >= 0)
                {
                    deco.setColor(255, 0, 255);
                    deco.setThickness(1);
                    deco.drawCircle(cvRound(mouth.x), cvRound(mouth.y), 3);
                }
            }

            // Detect eyes
            if (ed.get())
            {
                cv::Point2f le, re;
                le = fd.getMeanLeftEye(faces[i].box);
                re = fd.getMeanRightEye(faces[i].box);

                OKAPI_TIMER_START("detect eyes");
                eyes = ed->detectEyes(img, le, re);
                OKAPI_TIMER_STOP("detect eyes");

                /*if (search_regions)
                {
                    cv::Rect left_eye_roi, right_eye_roi;
                    int min_result_width, max_result_width;
                    ed->getSearchRegions(img, le, re, left_eye_roi, right_eye_roi,
                                         min_result_width, max_result_width);
                    deco.setThickness(1);
                    deco.setColor(255, 127, 127);
                    deco.drawRect(left_eye_roi);
                    deco.drawRect(right_eye_roi);
                }*/

                // Draw eye locations
                deco.setThickness(1);
                deco.setColor(0, 127, 0);
                if (eyes.left.x > 0)
                    deco.drawCircle(eyes.left.x, eyes.left.y, 3);
                if (eyes.right.x > 0)
                    deco.drawCircle(eyes.right.x, eyes.right.y, 3);
            }

            // Draw rotated face bounding box
            if (eyes.isValid())
            {
                // Draw detected face (lightly)
                deco.setColor(127, 127, 127);
                deco.setThickness(1);
                deco.drawRect(faces[i].box);

                // Draw rotated face box
                cv::Point2f eye_center(eyes.right + eyes.left);
                eye_center *= 0.5f;
                double eye_dist = cv::norm(eyes.right - eyes.left);
                double angle = atan2(eyes.left.y - eyes.right.y, eyes.right.x - eyes.left.x);
                cv::Point2f offset(sin(angle), cos(angle));
                offset *= 0.25f * (float)eye_dist;
                cv::RotatedRect rrect(eye_center + offset, cv::Size2f(eye_dist * 2.0, eye_dist * 1.75), - angle / CV_PI * 180);
                deco.setColor(0, 255, 0);
                deco.setThickness(2);
                deco.drawRect(rrect);

                // Run the eye detector
                if (set_auto_isophote_sigma)
                    iecd.setAutoSigma();
                else
                    iecd.setSigma(isophote_row_sigma,isophote_col_sigma);
                OKAPI_TIMER_START("iecd.detectEyeCenters");
                EyeCenterLocations<iecd_t::coord_t> eye_centers = iecd.detectEyeCenters(gimg,faces[i].box,eyes.left,eyes.right);
                OKAPI_TIMER_STOP("iecd.detectEyeCenters");
                if (set_auto_isophote_sigma)
                {
                    win_params->setSlider("isophote row sigma (sigma)",          iecd.getRowSigma(), 0, 5);
                    win_params->setSlider("isophote col sigma (sigma)",          iecd.getColSigma(), 0, 5);    
                }
                deco.setThickness(2);
                deco.setColor(255,0,0);
                deco.drawCircle(eye_centers.left.x,eye_centers.left.y,3);
                deco.drawCircle(eye_centers.right.x,eye_centers.right.y,3);
                deco.setThickness(1);
                deco.drawLine(eye_centers.left.x,eye_centers.left.y,eye_centers.right.x,eye_centers.right.y);

                // Draw search regions, i.e. regions of interest
                cv::Rect_<iecd_t::coord_t> left_roi, right_roi;
                iecd.getCurrentSearchRegions(left_roi, right_roi);
                if (display_search_rectangles)
                {
                    if (iecd_t::isValidCoord(left_roi))
                    {
                        deco.setThickness(1);
                        deco.setColor(255, 127, 127);
                        deco.drawRect(left_roi);
                    }
                    if (iecd_t::isValidCoord(right_roi))
                    {
                        deco.setThickness(1);
                        deco.setColor(255, 127, 127);
                        deco.drawRect(right_roi); 
                    }
                }
                //cv::Mat left_eye_image(left_roi.height,left_roi.width,img.type());
                //cv::Mat right_eye_image(left_roi.height,left_roi.width,img.type());
                cv::Mat left_eye_image(img,left_roi);
                cv::Mat right_eye_image(img,right_roi);
                cv::Mat left_eye_image_resized;
                cv::Mat right_eye_image_resized;
                cv::resize(left_eye_image,left_eye_image_resized,cv::Size(), 3, 3);
                cv::resize(right_eye_image,right_eye_image_resized,cv::Size(), 3, 3);
                ImageDeco dleir(left_eye_image_resized);
                ImageDeco dreir(right_eye_image_resized);
                dleir.setThickness(2);
                dleir.setColor(255,0,0);
                dleir.drawCircle(3*(eye_centers.left.x - left_roi.x),3*(eye_centers.left.y - left_roi.y),3);
                dreir.setThickness(2);
                dreir.setColor(255,0,0);
                dreir.drawCircle(3*(eye_centers.right.x - right_roi.x),3*(eye_centers.right.y - right_roi.y),3); 
                imgwin->setImage("left_eye_image", left_eye_image_resized);
                imgwin->setImage("right_eye_image", right_eye_image_resized);
            }
            else
            {
                // Draw detected face (strongly)
                deco.setColor(127, 0, 0);
                deco.setThickness(1);
                deco.drawRect(faces[i].box);

                // Run the eye detector
                if (set_auto_isophote_sigma)
                    iecd.setAutoSigma();
                else
                    iecd.setSigma(isophote_row_sigma,isophote_col_sigma);
                OKAPI_TIMER_START("iecd.detectEyeCenters");
                EyeCenterLocations<iecd_t::coord_t> eye_centers = iecd.detectEyeCenters(gimg,faces[i].box,iecd_t::getInvalidCoordPoint(),iecd_t::getInvalidCoordPoint());
                OKAPI_TIMER_STOP("iecd.detectEyeCenters");
                if (set_auto_isophote_sigma)
                {
                    win_params->setSlider("isophote row sigma (sigma)",          iecd.getRowSigma(), 0, 5);
                    win_params->setSlider("isophote col sigma (sigma)",          iecd.getColSigma(), 0, 5);    
                }
                deco.setThickness(1);
                deco.setColor(127,0,0);
                deco.drawCircle(eye_centers.left.x,eye_centers.left.y,3);
                deco.drawCircle(eye_centers.right.x,eye_centers.right.y,3);
                deco.drawLine(eye_centers.left.x,eye_centers.left.y,eye_centers.right.x,eye_centers.right.y);
            }
        }

        if (display_fps)
        {
            double new_ts = src->getTimestamp();

            if (old_ts < 0)
                old_ts = new_ts - 0.1;

            // Compute fps
            double new_fps = 1 / (new_ts - old_ts);
            if (fps < 0)
                fps = new_fps;
            else
                fps = 0.95 * fps + 0.05 * new_fps;

            char buf[1000];
            sprintf(buf, "%4.1f fps\n", fps);
            old_ts = new_ts;

            // Write frame rate
            deco.setColor(255, 255, 255);
            deco.setThickness(1);
            deco.drawText(buf, 0, 15);
        }

        // Display annotated image
        string name;
        if (use_videosource)
            name = "Video";
        else
            name = basename(fn);
        imgwin->setImage(name, aimg, low_resolution ? 2 : 1);
        imgwin->setImage("accumulator",iecd.getMatAcc(), low_resolution ? 2 : 1);
    }

    return 0;
}