Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

Tutorials for Beginner Cascade Classifier Training in C++?

I am a fifth-year University art student and hobbyist programmer. I am fairly familiar with C++ and have managed to familiarize myself with OpenCV thanks to the abundance of fine resources. I am working on an ongoing project that will culminate in a museum installation.

So far I have created a program that tracks movement in live webcam video stream. The program can track many different objects by using absdiff comparisons on image frames, creating a threshold image, and analyzing the contours. I am using variables for threshold sensitivity and blur amount to somewhat control contour sizes. I have provided my source code at the bottom of this post.

The problem I have, is that a single person or object may be treated/tracked as many different objects based on lighting and other factors. I am interested in Cascade Classifier Training in order to identify the human figure. The background of my live stream is simple, monochromatic, static, and well-lit - so I don't imagine it will be hard to create negative training examples. The position of the camera will be unchanging, so I should be able to use it to take many still images of human figures throughout the space, for use as positive training examples.

I hope my understanding of Cascade Classifier Training is somewhat accurate. I am seeking simple beginner tutorials to instruct me step-by-step in training a classifier, with example code. Any help or resources that you can provide are of great help and interest, and I thank you in advance.

I am also interested in attempting to map the detected movement to a drawn space: although I am tracking the X & Y coordinates of the center of the contour objects, I have no way to, say, map the human figure's movement to a very basic rendered figure. This thread is likely not the place for this question, but perhaps someone looking at my cascade question can point me in the right direction.

Thing.h

#pragma once
#include <string>

using namespace std;

class Thing{

public:
    Thing();
    ~Thing();

    int getXPos();
    void setXPos(int x);

    int getYPos();
    void setYPos(int y);

private:
    int xPos, yPos;
    string type;
};

Thing.cpp

#include "Thing.h"

Thing::Thing(){}

Thing::~Thing(){}

int Thing::getXPos(){
    return Thing::xPos;
}

void Thing::setXPos(int x){
    Thing::xPos = x;
}

int Thing::getYPos(){
    return Thing::yPos;
}

void Thing::setYPos(int y){
    Thing::yPos = y;
}

main.cpp

#define _CRT_SECURE_NO_DEPRECATE

#include <sstream>
#include <string>
#include <iostream>
#include <vector>
#include <opencv\cv.h>
#include <opencv\highgui.h>
#include "Thing.h"

using namespace std;
using namespace cv;

const static int SENSITIVITY_VALUE = 10;
const static int BLUR_SIZE = 70;

const int FRAME_WIDTH = 640;
const int FRAME_HEIGHT = 480;

const int MIN_OBJECT_AREA = 20 * 20;


string intToString(int number){
    std::stringstream ss;
    ss << number;
    return ss.str();
}


void drawObject(vector<Thing> theThings, Mat &frame){

    for (int i = 0; i<theThings.size(); i++){
        cv::circle(frame, cv::Point(theThings.at(i).getXPos(), theThings.at(i).getYPos()), 10, cv::Scalar(0, 0, 255));
        cv::putText(frame, intToString(theThings.at(i).getXPos()) + " , " + intToString(theThings.at(i).getYPos()), cv::Point(theThings.at(i).getXPos(), theThings.at(i).getYPos() + 20), 1, 1, Scalar(0, 255, 0));
    }
}


