loading...
  1. OpenCV : Motion Tracking with Recording using WebCam (Easy Way) aka Motion Activated Security Camera

OpenCV : Motion Tracking with Recording using WebCam (Easy Way) aka Motion Activated Security Camera

Hello Everybody, Pankaj here !!
Today we are going to be going over another OpenCV Tutotirla using C++, more importantly, we are going to use our new found knowledge of Image Processing to build our very own motion-activated surveillance camera.

Here is a little preview of what the program is going to look like when all is said and done:

You can see that once, there is motion detected in the camera’s field of vision, the program then starts recording and it will save to a video file on the hard drive and you’ll be able to open it up at the end of the day if you run it for the whole day and view all the activity that’s happened on your camera.

Some of the features that you’ll notice is that the camera only records when there is a motion on the screen. Also, we have added a little data and time stamp for your convinience just like the CCTV camera or a closed-circuit camera that’s used for survelliance.

So let’s look at the complete code and pay attention to the comments with each line of code to understand what every line is meant to do.
Let’s get started!
Open up visual studio and creat an empty project and then create a new motion.cpp file and paste the below code in it. Ensure that your build is correctly set and you have linked the opencv libraries and includes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
 
//http://pythonopencv.com
 
#include <opencv\cv.h>
#include <opencv\highgui.h>
#include <time.h>
 
using namespace std;
using namespace cv;
 
//our sensitivity value to be used in the absdiff() function
//for higher sensitivity, use a lower value
const static int SENSITIVITY_VALUE = 40;
//size of blur used to smooth the intensity image output from absdiff() function
const static int BLUR_SIZE = 10;
//these two can be toggled by pressing 'd' or 't'
bool debugMode;
bool trackingEnabled;
 
//int to string helper function
string intToString(int number){
 
	//this function has a number input and string output
	std::stringstream ss;
	ss << number;
	return ss.str();
}
 
 
string getDateTime(){
	//get the system time
	SYSTEMTIME theTime;
	GetLocalTime(&theTime);
	//create string to store the date and time
	string dateTime;
	//convert year to string
	string year = intToString(theTime.wYear);
	//use stringstream to add a leading '0' to the month (ie. 3 -> 03)
	//we use 'setw(2)' so that we force the string 2 characters wide with a zero in front of it.
	//if the month is '10' then it will remain '10'
	std::stringstream m;
	m<<std::setfill('0')<<std::setw(2)<< theTime.wMonth;
	string month = m.str();
	//day
	std::stringstream d;
	d<<std::setfill('0')<<std::setw(2)<< theTime.wDay;
	string day = d.str();
	//hour
	std::stringstream hr;
	hr<<setfill('0')<<std::setw(2)<<theTime.wHour;
	string hour = hr.str();
	//minute
	std::stringstream min;
	min<<setfill('0')<<std::setw(2)<<theTime.wMinute;
	string minute = min.str();
	//second
	std::stringstream sec;
	sec<<setfill('0')<<std::setw(2)<<theTime.wSecond;
	string second = sec.str();
 
	//here we use the year, month, day, hour, minute info to create a custom string
	//this will be displayed in the bottom left corner of our video feed.
	dateTime = year + "-" + month + "-" + day + "  " + hour + ":" + minute + ":" + second;
 
	return dateTime;
}
string getDateTimeForFile(){
	//this function does the exact same as "getDateTime()" 
	//however it returns a string that can be used as a filename
	SYSTEMTIME theTime;
	GetLocalTime(&theTime);
	string dateTime;
 
	string year = intToString(theTime.wYear);
 
	std::stringstream m;
	m<<std::setfill('0')<<std::setw(2)<< theTime.wMonth;
	string month = m.str();
 
	std::stringstream d;
	d<<std::setfill('0')<<std::setw(2)<< theTime.wDay;
	string day = d.str();
 
	std::stringstream hr;
	hr<<setfill('0')<<std::setw(2)<<theTime.wHour;
	string hour = hr.str();
 
	std::stringstream min;
	min<<setfill('0')<<std::setw(2)<<theTime.wMinute;
	string minute = min.str();
 
	std::stringstream sec;
	sec<<setfill('0')<<std::setw(2)<<theTime.wSecond;
	string second = sec.str();
 
	//here we use "_" instead of "-" and ":"
	//if we try to save a filename with "-" or ":" in it we will get an error.
	dateTime = year + "_" + month + "_" + day + "_" + hour + "h" + minute + "m" + second + "s";
 
	return dateTime;
}
 
