-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmain.cpp
More file actions
407 lines (337 loc) · 11.1 KB
/
main.cpp
File metadata and controls
407 lines (337 loc) · 11.1 KB
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
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <vector>
#include <utility>
#include <tuple>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "opencv2/objdetect.hpp"
using namespace cv;
using namespace std;
//MOTION PROTOTYPES
bool motiondetect(int sensitivity, Mat prev_frame, Mat curr_frame);
void exclusionzone(tuple<int, int> top_left,tuple<int, int> bottom_right, Mat in_mat, Mat &ex_mat);
void populatezones(Mat in, Mat &out);
void onmouse(int action, int x, int y, int, void*);
void onTrackbar(int pos, void*);
int motioncalibrate(VideoCapture camera);
//PET PROTOTYPES
bool catDetect(Mat frame);
//GLOBAL VARIABLES
CascadeClassifier cat_cascade;
vector<pair<tuple<int, int>, tuple<int, int>>> exclusion_collection;//data-struct of tuples each rectangle is comprised of a pair of points
tuple<int, int> tl(-1,-1); //top right
tuple<int, int> br(-1,-1); //bot left
Mat temp;
int sliderValue = 0;
int sensitivity = 1;
//KEYBINDS
#define YES_KEY (89)
#define YES_KEY_LOWER (121)
#define NO_KEY (78)
#define NO_KEY_LOWER (110)
#define ENTER_KEY (13)
#define ESCAPE_KEY (27)
// MAIN
int main(int argc, char* argv[]) {
//create the keys for the arg parser
const String keys =
"{help h usage ? || print this message }"
"{c || specify a camera to use}"
"{b calibrate || run motion calibration for exclusions and sensitivity before starting the program}"
"{d diff || use the diff tool to compare two images for changes}"
;
//parce command line arguments arguments
cv::CommandLineParser parser(argc, argv, keys);
//local variables
VideoCapture cap;
if (parser.has("help") || parser.has("h")){
parser.printMessage();
return 0;
}
if (parser.has("c")){
int cam = parser.get<int>("c");
cout << "Using Capture Device: " << cam << endl;
cap.open(cam);
if(!cap.isOpened()) {
cout << "--(!)Error opening camera\n";
return -1;
}
}
if (parser.has("calibrate") || parser.has("b")){
//check if a camera is opened already
if(!cap.isOpened()){
//attempt to open default camera
cap.open(0);
}
if(!cap.isOpened()) {
cout << "--(!)Error opening camera\n";
return -1;
}
//run calibration
motioncalibrate(cap);
}
tuple<int, int> tr(170, 83);
tuple<int, int> bl(400, 377);
namedWindow("Original");
namedWindow("Excluded");
Mat frame;
Mat prev_frame;
Mat excluded_frame;
while(true) {
prev_frame = frame.clone(); // save current frame for motion detection
cap >> frame;
if(frame.empty()) {
cout << "--(!)No frame captured from camera\n";
break;
}
//if exclusion zones exist populate them all now
populatezones(frame, excluded_frame);
// exclusionzone(tr, bl, frame, excluded_frame);
imshow("Original", frame);
imshow("Excluded", excluded_frame);
if(!prev_frame.empty()) {
Mat excluded_prev_frame;
populatezones(prev_frame, excluded_prev_frame);
// Check for motion, if yes pass frame to catDetect
if(motiondetect(sensitivity, excluded_prev_frame, excluded_frame) == true) {
catDetect(frame);
}
}
// Exit on ESC key
if(waitKey(1) == 27)
break;
}
return 0;
}
//MOTION DETECTION FUNCTIONS
bool motiondetect(int sensitivity, Mat prev_frame, Mat curr_frame){
/*
sensitivity: the minimum difference needed for motion detection to trigger
prev_frame: the previous frame captured from the camera
curr_frame: the most recent capture from the camera
motion detection will return true if the absolute difference detected is above the given threshold
TODO: a default should be determined if the sensitivity is not given or zero is given
*/
int diff_value;
Mat diff_frame;
//for now we will just do a color diff not a grayscale
absdiff(prev_frame, curr_frame, diff_frame);
diff_value = (unsigned int)cv::sum(diff_frame)[0]; //add up only the blue frame
if (diff_value > sensitivity){
//motion detected
return true;
}
return false;
}
void exclusionzone(tuple<int, int> top_left, tuple<int, int> bottom_right, Mat in_mat, Mat &ex_mat){
/*
example mat is 250x250
example inputs
x y
topleft = (34, 27)
topright = (123, 238)
*/
//this program will draw a white box over an area in the mat which excludes it from motion detection.
//get the coordinates of the tuples for top right and lower left
int x1 = get<0>(top_left);
int y1 = get<1>(top_left);
int x2 = get<0>(bottom_right);
int y2 = get<1>(bottom_right);
//clone in_mat to ex_mat
ex_mat = in_mat.clone();
if (x1 > x2) swap(x1, x2);
if (y1 > y2) swap(y1, y2);
//loop through ex_mat and overwrite pixels
// cout << ex_mat
for(int y = 0; y < ex_mat.rows; y++){
if(y >= y1 && y <=y2){
//if row is in range
for(int x = 0; x < ex_mat.cols; x++){
//if col is in range
if(x >= x1 && x <=x2){
ex_mat.at<Vec3b>(y,x)[0] = 0;
ex_mat.at<Vec3b>(y,x)[1] = 0;
ex_mat.at<Vec3b>(y,x)[2] = 0;
//pixel == 255
}
}
}
}
}
void populatezones( Mat in, Mat &out){
if(!exclusion_collection.empty()){
//loop through collection
out = in.clone();
for (auto& [p1, p2] : exclusion_collection){
exclusionzone(p1, p2, out, out);
}
}
}
void onTrackbar(int pos, void*) {
sensitivity = pos;
}
void onmouse(int action, int x, int y, int, void*){
if(action == EVENT_LBUTTONDOWN){
cout << "click detected (" << x << ", " << y << ")" << endl;
//mouse click
tl = make_tuple(x,y);
}else if(action == EVENT_LBUTTONUP){
cout << "unclick detected (" << x << ", " << y << ")" << endl;
//unclick
br = make_tuple(x,y);
cout << "keep? (y/n/enter/esc)" << endl;
}
}
int motioncalibrate(VideoCapture camera){
//sensitivity num
Mat frame, exclusions, e_instructions;
bool done = false;
//start capture from cam
// VideoCapture cam0(0);
namedWindow("exclusion selection");
//TODO: write on the window instructions "click and drag a square to exclude area"
//capture frame
camera.read(frame);
exclusions = frame.clone();
//loop
//void cv::setMouseCallback (const String &winname, MouseCallback onMouse, void * userdata = 0 )
bool skip_switch = false;
int key = -1;
//GET EXCLUSION ZONES
while(1){
//wait for bottom left point to populate
while(get<1>(br) == -1 && get<1>(tl) == -1){
string instruct = "click and drag to select an area or press enter to continue";
e_instructions = exclusions.clone();
putText(e_instructions, instruct, Point(20, 20), 2, 0.5, Scalar(50,50,50), 2);
imshow("exclusion selection", e_instructions);
//draw exclusion with mouse
setMouseCallback("exclusion selection", onmouse);
char input;
if((input = waitKey(1)) == ESCAPE_KEY){
return -1;
}else if(input == ENTER_KEY){
skip_switch = true;
done = true;
break;
}
}
string instruct = "keep this area (y/n); esc to quit; enter to discard and continue";
e_instructions = exclusions.clone();
exclusionzone(tl, br, exclusions, e_instructions);
putText(e_instructions, instruct, Point(20, 20), 2, 0.5, Scalar(50,50,50), 2);
imshow("exclusion selection", e_instructions);
if(!skip_switch){
key = waitKey(1);
}
pair<tuple<int, int>, tuple<int, int>> pair;
switch(key){
//keep (y/n)
case YES_KEY:
case YES_KEY_LOWER:
//write
cout << "yes" << endl;
cout << "(" << get<0>(tl) << ", " << get<1>(tl) << ") ; (" << get<0>(br) << ", " << get<1>(br) << ")" << endl;
pair = make_pair(tl,br);
exclusion_collection.push_back(pair);
//update mat
exclusionzone(tl, br, exclusions, exclusions);
tl = make_tuple(-1,-1);
br = make_tuple(-1,-1);
break;
case NO_KEY:
case NO_KEY_LOWER:
cout << "no" << endl;
tl = make_tuple(-1,-1);
br = make_tuple(-1,-1);
cout << "click and drag again, enter to continue, esc to quit" << endl;
break;
//done? (enter)
case ENTER_KEY:
done = true;
break;
//exit (esc)
case ESCAPE_KEY:
return -1;
break;//useless but looks nice
case -1:
continue;
default:
break;
}
if(done){
break;
}
}
//show camera with sensitivity bar
Mat ex2, ex1, f1;
ex2 = exclusions.clone();
bool capture = false;
sensitivity = 1;
destroyWindow("exclusion selection");
//SENSITIVITY CALIBRATE
while(1){
//press enter to save value
//TODO: toggle exclusions (space?)
//create window
namedWindow("sensitivity calibration");
//read cam
camera.read(frame);
//exclude selection(s)
populatezones(frame, ex1);
//create slider
createTrackbar("sensitivity", "sensitivity calibration", nullptr, (frame.rows * frame.cols * 5), onTrackbar);
//check motion
capture = motiondetect(sensitivity, ex2, ex1);
//write if detect is true on f1
string text = (capture) ? "motion detected! (" : "no motion (";
text.append(to_string(sensitivity));
text.append(")");
f1 = ex1.clone();
putText(f1, text, Point(20, 20), 2, 0.5, Scalar(50,50,50), 2);
//show frame
imshow("sensitivity calibration", f1);
key = waitKey(1);
//exit if enter or esc
if (key == ESCAPE_KEY){
return -1;
}else if(key == ENTER_KEY){
break;
}
ex2 = ex1.clone();
}
destroyWindow("sensitivity calibration");
return 0;
}
/*
Credits:
https://docs.opencv.org/4.11.0/db/d28/tutorial_cascade_classifier.html
https://github.com/opencv/opencv/tree/4.x/data/haarcascades
*/
//PET DETECTION FUNCTIONS
bool catDetect(Mat frame)
{
// Load the cascade
if(!cat_cascade.load("haarcascade_frontalcatface.xml"))
{
cout << "--(!)Error loading cat cascade\n";
return -1;
}
Mat frame_gray;
cvtColor(frame, frame_gray, COLOR_BGR2GRAY);
equalizeHist(frame_gray, frame_gray);
// Detect cats
std::vector<Rect> cats;
cat_cascade.detectMultiScale(frame_gray, cats);
for(size_t i = 0; i < cats.size(); i++)
{
Point center(cats[i].x + cats[i].width/2, cats[i].y + cats[i].height/2);
ellipse(frame, center, Size(cats[i].width/2, cats[i].height/2), 0, 0, 360, Scalar(255, 0, 255), 4);
cout << "Cat detected!" << endl;
}
imshow("Cat Detection", frame);
return true;
}