Hi all,
I would like to get an idea of the best way to go about tracking people with an ceiling-mounted IP camera. Ultimately, I would like to have an application that does the following:
- show the video feed in a primary window
- identifies moving objects then assigns them with an identifier of some kind (object1, object2, etc.)
- in the primary window, draw a rectangle around each object and add the object ID to it as a tag
- capture the x and y coordinates of the object or rectangle, preferably the center (it seems that this can be done using the moments() method)
- in a second window show a binary image of blobs/people in black and everything else in white
- on the binary image, draw a line from the previous x,y coordinates of each object to the current coordinates
EDIT: I am currently getting far too much noise in my image, and cannot limit it with the filters I'm using. I'm also unable to track multiple objects. This may be the result of the example I borrowed from, in which only the object with the largest area is tagged. Unfortunately, I have not been playing with BackgroundSubtractorMOG2, but before I move on I would like able to know if there's a better way of going about this. I'm working in C++ on a Win7 machine. I've just started working with OpenCV, and my C++ skills are fairly limited. In fact my only real C experience is with Objective-C, so any advice on how to achieve the above tasks would be greatly appreciated.fix this.
My code to date:
#include <sstream>
<opencv2/opencv.hpp>
#include <string>
"opencv2/imgproc/imgproc.hpp"
#include <iostream>
"opencv2/highgui/highgui.hpp"
#include <opencv\highgui.h>
"highgui.h"
#include <opencv\cv.h>
<stdlib.h>
#include <vector>
<stdio.h>
#include <background_segm.hpp>
"opencv2\video\background_segm.hpp"
#include <opencv2/opencv.hpp>
<Windows.h>
using namespace cv;
using namespace std;
/// Global variables
int nmixtures = 25;
hist = 0;
int bShadowDetection const histMax = 10;
int thresh = 0;
int const threshMax = 5000;
int erosion_elem = 0;
int erosion_size = 0;
int dilation_elem = 0;
int dilation_size = 0;
int const max_elem = 2;
int const max_kernel_size = 21;
int lowerH=0;
int lowerS=0;
int lowerV=0;
int upperH=180;
int upperS=256;
int upperV=256;
int MIN_NUM_OBJECTS = 1;
int history = 25;
const string trackbarWindowName = "Trackbars";
MAX_NUM_OBJECTS=100;
int MIN_OBJECT_AREA = 1*1;
int MAX_OBJECT_AREA = 5000*5000;
Mat curVid; // live feed from IP Cam
Mat imgHSV;
Mat erosion_dst;
Mat dilation_dst;
Mat blur_dst;
Mat mask; // final filtered image
Mat test; // test for draw objects
/** Function Headers */
void on_trackbar( setwindowSettings();
void HSV();
void Erosion( int, void* ){
//This function gets called whenever a trackbar position is changed
}
);
void createTrackbars(){
//create window for trackbars
namedWindow(trackbarWindowName,0);
//create memory to store trackbar name on window
char TrackbarName[50];
sprintf_s( TrackbarName, "nmixtures", nmixtures);
sprintf_s( TrackbarName, "bShadowDetection", bShadowDetection);
sprintf_s( TrackbarName, "history", history);
//create trackbars and insert them into window
createTrackbar( "nmixtures", trackbarWindowName, &nmixtures, nmixtures, on_trackbar Dilation( int, void* );
createTrackbar( "bShadowDetection", trackbarWindowName, &bShadowDetection, bShadowDetection, on_trackbar void Blur( int, void* );
createTrackbar( "history", trackbarWindowName, &history, history, on_trackbar );
}
IplImage* GetThresholdedImage(IplImage* imgHSV);
void trackFilteredObject();
void drawObject(int x,int y,Mat &frame);
string intToString(int number);
int main(int argc, char *argv[])
{
//if we would like // get cam feed and show in new window
VideoCapture cap(0);
if (!cap.isOpened()) // if not success, exit program
{
cout << "Cannot open the video file" << endl;
return -1;
}
setwindowSettings();
// background subtraction init
BackgroundSubtractorMOG2 bg = BackgroundSubtractorMOG2(hist,thresh,false);
cap.read(curVid);
Sleep(5000);
// show images in windows
while(1)
{
// show camera feed
bool bSuccess = cap.read(curVid); // read a new frame from video
if (!bSuccess) // if not success, break loop
{
cout << "Cannot read a frame from video file" << endl;
break;
}
// start morph functions then draw
HSV();
Erosion( 0, 0 );
Dilation( 0, 0 );
// background subtraction
bg(dilation_dst,mask,-1);
imshow("mask",mask);
// draw to calibrate our values, set to true.
bool calibrationMode = true;
cv::Mat frame;
cv::Mat back;
cv::Mat fore;
const bool boolShadowDetection = false;
if(calibrationMode){
//create slider bars curVid
trackFilteredObject();
imshow("test", test);
// escape program with keystroke
if (waitKey(30) == 27) //wait for HSV filtering
createTrackbars();
if(bShadowDetection == 1){
const bool boolShadowDetection = true;
} else if (bShadowDetection == 0){
const bool boolShadowDetection = false;
'esc' key press for 30ms. If 'esc' key is pressed, break loop
{
cout << "esc key is pressed by user" << endl;
break;
}
}
cv::VideoCapture cap("IPCamAddress");
float fltHistory = (float)history;
float fltNmixtures = (float)nmixtures;
cv::BackgroundSubtractorMOG2 bg (history,fltNmixtures,boolShadowDetection);
std::vector<std::vector<cv::Point> > contours;
cv::namedWindow("Frame");
cv::namedWindow("Background");
for(;;)
{
cap >> frame;
bg.operator ()(frame,fore);
bg.getBackgroundImage(back);
cv::erode(fore,fore,cv::Mat());
cv::dilate(fore,fore,cv::Mat());
cv::findContours(fore,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);
cv::drawContours(frame,contours,-1,cv::Scalar(0,0,255),2);
cv::imshow("Frame",frame);
cv::imshow("Background",back);
if(cv::waitKey(30) >= 0) break;
}
return 0;
}
void setwindowSettings()
{
namedWindow("Settings", CV_WINDOW_AUTOSIZE);
resizeWindow("Settings",500,600);
namedWindow("mask", CV_WINDOW_AUTOSIZE);
namedWindow("test", CV_WINDOW_AUTOSIZE);
// Create Erosion Trackbar
createTrackbar( "EroElem", "Settings",
&erosion_elem, max_elem,
Erosion );
createTrackbar( "EroSize", "Settings",
&erosion_size, max_kernel_size,
Erosion );
/// Create Dilation Trackbar
createTrackbar( "DialElem", "Settings",
&dilation_elem, max_elem,
Dilation );
createTrackbar( "DialSize", "Settings",
&dilation_size, max_kernel_size,
Dilation );
/*/// Create GaussianBlur Trackbars
createTrackbar( "blurType", "Settings",
&blurType, max_blurType,
Blur );*/
/// Create History Trackbar
createTrackbar( "ImgHist", "Settings",
&hist, histMax );
/// Create Threshold Trackbar
createTrackbar( "Threshold", "Settings",
&thresh, threshMax );
// Create NUM_OBJECTS Trackbar
createTrackbar("MIN_NUM_OBJECTS", "Settings",
&MIN_NUM_OBJECTS, 100);
createTrackbar("MAX_NUM_OBJECTS", "Settings",
&MAX_NUM_OBJECTS, 100);
// Create NUM_OBJECTS Trackbar
createTrackbar("MIN_OBJECT_AREA", "Settings",
&MIN_OBJECT_AREA, 5000*5000);
createTrackbar("MAX_OBJECT_AREA", "Settings",
&MAX_OBJECT_AREA, 5000*5000);
}
/** @function HSV */
void HSV()
{
//cvGetSize(curVid);
//Mat* img_HSV = cvCreateImage(cvGetSize(curVid), IPL_DEPTH_8U, 3);
cvtColor(curVid,imgHSV,CV_BGR2HSV);
//imgHSV = GetThresholdImage(img_HSV);
//imshow("HSV", imgHSV);
}
/** @function Erosion */
void Erosion( int, void* )
{
int erosion_type;
if( erosion_elem == 0 ){ erosion_type = MORPH_RECT; }
else if( erosion_elem == 1 ){ erosion_type = MORPH_CROSS; }
else if( erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; }
Mat element = getStructuringElement( erosion_type,
Size( 2*erosion_size + 1, 2*erosion_size+1 ),
Point( erosion_size, erosion_size ) );
/// Apply the erosion operation
erode( imgHSV, erosion_dst, element );
}
/** @function Dilation */
void Dilation( int, void* )
{
int dilation_type;
if( dilation_elem == 0 ){ dilation_type = MORPH_RECT; }
else if( dilation_elem == 1 ){ dilation_type = MORPH_CROSS; }
else if( dilation_elem == 2) { dilation_type = MORPH_ELLIPSE; }
Mat element = getStructuringElement( dilation_type,
Size( 2*dilation_size + 1, 2*dilation_size+1 ),
Point( dilation_size, dilation_size ) );
/// Apply the dilation operation
dilate( erosion_dst, dilation_dst, element );
}
void trackFilteredObject(){
int x,y;
Mat temp;
mask.copyTo(temp);
//inRange(mask,Scalar(MIN_OBJECT_AREA),Scalar(MAX_OBJECT_AREA),temp);
curVid.copyTo(test);
//these two vectors needed for output of findContours
vector< vector<Point> > contours;
vector<Vec4i> hierarchy;
//find contours of filtered image using openCV findContours function
findContours(temp,contours,hierarchy,CV_RETR_CCOMP,CV_CHAIN_APPROX_SIMPLE );
// iterate through all the top-level contours,
// draw each connected component with its own random color
int idx = 0;
for( ; idx >= 0; idx = hierarchy[idx][0] )
{
Scalar color( rand()&0, rand()&0, rand()&255 );
drawContours( test, contours, idx, color, CV_FILLED, 8, hierarchy );
}
//use moments method to find our filtered object
double refArea = 0;
bool objectFound = false;
cout << hierarchy.size() << endl;
if (hierarchy.size() > 0) {
int numObjects = hierarchy.size();
//if number of objects greater than MAX_NUM_OBJECTS we have a noisy filter
if(numObjects<MAX_NUM_OBJECTS){
for (int index = 0; index >= 0; index = hierarchy[index][0]) {
Moments moment = moments((cv::Mat)contours[index]);
double area = moment.m00;
//if the area is less than 20 px by 20px then it is probably just noise
//if the area is the same as the 3/2 of the image size, probably just a bad filter
//this is setup to get the object with the largest area so we safe a reference area each
//iteration and compare it to the area in the next iteration. DO NOT WANT THIS!
if(area>MIN_OBJECT_AREA){
x = moment.m10/area;
y = moment.m01/area;
objectFound = true;
}else objectFound = false;
}
//let user know you found an object
if(objectFound ==true){
//draw object location on screen
drawObject(x,y,test);}
}else putText(test,"TOO MUCH NOISE! ADJUST FILTER",Point(0,50),1,2,Scalar(0,0,255),2);
}
}
void drawObject(int x,int y,Mat &frame){
circle(frame,cv::Point(x,y),10,cv::Scalar(0,0,255),3);
putText(frame,intToString(x)+ " , " + intToString(y),cv::Point(x,y+20),1,1,Scalar(0,255,0));
}
string intToString(int number){
std::stringstream ss;
ss << number;
return ss.str();
}