bool detectMotion(Mat thresholdImage, Mat &cameraFeed){
	//create motionDetected variable.
	bool motionDetected = false;
	//create temp Mat for threshold image
	Mat temp;
	thresholdImage.copyTo(temp);
	//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 );// retrieves all contours
	findContours(temp,contours,hierarchy,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE );// retrieves external contours
 
	//if contours vector is not empty, we have found some objects
	//we can simply say that if the vector is not empty, motion in the video feed has been detected.
	if(contours.size()>0)motionDetected=true;
	else motionDetected = false;
 
	return motionDetected;
 
}
int main(){
	//set recording and startNewRecording initially to false.
	bool recording = false;
	bool startNewRecording = false;
	int inc=0;
	bool firstRun = true;
	//if motion is detected in the video feed, we will know to start recording.
	bool motionDetected = false;
 
	//pause and resume code (if needed)
	bool pause = false;
	//set debug mode and trackingenabled initially to false
	//these can be toggled using 'd' and 't'
	debugMode = false;
	trackingEnabled = true;
	//set up the matrices that we will need
	//the two frames we will be comparing
	Mat frame1,frame2;
	//their grayscale images (needed for absdiff() function)
	Mat grayImage1,grayImage2;
	//resulting difference image
	Mat differenceImage;
	//thresholded difference image (for use in findContours() function)
	Mat thresholdImage;
	//video capture object.
	VideoCapture capture;
	capture.open(0);
	VideoWriter oVideoWriter;//create videoWriter object, not initialized yet
	double dWidth = capture.get(CV_CAP_PROP_FRAME_WIDTH); //get the width of frames of the video
	double dHeight = capture.get(CV_CAP_PROP_FRAME_HEIGHT); //get the height of frames of the video
	//set framesize for use with videoWriter
	Size frameSize(static_cast<int>(dWidth), static_cast<int>(dHeight));
 
	if(!capture.isOpened()){
		cout<<"ERROR ACQUIRING VIDEO FEED\n";
		getchar();
		return -1;
	}
	while(1){
 
 
		//read first frame
		capture.read(frame1);
		//convert frame1 to gray scale for frame differencing
		cv::cvtColor(frame1,grayImage1,COLOR_BGR2GRAY);
		//copy second frame
		capture.read(frame2);
		//convert frame2 to gray scale for frame differencing
		cv::cvtColor(frame2,grayImage2,COLOR_BGR2GRAY);
		//perform frame differencing with the sequential images. This will output an "intensity image"
		//do not confuse this with a threshold image, we will need to perform thresholding afterwards.
		cv::absdiff(grayImage1,grayImage2,differenceImage);
		//threshold intensity image at a given sensitivity value
		cv::threshold(differenceImage,thresholdImage,SENSITIVITY_VALUE,255,THRESH_BINARY);
		if(debugMode==true){
			//show the difference image and threshold image
			cv::imshow("Difference Image",differenceImage);
			cv::imshow("Threshold Image", thresholdImage);
		}else{
			//if not in debug mode, destroy the windows so we don't see them anymore
			cv::destroyWindow("Difference Image");
			cv::destroyWindow("Threshold Image");
		}
		//blur the image to get rid of the noise. This will output an intensity image
		cv::blur(thresholdImage,thresholdImage,cv::Size(BLUR_SIZE,BLUR_SIZE));
		//threshold again to obtain binary image from blur output
		cv::threshold(thresholdImage,thresholdImage,SENSITIVITY_VALUE,255,THRESH_BINARY);
		if(debugMode==true){
			//show the threshold image after it's been "blurred"
 
			imshow("Final Threshold Image",thresholdImage);
 
		}
		else {
			//if not in debug mode, destroy the windows so we don't see them anymore
			cv::destroyWindow("Final Threshold Image");
		}
 
		//if tracking enabled, search for Motion
		if(trackingEnabled){
 
			//check for motion in the video feed
			//the detectMotion function will return true if motion is detected, else it will return false.
			//set motionDetected boolean to the returned value.
			motionDetected = detectMotion(thresholdImage,frame1);
 
		}else{ 
			//reset our variables if tracking is disabled
			motionDetected = false;
 
		}
 
////////////**STEP 1**//////////////////////////////////////////////////////////////////////////////////////////////////////////////
		//draw time stamp to video in bottom left corner. We draw it before we write so that it is written on the video file.
 
 
		//if we're in recording mode, write to file
		if(recording){
 
			//check if it's our first time running the program so that we don't create a new video file over and over again.
			//we use the same boolean check to create a new recording if we want.
			if(firstRun == true || startNewRecording == true){
 
//////////**STEP 3**///////////////////////////////////////////////////////////////////////////////////////////////////////////////
				//Create a unique filename for each video based on the date and time the recording has started
				string videoFileName = "D:/MyVideo"+intToString(inc)+".avi";
 
				cout << "File has been opened for writing: " << videoFileName<<endl;
 
				cout << "Frame Size = " << dWidth << "x" << dHeight << endl;
 
				oVideoWriter  = VideoWriter(videoFileName, CV_FOURCC('D', 'I', 'V', '3'), 20, frameSize, true);
 
				if ( !oVideoWriter.isOpened() ) 
				{
					cout << "ERROR: Failed to initialize video writing" << endl;
					getchar();
					return -1;
				}
				//reset our variables to false.
				firstRun = false;
				startNewRecording = false;
 
 
			}
 
			oVideoWriter.write(frame1);
			//show "REC" in top left corner in red
			//be sure to do this AFTER you write to the file so that "REC" doesn't show up on the recorded video file.
			//Cut and paste the following line above "oVideoWriter.write(frame1)" to see what I'm talking about.
			putText(frame1,"REC",Point(0,60),2,2,Scalar(0,0,255),2);
 
 
		}
 
 
 
		//check if motion is detected in the video feed.
		if(motionDetected){
			//show "MOTION DETECTED" in bottom left corner in green
			putText(frame1,"MOTION DETECTED",cv::Point(0,420),2,2,cv::Scalar(0,255,0));
 
//////////**STEP 2**///////////////////////////////////////////////////////////////////////////////////////////////////////////////
			//set recording to true since there is motion in the video feed.
			//else recording should be false.
 
 
		}
		//show our captured frame
		imshow("Frame1",frame1);
 
		//check to see if a button has been pressed.
		//the 30ms delay is necessary for proper operation of this program
		//if removed, frames will not have enough time to referesh and a blank image will appear.
		switch(waitKey(30)){
 
		case 27: //'esc' key has been pressed, exit program.
			return 0;
		case 116: //'t' has been pressed. this will toggle tracking (disabled for security cam)
			/*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: //'p' has been pressed. this will pause/resume the code.
			pause = !pause;
			if(pause == true){ cout<<"Code paused, press 'p' again to resume"<<endl;
			while (pause == true){
				//stay in this loop until 
				switch (waitKey()){
					//a switch statement inside a switch statement? Mind blown.
				case 112: 
					//change pause back to false
					pause = false;
					cout<<"Code Resumed"<<endl;
					break;
				}
			}
			}
 
		case 114:
			//'r' has been pressed.
			//toggle recording mode
			recording =!recording;
 
			if (!recording)cout << "Recording Stopped" << endl;
 
			else cout << "Recording Started" << endl;
 
			break;
 
		case 110:
			//'n' has been pressed
			//start new video file
			startNewRecording = true;
			recording = true;
			cout << "New Recording Started" << endl;
			//increment video file name
			inc+=1;
			break; 
 
		}
 
	}
 
	return 0;
 
}




Leave a Reply

Your email address will not be published. Required fields are marked *

Welcome to OpenCV World !! Come as a Guest, stay as a Family