void searchForMovement(Mat thresholdImage, Mat &cameraFeed){

    vector <Thing> ones;

    Mat temp;
    thresholdImage.copyTo(temp);

    vector< vector<Point> > contours;
    vector<Vec4i> hierarchy;

    findContours(temp, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    double refArea = 0;
    bool objectDetected = false;
    if (hierarchy.size() > 0) {
        for (int index = 0; index >= 0; index = hierarchy[index][0]) {

            Moments moment = moments((cv::Mat)contours[index]);
            double area = moment.m00;

            if (area > MIN_OBJECT_AREA){

                Thing one;

                one.setXPos(moment.m10 / area);
                one.setYPos(moment.m01 / area);

                ones.push_back(one);

                objectDetected = true;}

            else objectDetected = false;

        }

        if (objectDetected == true){drawObject(ones, cameraFeed);}
        else putText(cameraFeed, "NO OBJECT DETECTED\n", Point(0, 50), 1, 2, Scalar(0, 0, 255), 2);

    }
}


int main(){

    bool objectDetected = false;
    bool debugMode = false;
    bool trackingEnabled = false;
    bool pause = false;

    Mat frame1, frame2;
    Mat grayImage1, grayImage2;
    Mat differenceImage;
    Mat thresholdImage;


    VideoCapture capture;


    capture.open(0);
    capture.set(CV_CAP_PROP_FRAME_WIDTH, FRAME_WIDTH);
    capture.set(CV_CAP_PROP_FRAME_HEIGHT, FRAME_HEIGHT);

    if (!capture.isOpened()){
        cout << "ERROR ACQUIRING VIDEO FEED\n";
        getchar();
        return -1;}

    while (1){

        capture.read(frame1);
        cv::cvtColor(frame1, grayImage1, COLOR_BGR2GRAY);

        capture.read(frame2);
        cv::cvtColor(frame2, grayImage2, COLOR_BGR2GRAY);

        cv::absdiff(grayImage1, grayImage2, differenceImage);
        cv::threshold(differenceImage, thresholdImage, SENSITIVITY_VALUE, 255, THRESH_BINARY);

        if (debugMode == true){
            cv::imshow("Difference Image", differenceImage);
            cv::imshow("Threshold Image", thresholdImage);}
        else{
            cv::destroyWindow("Difference Image");
            cv::destroyWindow("Threshold Image");}

        cv::blur(thresholdImage, thresholdImage, cv::Size(BLUR_SIZE, BLUR_SIZE));
        cv::threshold(thresholdImage, thresholdImage, SENSITIVITY_VALUE, 255, THRESH_BINARY);

        if (debugMode == true){imshow("Final Threshold Image", thresholdImage);}
        else {cv::destroyWindow("Final Threshold Image");}

        if (trackingEnabled){searchForMovement(thresholdImage, frame1);}

        imshow("Frame1", frame1);

        switch (waitKey(10)){
        case 27: //'esc' key has been pressed, exit program.
            return 0;
        case 116: //'t' has been pressed. this will toggle tracking
            trackingEnabled = !trackingEnabled;
            if (trackingEnabled == false) cout << "Tracking disabled." << endl;
            else cout << "Tracking enabled." << endl;
            break;
        case 100: //'d' has been pressed. this will debug mode
            debugMode = !debugMode;
            if (debugMode == false) cout << "Debug mode disabled." << endl;
            else cout << "Debug mode enabled." << endl;
            break;
        case 112:
            pause = !pause;
            if (pause == true){
                cout << "Code paused, press 'p' again to resume" << endl;
                while (pause == true){
                    switch (waitKey()){

                    case 112:
                        //change pause back to false
                        pause = false;
                        cout << "Code resumed." << endl;
                        break;
                    }
                }
            }
        }
    }
    return 0;
}

Tutorials for Beginner Cascade Classifier Training in C++?

I am a fifth-year University art student and hobbyist programmer. I am fairly familiar with C++ and have managed to familiarize myself with OpenCV thanks to the abundance of fine resources. I am working on an ongoing project that will culminate in a museum installation.

So far I have created a program that tracks movement in live webcam video stream. The program can track many different objects by using absdiff comparisons on image frames, creating a threshold image, and analyzing the contours. I am using variables for threshold sensitivity and blur amount to somewhat control contour sizes. I have provided my source code at the bottom of this post.

The problem I have, is that a single person or object may be treated/tracked as many different objects based on lighting and other factors. I am interested in Cascade Classifier Training in order to identify the human figure. The background of my live stream is simple, monochromatic, static, and well-lit - so I don't imagine it will be hard to create negative training examples. The position of the camera will be unchanging, so I should be able to use it to take many still images of human figures throughout the space, for use as positive training examples.

I hope my understanding of Cascade Classifier Training is somewhat accurate. I am seeking simple beginner tutorials to instruct me step-by-step in training a classifier, with example code. Any help or resources that you can provide are of great help and interest, and I thank you in advance.

I am also interested in attempting to map the detected movement to a drawn space: although I am tracking the X & Y coordinates of the center of the contour objects, I have no way to, say, map the human figure's movement to a very basic rendered figure. This thread is likely not the place for this question, but perhaps someone looking at my cascade question can point me in the right direction.

Thing.h

#pragma once
#include <string>

using namespace std;

class Thing{

public:
    Thing();
    ~Thing();

    int getXPos();
    void setXPos(int x);

    int getYPos();
    void setYPos(int y);

private:
    int xPos, yPos;
    string type;
};

Thing.cpp

#include "Thing.h"

Thing::Thing(){}

Thing::~Thing(){}

int Thing::getXPos(){
    return Thing::xPos;
}

void Thing::setXPos(int x){
    Thing::xPos = x;
}

int Thing::getYPos(){
    return Thing::yPos;
}

void Thing::setYPos(int y){
    Thing::yPos = y;
}

main.cpp

#define _CRT_SECURE_NO_DEPRECATE

#include <sstream>
#include <string>
#include <iostream>
#include <vector>
#include <opencv\cv.h>
#include <opencv\highgui.h>
#include "Thing.h"

using namespace std;
using namespace cv;

const static int SENSITIVITY_VALUE = 10;
const static int BLUR_SIZE = 70;

const int FRAME_WIDTH = 640;
const int FRAME_HEIGHT = 480;

const int MIN_OBJECT_AREA = 20 * 20;


string intToString(int number){
    std::stringstream ss;
    ss << number;
    return ss.str();
}


void drawObject(vector<Thing> theThings, Mat &frame){

    for (int i = 0; i<theThings.size(); i++){
        cv::circle(frame, cv::Point(theThings.at(i).getXPos(), theThings.at(i).getYPos()), 10, cv::Scalar(0, 0, 255));
        cv::putText(frame, intToString(theThings.at(i).getXPos()) + " , " + intToString(theThings.at(i).getYPos()), cv::Point(theThings.at(i).getXPos(), theThings.at(i).getYPos() + 20), 1, 1, Scalar(0, 255, 0));
    }
}


void searchForMovement(Mat thresholdImage, Mat &cameraFeed){

    vector <Thing> ones;

    Mat temp;
    thresholdImage.copyTo(temp);

    vector< vector<Point> > contours;
    vector<Vec4i> hierarchy;

    findContours(temp, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    double refArea = 0;
    bool objectDetected = false;
    if (hierarchy.size() > 0) {
        for (int index = 0; index >= 0; index = hierarchy[index][0]) {

            Moments moment = moments((cv::Mat)contours[index]);
            double area = moment.m00;

            if (area > MIN_OBJECT_AREA){

                Thing one;

                one.setXPos(moment.m10 / area);
                one.setYPos(moment.m01 / area);

                ones.push_back(one);

                objectDetected = true;}

            else objectDetected = false;

        }

        if (objectDetected == true){drawObject(ones, cameraFeed);}
        else putText(cameraFeed, "NO OBJECT DETECTED\n", Point(0, 50), 1, 2, Scalar(0, 0, 255), 2);

    }
}


int main(){

    bool objectDetected = false;
    bool debugMode = false;
    bool trackingEnabled = false;
    bool pause = false;

    Mat frame1, frame2;
    Mat grayImage1, grayImage2;
    Mat differenceImage;
    Mat thresholdImage;


    VideoCapture capture;


    capture.open(0);
    capture.set(CV_CAP_PROP_FRAME_WIDTH, FRAME_WIDTH);
    capture.set(CV_CAP_PROP_FRAME_HEIGHT, FRAME_HEIGHT);

    if (!capture.isOpened()){
        cout << "ERROR ACQUIRING VIDEO FEED\n";
        getchar();
        return -1;}

    while (1){

        capture.read(frame1);
        cv::cvtColor(frame1, grayImage1, COLOR_BGR2GRAY);

        capture.read(frame2);
        cv::cvtColor(frame2, grayImage2, COLOR_BGR2GRAY);

        cv::absdiff(grayImage1, grayImage2, differenceImage);
        cv::threshold(differenceImage, thresholdImage, SENSITIVITY_VALUE, 255, THRESH_BINARY);

        if (debugMode == true){
            cv::imshow("Difference Image", differenceImage);
            cv::imshow("Threshold Image", thresholdImage);}
        else{
            cv::destroyWindow("Difference Image");
            cv::destroyWindow("Threshold Image");}

        cv::blur(thresholdImage, thresholdImage, cv::Size(BLUR_SIZE, BLUR_SIZE));
        cv::threshold(thresholdImage, thresholdImage, SENSITIVITY_VALUE, 255, THRESH_BINARY);

        if (debugMode == true){imshow("Final Threshold Image", thresholdImage);}
        else {cv::destroyWindow("Final Threshold Image");}

        if (trackingEnabled){searchForMovement(thresholdImage, frame1);}

        imshow("Frame1", frame1);

        switch (waitKey(10)){
        case 27: //'esc' key has been pressed, exit program.
            return 0;
        case 116: //'t' has been pressed. this will toggle tracking
            trackingEnabled = !trackingEnabled;
            if (trackingEnabled == false) cout << "Tracking disabled." << endl;
            else cout << "Tracking enabled." << endl;
            break;
        case 100: //'d' has been pressed. this will debug mode
            debugMode = !debugMode;
            if (debugMode == false) cout << "Debug mode disabled." << endl;
            else cout << "Debug mode enabled." << endl;
            break;
        case 112:
            pause = !pause;
            if (pause == true){
                cout << "Code paused, press 'p' again to resume" << endl;
                while (pause == true){
                    switch (waitKey()){

                    case 112:
                        //change pause back to false
                        pause = false;
                        cout << "Code resumed." << endl;
                        break;
                    }
                }
            }
        }
    }
    return 0;
}