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;
}