1
1
// Example 15-3. Computing the on and off-diagonal elements of a variance/covariance model
2
+ #include < opencv2/opencv.hpp>
3
+ #include < vector>
4
+ #include < iostream>
5
+ #include < cstdlib>
6
+ #include < fstream>
7
+
8
+ using namespace std ;
2
9
3
10
vector<cv::Mat> planes (3 );
4
11
vector<cv::Mat> sums (3 );
5
12
vector<cv::Mat> xysums (6 );
13
+ cv::Mat sum, sqsum;
6
14
int image_count = 0 ;
7
15
8
16
// A function to accumulate
9
17
// the information we need for our variance computation:
18
+ //
10
19
void accumulateVariance (
11
- cv::Mat& I
12
- ) {
13
- if ( sum.empty ) {
20
+ cv::Mat& I) {
21
+ if ( sum.empty () ) {
14
22
sum = cv::Mat::zeros ( I.size (), CV_32FC (I.channels ()) );
15
23
sqsum = cv::Mat::zeros ( I.size (), CV_32FC (I.channels ()) );
24
+ image_count = 0 ;
16
25
}
17
26
cv::accumulate ( I, sum );
18
27
cv::accumulateSquare ( I, sqsum );
@@ -23,29 +32,43 @@ void accumulateVariance(
23
32
// (note that 'variance' is sigma^2)
24
33
//
25
34
void computeVariance (
26
- cv::Mat& variance
27
- ) {
35
+ cv::Mat& variance) {
28
36
double one_by_N = 1.0 / image_count;
29
- variance = one_by_N * sqsum – ( one_by_N * one_by_N) * sum.mul (sum);
37
+ variance = ( one_by_N * sqsum) - (( one_by_N * one_by_N) * sum.mul (sum) );
30
38
}
31
39
40
+ // Same as above function, but compute standard deviation
41
+ void computeStdev (
42
+ cv::Mat& std__) {
43
+ double one_by_N = 1.0 / image_count;
44
+ cv::sqrt (((one_by_N * sqsum) -((one_by_N * one_by_N) * sum.mul (sum))), std__);
45
+ }
32
46
47
+ // And avg images
48
+ void computeAvg (
49
+ cv::Mat& av) {
50
+ double one_by_N = 1.0 / image_count;
51
+ av = one_by_N * sum;
52
+ }
53
+
54
+ // ===================================================================//
33
55
34
56
35
57
void accumulateCovariance (
36
58
cv::Mat& I
37
59
) {
38
60
int i, j, n;
39
61
40
- if ( sum.empty ) {
62
+ if ( sum.empty () ) {
63
+ image_count = 0 ;
41
64
for ( i=0 ; i<3 ; i++ ) {
42
65
// the r, g, and b sums
43
66
sums[i]
44
- = cv::Mat::zeros ( I.size (), CV::F32C1 );
67
+ = cv::Mat::zeros ( I.size (), CV_32FC1 );
45
68
}
46
69
for ( n=0 ; n<6 ; n++ ) {
47
70
// the rr, rg, rb, gg, gb, and bb elements
48
- xysums[n] = cv::Mat::zeros ( I.size (), CV::F32C1 ) );
71
+ xysums[n] = cv::Mat::zeros ( I.size (), CV_32FC1 );
49
72
}
50
73
}
51
74
cv::split ( I, planes );
@@ -64,11 +87,11 @@ cv::Mat& I
64
87
image_count++;
65
88
}
66
89
67
- // The corresponding compute function is also just a slight extension of the compute
68
- // function for the variances we saw earlier.
90
+ // The corresponding compute function is also just a slight extension of
91
+ // the compute function for the variances we saw earlier.
69
92
// note that 'variance' is sigma^2
70
93
//
71
- void computeVariance (
94
+ void computeCoariance (
72
95
cv::Mat& covariance
73
96
// a six-channel array, channels are the
74
97
// rr, rg, rb, gg, gb, and bb elements of Sigma_xy
@@ -83,12 +106,203 @@ void computeVariance(
83
106
for ( int j=i; j<3 ; j++ ) {
84
107
// "column" of Sigma
85
108
n++;
86
- xysums[n] = one_by_N * xysums[n]
87
- – ( one_by_N * one_by_N) * sums[i].mul (sums[j]);
109
+ xysums[n] = ( one_by_N * xysums[n])
110
+ - (( one_by_N * one_by_N) * sums[i].mul (sums[j]) );
88
111
}
89
112
}
90
113
91
114
// reassemble the six individual elements into a six-channel array
92
115
//
93
116
cv::merge ( xysums, covariance );
94
117
}
118
+
119
+ // //////////////////////////////////////////////////////////////////////
120
+ // ///////////Utilities to run///////////////////////////////////////////
121
+
122
+ void help (char ** argv ) {
123
+ cout << " \n "
124
+ << " Compute mean and std on <#frames to train on> frames of an incoming video, then run the model\n "
125
+ << argv[0 ] <<" <#frames to train on> <avi_path/filename>\n "
126
+ << " For example:\n "
127
+ << argv[0 ] << " 50 ../tree.avi\n "
128
+ << " 'a' to adjust thresholds, esc, 'q' or 'Q' to quit"
129
+ << endl;
130
+ }
131
+
132
+ // //////////// Borrowed code from example_15-02 //////////////////////
133
+
134
+ // Global storage
135
+ //
136
+ // Float, 3-channel images
137
+ //
138
+ cv::Mat image; // movie frame
139
+ cv::Mat IavgF, IdiffF, IhiF, IlowF; // threshold
140
+ cv::Mat tmp, mask; // scratch and our mask
141
+
142
+ // Float, 1-channel images
143
+ //
144
+ vector<cv::Mat> Igray (3 ); // scratch to split image
145
+ vector<cv::Mat> Ilow (3 );// low per pixel thresh
146
+ vector<cv::Mat> Ihi (3 ); // high per pixel thresh
147
+
148
+ // Byte, 1-channel image
149
+ //
150
+ cv::Mat Imaskt; // Temp mask
151
+
152
+ // Thresholds
153
+ //
154
+ float high_thresh = 21.0 ; // scaling the thesholds in backgroundDiff()
155
+ float low_thresh = 2.0 ; //
156
+
157
+ // I is just a sample image for allocation purposes
158
+ // (passed in for sizing)
159
+ //
160
+ void AllocateImages ( const cv::Mat& I ) {
161
+ cv::Size sz = I.size ();
162
+ IavgF = cv::Mat::zeros (sz, CV_32FC3 );
163
+ IdiffF = cv::Mat::zeros (sz, CV_32FC3 );
164
+ IhiF = cv::Mat::zeros (sz, CV_32FC3 );
165
+ IlowF = cv::Mat::zeros (sz, CV_32FC3 );
166
+ tmp = cv::Mat::zeros ( sz, CV_32FC3 );
167
+ Imaskt = cv::Mat ( sz, CV_32FC1 );
168
+ }
169
+
170
+
171
+ void setHighThreshold ( float scale ) {
172
+ IhiF = IavgF + (IdiffF * scale);
173
+ cv::split ( IhiF, Ihi );
174
+ }
175
+
176
+ void setLowThreshold ( float scale ) {
177
+ IlowF = IavgF - (IdiffF * scale);
178
+ cv::split ( IlowF, Ilow );
179
+ }
180
+
181
+ void createModelsfromStats () {
182
+ // IavgF is already set;
183
+ // IdiffF is the standard deviation image...
184
+
185
+ // Make sure diff is always something
186
+ //
187
+ IdiffF += cv::Scalar ( 0.1 , 0.1 , 0.1 );
188
+ setHighThreshold ( high_thresh);
189
+ setLowThreshold ( low_thresh);
190
+ }
191
+
192
+
193
+ // Create a binary: 0,255 mask where 255 (red) means foreground pixel
194
+ // I Input image, 3-channel, 8u
195
+ // Imask Mask image to be created, 1-channel 8u
196
+ //
197
+ void backgroundDiff (
198
+ cv::Mat& I,
199
+ cv::Mat& Imask) {
200
+
201
+ I.convertTo ( tmp, CV_32F ); // To float
202
+ cv::split ( tmp, Igray );
203
+
204
+ // Channel 1
205
+ //
206
+ cv::inRange ( Igray[0 ], Ilow[0 ], Ihi[0 ], Imask );
207
+
208
+ // Channel 2
209
+ //
210
+ cv::inRange ( Igray[1 ], Ilow[1 ], Ihi[1 ], Imaskt );
211
+ Imask = cv::min ( Imask, Imaskt );
212
+
213
+ // Channel 3
214
+ //
215
+ cv::inRange ( Igray[2 ], Ilow[2 ], Ihi[2 ], Imaskt );
216
+ Imask = cv::min ( Imask, Imaskt );
217
+
218
+ // Finally, invert the results
219
+ //
220
+ Imask = 255 - Imask;
221
+ }
222
+
223
+
224
+ void showForgroundInRed ( char ** argv, const cv::Mat &img) {
225
+ cv::Mat rawImage;
226
+ cv::split ( img, Igray );
227
+ Igray[2 ] = cv::max ( mask, Igray[2 ] );
228
+ cv::merge ( Igray, rawImage );
229
+ cv::imshow ( argv[0 ], rawImage );
230
+ cv::imshow (" Segmentation" , mask);
231
+ }
232
+
233
+ void adjustThresholds (char ** argv, cv::Mat &img) {
234
+ int key = 1 ;
235
+ while ((key = cv::waitKey ()) != 27 && key != ' Q' && key != ' q' ) // Esc or Q or q to exit
236
+ {
237
+ if (key == ' L' ) { low_thresh += 0.2 ;}
238
+ if (key == ' l' ) { low_thresh -= 0.2 ;}
239
+ if (key == ' H' ) { high_thresh += 0.2 ;}
240
+ if (key == ' h' ) { high_thresh -= 0.2 ;}
241
+ cout << " H or h, L or l, esq or q to quit; high_thresh = " << high_thresh << " , " << " low_thresh = " << low_thresh << endl;
242
+ setHighThreshold (high_thresh);
243
+ setLowThreshold (low_thresh);
244
+ backgroundDiff (img, mask);
245
+ showForgroundInRed (argv, img);
246
+ }
247
+ }
248
+
249
+ int main ( int argc, char ** argv) {
250
+ cv::namedWindow ( argv[0 ], cv::WINDOW_AUTOSIZE );
251
+ cv::VideoCapture cap;
252
+ if ((argc < 3 )|| !cap.open (argv[2 ])) {
253
+ cerr << " Couldn't run the program" << endl;
254
+ help (argv);
255
+ cap.open (0 );
256
+ return -1 ;
257
+ }
258
+ int number_to_train_on = atoi ( argv[1 ] );
259
+
260
+ // FIRST PROCESSING LOOP (TRAINING):
261
+ //
262
+ int image_count = 0 ;
263
+ int key;
264
+ bool first_frame = true ;
265
+ cout << " Total frames to train on = " << number_to_train_on << endl; // db
266
+ while (1 ) {
267
+ cout << " frame#: " << image_count << endl;
268
+ cap >> image;
269
+ if ( !image.data ) exit (1 ); // Something went wrong, abort
270
+ if (image_count == 0 ) AllocateImages ( image );
271
+ accumulateVariance (image);
272
+ cv::imshow ( argv[0 ], image );
273
+ image_count++;
274
+ if ( (key = cv::waitKey (7 )) == 27 || key == ' q' || key == ' Q' || image_count >= number_to_train_on) break ; // Allow early exit on space, esc, q
275
+ }
276
+
277
+ // We have accumulated our training, now create the models
278
+ //
279
+ cout << " Creating the background model" << endl;
280
+ computeAvg (IavgF);
281
+ computeStdev (IdiffF);
282
+ createModelsfromStats ();
283
+ cout << " Done! Hit any key to continue into single step. Hit 'a' or 'A' to adjust thresholds, esq, 'q' or 'Q' to quit\n " << endl;
284
+
285
+ // SECOND PROCESSING LOOP (TESTING):
286
+ //
287
+ cv::namedWindow (" Segmentation" , cv::WINDOW_AUTOSIZE ); // For the mask image
288
+ while ((key = cv::waitKey ()) != 27 || key == ' q' || key == ' Q' ) { // esc, 'q' or 'Q' to exit
289
+ cap >> image;
290
+ if ( !image.data ) exit (0 );
291
+ cout << image_count++ << endl;
292
+ backgroundDiff ( image, mask );
293
+ cv::imshow (" Segmentation" , mask);
294
+
295
+ // A simple visualization is to write to the red channel
296
+ //
297
+ showForgroundInRed ( argv, image);
298
+ if (key == ' a' ) {
299
+ cout << " In adjust thresholds, 'H' or 'h' == high thresh up or down; 'L' or 'l' for low thresh up or down." << endl;
300
+ cout << " esq, 'q' or 'Q' to quit " << endl;
301
+ adjustThresholds (argv, image);
302
+ cout << " Done with adjustThreshold, back to frame stepping, esq, q or Q to quit." << endl;
303
+ }
304
+ }
305
+ exit (0 );
306
+ }
307
+
308
+
0 commit comments