spyglass 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -1
- data/examples/background_subtractor.rb +1 -1
- data/ext/spyglass/background_subtractor.cc +84 -29
- data/ext/spyglass/background_subtractor.h +11 -4
- data/ext/spyglass/bgslib_bgs.h +67 -0
- data/ext/spyglass/bgslib_bgs_params.h +60 -0
- data/ext/spyglass/bgslib_dp_prati_mediod_bgs.cc +79 -0
- data/ext/spyglass/bgslib_dp_prati_mediod_bgs.h +53 -0
- data/ext/spyglass/bgslib_image.cc +77 -0
- data/ext/spyglass/bgslib_image.h +365 -0
- data/ext/spyglass/bgslib_prati_mediod_bgs.cc +276 -0
- data/ext/spyglass/bgslib_prati_mediod_bgs.h +142 -0
- data/ext/spyglass/contour.cc +0 -2
- data/ext/spyglass/extconf.rb +1 -0
- data/ext/spyglass/prelude.h +1 -1
- data/ext/spyglass/spyglass.cc +1 -1
- data/lib/spyglass/version.rb +2 -2
- data/spec/spyglass/background_subtractor_spec.rb +23 -18
- metadata +10 -2
@@ -0,0 +1,77 @@
|
|
1
|
+
/*
|
2
|
+
This file is part of BGSLibrary.
|
3
|
+
|
4
|
+
BGSLibrary is free software: you can redistribute it and/or modify
|
5
|
+
it under the terms of the GNU General Public License as published by
|
6
|
+
the Free Software Foundation, either version 3 of the License, or
|
7
|
+
(at your option) any later version.
|
8
|
+
|
9
|
+
BGSLibrary is distributed in the hope that it will be useful,
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
GNU General Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License
|
15
|
+
along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
*/
|
17
|
+
/****************************************************************************
|
18
|
+
*
|
19
|
+
* Image.hpp
|
20
|
+
*
|
21
|
+
* Purpose: C++ wrapper for OpenCV IplImage which supports simple and
|
22
|
+
* efficient access to the image data
|
23
|
+
*
|
24
|
+
* Author: Donovan Parks, September 2007
|
25
|
+
*
|
26
|
+
* Based on code from:
|
27
|
+
* http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.hpptml
|
28
|
+
******************************************************************************/
|
29
|
+
|
30
|
+
#include "bgslib_image.h"
|
31
|
+
|
32
|
+
ImageBase::~ImageBase()
|
33
|
+
{
|
34
|
+
if(imgp != NULL && m_bReleaseMemory)
|
35
|
+
cvReleaseImage(&imgp);
|
36
|
+
imgp = NULL;
|
37
|
+
}
|
38
|
+
|
39
|
+
void DensityFilter(BwImage& image, BwImage& filtered, int minDensity, unsigned char fgValue)
|
40
|
+
{
|
41
|
+
for(int r = 1; r < image.Ptr()->height-1; ++r)
|
42
|
+
{
|
43
|
+
for(int c = 1; c < image.Ptr()->width-1; ++c)
|
44
|
+
{
|
45
|
+
int count = 0;
|
46
|
+
if(image(r,c) == fgValue)
|
47
|
+
{
|
48
|
+
if(image(r-1,c-1) == fgValue)
|
49
|
+
count++;
|
50
|
+
if(image(r-1,c) == fgValue)
|
51
|
+
count++;
|
52
|
+
if(image(r-1,c+1) == fgValue)
|
53
|
+
count++;
|
54
|
+
if(image(r,c-1) == fgValue)
|
55
|
+
count++;
|
56
|
+
if(image(r,c+1) == fgValue)
|
57
|
+
count++;
|
58
|
+
if(image(r+1,c-1) == fgValue)
|
59
|
+
count++;
|
60
|
+
if(image(r+1,c) == fgValue)
|
61
|
+
count++;
|
62
|
+
if(image(r+1,c+1) == fgValue)
|
63
|
+
count++;
|
64
|
+
|
65
|
+
if(count < minDensity)
|
66
|
+
filtered(r,c) = 0;
|
67
|
+
else
|
68
|
+
filtered(r,c) = fgValue;
|
69
|
+
}
|
70
|
+
else
|
71
|
+
{
|
72
|
+
filtered(r,c) = 0;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
@@ -0,0 +1,365 @@
|
|
1
|
+
/*
|
2
|
+
This file is part of BGSLibrary.
|
3
|
+
|
4
|
+
BGSLibrary is free software: you can redistribute it and/or modify
|
5
|
+
it under the terms of the GNU General Public License as published by
|
6
|
+
the Free Software Foundation, either version 3 of the License, or
|
7
|
+
(at your option) any later version.
|
8
|
+
|
9
|
+
BGSLibrary is distributed in the hope that it will be useful,
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
GNU General Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License
|
15
|
+
along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
*/
|
17
|
+
/****************************************************************************
|
18
|
+
*
|
19
|
+
* Image.h
|
20
|
+
*
|
21
|
+
* Purpose: C++ wrapper for OpenCV IplImage which supports simple and
|
22
|
+
* efficient access to the image data
|
23
|
+
*
|
24
|
+
* Author: Donovan Parks, September 2007
|
25
|
+
*
|
26
|
+
* Based on code from:
|
27
|
+
* http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html
|
28
|
+
******************************************************************************/
|
29
|
+
|
30
|
+
#ifndef _IMAGE_H_
|
31
|
+
#define _IMAGE_H_
|
32
|
+
|
33
|
+
#include <opencv/cv.h>
|
34
|
+
#include <opencv/cxcore.h>
|
35
|
+
|
36
|
+
// --- Image Iterator ---------------------------------------------------------
|
37
|
+
|
38
|
+
template <class T>
|
39
|
+
class ImageIterator
|
40
|
+
{
|
41
|
+
public:
|
42
|
+
ImageIterator(IplImage* image, int x=0, int y=0, int dx= 0, int dy=0) :
|
43
|
+
i(x), j(y), i0(0)
|
44
|
+
{
|
45
|
+
data = reinterpret_cast<T*>(image->imageData);
|
46
|
+
step = image->widthStep / sizeof(T);
|
47
|
+
|
48
|
+
nl= image->height;
|
49
|
+
if ((y+dy)>0 && (y+dy) < nl)
|
50
|
+
nl= y+dy;
|
51
|
+
|
52
|
+
if (y<0)
|
53
|
+
j=0;
|
54
|
+
|
55
|
+
data += step*j;
|
56
|
+
|
57
|
+
nc = image->width;
|
58
|
+
if ((x+dx) > 0 && (x+dx) < nc)
|
59
|
+
nc = x+dx;
|
60
|
+
|
61
|
+
nc *= image->nChannels;
|
62
|
+
if (x>0)
|
63
|
+
i0 = x*image->nChannels;
|
64
|
+
i = i0;
|
65
|
+
|
66
|
+
nch = image->nChannels;
|
67
|
+
}
|
68
|
+
|
69
|
+
|
70
|
+
/* has next ? */
|
71
|
+
bool operator!() const { return j < nl; }
|
72
|
+
|
73
|
+
/* next pixel */
|
74
|
+
ImageIterator& operator++()
|
75
|
+
{
|
76
|
+
i++;
|
77
|
+
if (i >= nc)
|
78
|
+
{
|
79
|
+
i=i0;
|
80
|
+
j++;
|
81
|
+
data += step;
|
82
|
+
}
|
83
|
+
return *this;
|
84
|
+
}
|
85
|
+
|
86
|
+
ImageIterator& operator+=(int s)
|
87
|
+
{
|
88
|
+
i+=s;
|
89
|
+
if (i >= nc)
|
90
|
+
{
|
91
|
+
i=i0;
|
92
|
+
j++;
|
93
|
+
data += step;
|
94
|
+
}
|
95
|
+
return *this;
|
96
|
+
}
|
97
|
+
|
98
|
+
/* pixel access */
|
99
|
+
T& operator*() { return data[i]; }
|
100
|
+
|
101
|
+
const T operator*() const { return data[i]; }
|
102
|
+
|
103
|
+
const T neighbor(int dx, int dy) const
|
104
|
+
{
|
105
|
+
return *(data+dy*step+i+dx);
|
106
|
+
}
|
107
|
+
|
108
|
+
T* operator&() const { return data+i; }
|
109
|
+
|
110
|
+
/* current pixel coordinates */
|
111
|
+
int column() const { return i/nch; }
|
112
|
+
int line() const { return j; }
|
113
|
+
|
114
|
+
private:
|
115
|
+
int i, i0,j;
|
116
|
+
T* data;
|
117
|
+
int step;
|
118
|
+
int nl, nc;
|
119
|
+
int nch;
|
120
|
+
};
|
121
|
+
|
122
|
+
// --- Constants --------------------------------------------------------------
|
123
|
+
|
124
|
+
const unsigned char NUM_CHANNELS = 3;
|
125
|
+
|
126
|
+
// --- Pixel Types ------------------------------------------------------------
|
127
|
+
|
128
|
+
class RgbPixel
|
129
|
+
{
|
130
|
+
public:
|
131
|
+
RgbPixel() {;}
|
132
|
+
RgbPixel(unsigned char _r, unsigned char _g, unsigned char _b)
|
133
|
+
{
|
134
|
+
ch[0] = _r; ch[1] = _g; ch[2] = _b;
|
135
|
+
}
|
136
|
+
|
137
|
+
RgbPixel& operator=(const RgbPixel& rhs)
|
138
|
+
{
|
139
|
+
ch[0] = rhs.ch[0]; ch[1] = rhs.ch[1]; ch[2] = rhs.ch[2];
|
140
|
+
return *this;
|
141
|
+
}
|
142
|
+
|
143
|
+
inline unsigned char& operator()(const int _ch)
|
144
|
+
{
|
145
|
+
return ch[_ch];
|
146
|
+
}
|
147
|
+
|
148
|
+
inline unsigned char operator()(const int _ch) const
|
149
|
+
{
|
150
|
+
return ch[_ch];
|
151
|
+
}
|
152
|
+
|
153
|
+
unsigned char ch[3];
|
154
|
+
};
|
155
|
+
|
156
|
+
class RgbPixelFloat
|
157
|
+
{
|
158
|
+
public:
|
159
|
+
RgbPixelFloat() {;}
|
160
|
+
RgbPixelFloat(float _r, float _g, float _b)
|
161
|
+
{
|
162
|
+
ch[0] = _r; ch[1] = _g; ch[2] = _b;
|
163
|
+
}
|
164
|
+
|
165
|
+
RgbPixelFloat& operator=(const RgbPixelFloat& rhs)
|
166
|
+
{
|
167
|
+
ch[0] = rhs.ch[0]; ch[1] = rhs.ch[1]; ch[2] = rhs.ch[2];
|
168
|
+
return *this;
|
169
|
+
}
|
170
|
+
|
171
|
+
inline float& operator()(const int _ch)
|
172
|
+
{
|
173
|
+
return ch[_ch];
|
174
|
+
}
|
175
|
+
|
176
|
+
inline float operator()(const int _ch) const
|
177
|
+
{
|
178
|
+
return ch[_ch];
|
179
|
+
}
|
180
|
+
|
181
|
+
float ch[3];
|
182
|
+
};
|
183
|
+
|
184
|
+
// --- Image Types ------------------------------------------------------------
|
185
|
+
|
186
|
+
class ImageBase
|
187
|
+
{
|
188
|
+
public:
|
189
|
+
ImageBase(IplImage* img = NULL) { imgp = img; m_bReleaseMemory = true; }
|
190
|
+
~ImageBase();
|
191
|
+
|
192
|
+
void ReleaseMemory(bool b) { m_bReleaseMemory = b; }
|
193
|
+
|
194
|
+
IplImage* Ptr() { return imgp; }
|
195
|
+
const IplImage* Ptr() const { return imgp; }
|
196
|
+
|
197
|
+
void ReleaseImage()
|
198
|
+
{
|
199
|
+
cvReleaseImage(&imgp);
|
200
|
+
}
|
201
|
+
|
202
|
+
void operator=(IplImage* img)
|
203
|
+
{
|
204
|
+
imgp = img;
|
205
|
+
}
|
206
|
+
|
207
|
+
// copy-constructor
|
208
|
+
ImageBase(const ImageBase& rhs)
|
209
|
+
{
|
210
|
+
// it is very inefficent if this copy-constructor is called
|
211
|
+
assert(false);
|
212
|
+
}
|
213
|
+
|
214
|
+
// assignment operator
|
215
|
+
ImageBase& operator=(const ImageBase& rhs)
|
216
|
+
{
|
217
|
+
// it is very inefficent if operator= is called
|
218
|
+
assert(false);
|
219
|
+
|
220
|
+
return *this;
|
221
|
+
}
|
222
|
+
|
223
|
+
virtual void Clear() = 0;
|
224
|
+
|
225
|
+
protected:
|
226
|
+
IplImage* imgp;
|
227
|
+
bool m_bReleaseMemory;
|
228
|
+
};
|
229
|
+
|
230
|
+
class RgbImage : public ImageBase
|
231
|
+
{
|
232
|
+
public:
|
233
|
+
RgbImage(IplImage* img = NULL) : ImageBase(img) { ; }
|
234
|
+
|
235
|
+
virtual void Clear()
|
236
|
+
{
|
237
|
+
cvZero(imgp);
|
238
|
+
}
|
239
|
+
|
240
|
+
void operator=(IplImage* img)
|
241
|
+
{
|
242
|
+
imgp = img;
|
243
|
+
}
|
244
|
+
|
245
|
+
// channel-level access using image(row, col, channel)
|
246
|
+
inline unsigned char& operator()(const int r, const int c, const int ch)
|
247
|
+
{
|
248
|
+
return (unsigned char &)imgp->imageData[r*imgp->widthStep+c*imgp->nChannels+ch];
|
249
|
+
}
|
250
|
+
|
251
|
+
inline const unsigned char& operator()(const int r, const int c, const int ch) const
|
252
|
+
{
|
253
|
+
return (unsigned char &)imgp->imageData[r*imgp->widthStep+c*imgp->nChannels+ch];
|
254
|
+
}
|
255
|
+
|
256
|
+
// RGB pixel-level access using image(row, col)
|
257
|
+
inline RgbPixel& operator()(const int r, const int c)
|
258
|
+
{
|
259
|
+
return (RgbPixel &)imgp->imageData[r*imgp->widthStep+c*imgp->nChannels];
|
260
|
+
}
|
261
|
+
|
262
|
+
inline const RgbPixel& operator()(const int r, const int c) const
|
263
|
+
{
|
264
|
+
return (RgbPixel &)imgp->imageData[r*imgp->widthStep+c*imgp->nChannels];
|
265
|
+
}
|
266
|
+
};
|
267
|
+
|
268
|
+
class RgbImageFloat : public ImageBase
|
269
|
+
{
|
270
|
+
public:
|
271
|
+
RgbImageFloat(IplImage* img = NULL) : ImageBase(img) { ; }
|
272
|
+
|
273
|
+
virtual void Clear()
|
274
|
+
{
|
275
|
+
cvZero(imgp);
|
276
|
+
}
|
277
|
+
|
278
|
+
void operator=(IplImage* img)
|
279
|
+
{
|
280
|
+
imgp = img;
|
281
|
+
}
|
282
|
+
|
283
|
+
// channel-level access using image(row, col, channel)
|
284
|
+
inline float& operator()(const int r, const int c, const int ch)
|
285
|
+
{
|
286
|
+
return (float &)imgp->imageData[r*imgp->widthStep+(c*imgp->nChannels+ch)*sizeof(float)];
|
287
|
+
}
|
288
|
+
|
289
|
+
inline float operator()(const int r, const int c, const int ch) const
|
290
|
+
{
|
291
|
+
return (float)imgp->imageData[r*imgp->widthStep+(c*imgp->nChannels+ch)*sizeof(float)];
|
292
|
+
}
|
293
|
+
|
294
|
+
// RGB pixel-level access using image(row, col)
|
295
|
+
inline RgbPixelFloat& operator()(const int r, const int c)
|
296
|
+
{
|
297
|
+
return (RgbPixelFloat &)imgp->imageData[r*imgp->widthStep+c*imgp->nChannels*sizeof(float)];
|
298
|
+
}
|
299
|
+
|
300
|
+
inline const RgbPixelFloat& operator()(const int r, const int c) const
|
301
|
+
{
|
302
|
+
return (RgbPixelFloat &)imgp->imageData[r*imgp->widthStep+c*imgp->nChannels*sizeof(float)];
|
303
|
+
}
|
304
|
+
};
|
305
|
+
|
306
|
+
class BwImage : public ImageBase
|
307
|
+
{
|
308
|
+
public:
|
309
|
+
BwImage(IplImage* img = NULL) : ImageBase(img) { ; }
|
310
|
+
|
311
|
+
virtual void Clear()
|
312
|
+
{
|
313
|
+
cvZero(imgp);
|
314
|
+
}
|
315
|
+
|
316
|
+
void operator=(IplImage* img)
|
317
|
+
{
|
318
|
+
imgp = img;
|
319
|
+
}
|
320
|
+
|
321
|
+
// pixel-level access using image(row, col)
|
322
|
+
inline unsigned char& operator()(const int r, const int c)
|
323
|
+
{
|
324
|
+
return (unsigned char &)imgp->imageData[r*imgp->widthStep+c];
|
325
|
+
}
|
326
|
+
|
327
|
+
inline unsigned char operator()(const int r, const int c) const
|
328
|
+
{
|
329
|
+
return (unsigned char)imgp->imageData[r*imgp->widthStep+c];
|
330
|
+
}
|
331
|
+
};
|
332
|
+
|
333
|
+
class BwImageFloat : public ImageBase
|
334
|
+
{
|
335
|
+
public:
|
336
|
+
BwImageFloat(IplImage* img = NULL) : ImageBase(img) { ; }
|
337
|
+
|
338
|
+
virtual void Clear()
|
339
|
+
{
|
340
|
+
cvZero(imgp);
|
341
|
+
}
|
342
|
+
|
343
|
+
void operator=(IplImage* img)
|
344
|
+
{
|
345
|
+
imgp = img;
|
346
|
+
}
|
347
|
+
|
348
|
+
// pixel-level access using image(row, col)
|
349
|
+
inline float& operator()(const int r, const int c)
|
350
|
+
{
|
351
|
+
return (float &)imgp->imageData[r*imgp->widthStep+c*sizeof(float)];
|
352
|
+
}
|
353
|
+
|
354
|
+
inline float operator()(const int r, const int c) const
|
355
|
+
{
|
356
|
+
return (float)imgp->imageData[r*imgp->widthStep+c*sizeof(float)];
|
357
|
+
}
|
358
|
+
};
|
359
|
+
|
360
|
+
// --- Image Functions --------------------------------------------------------
|
361
|
+
|
362
|
+
void DensityFilter(BwImage& image, BwImage& filtered, int minDensity, unsigned char fgValue);
|
363
|
+
|
364
|
+
#endif
|
365
|
+
|
@@ -0,0 +1,276 @@
|
|
1
|
+
/*
|
2
|
+
This file is part of BGSLibrary.
|
3
|
+
|
4
|
+
BGSLibrary is free software: you can redistribute it and/or modify
|
5
|
+
it under the terms of the GNU General Public License as published by
|
6
|
+
the Free Software Foundation, either version 3 of the License, or
|
7
|
+
(at your option) any later version.
|
8
|
+
|
9
|
+
BGSLibrary is distributed in the hope that it will be useful,
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
GNU General Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License
|
15
|
+
along with BGSLibrary. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
*/
|
17
|
+
/****************************************************************************
|
18
|
+
*
|
19
|
+
* PratiMediodBGS.h
|
20
|
+
*
|
21
|
+
* Purpose: Implementation of the temporal median background
|
22
|
+
* subtraction algorithm described in:
|
23
|
+
*
|
24
|
+
* [1] "Detecting Moving Objects, Shosts, and Shadows in Video Stream"
|
25
|
+
* by R. Cucchiara et al (2003)
|
26
|
+
*
|
27
|
+
* [2] "Reliable Background Suppression for Complex Scenes"
|
28
|
+
* by S. Calderara et al (2006)
|
29
|
+
*
|
30
|
+
* Author: Donovan Parks, September 2007
|
31
|
+
*
|
32
|
+
* Please note that this is not an implementation of the complete system
|
33
|
+
* given in the above papers. It simply implements the temporal media background
|
34
|
+
* subtraction algorithm.
|
35
|
+
******************************************************************************/
|
36
|
+
|
37
|
+
#include "bgslib_prati_mediod_bgs.h"
|
38
|
+
|
39
|
+
using namespace Algorithms::BackgroundSubtraction;
|
40
|
+
|
41
|
+
PratiMediodBGS::PratiMediodBGS()
|
42
|
+
{
|
43
|
+
m_median_buffer = NULL;
|
44
|
+
}
|
45
|
+
|
46
|
+
PratiMediodBGS::~PratiMediodBGS()
|
47
|
+
{
|
48
|
+
if(m_median_buffer != NULL)
|
49
|
+
delete[] m_median_buffer;
|
50
|
+
}
|
51
|
+
|
52
|
+
void PratiMediodBGS::Initalize(const BgsParams& param)
|
53
|
+
{
|
54
|
+
m_params = (PratiParams&)param;
|
55
|
+
|
56
|
+
m_mask_low_threshold = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 1);
|
57
|
+
m_mask_high_threshold = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 1);
|
58
|
+
|
59
|
+
m_background = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3);
|
60
|
+
|
61
|
+
m_median_buffer = new MEDIAN_BUFFER[m_params.Size()];
|
62
|
+
}
|
63
|
+
|
64
|
+
void PratiMediodBGS::InitModel(const RgbImage& data)
|
65
|
+
{
|
66
|
+
// there is no need to initialize the mode since it needs a buffer of frames
|
67
|
+
// before it can performing background subtraction
|
68
|
+
}
|
69
|
+
|
70
|
+
void PratiMediodBGS::Update(int frame_num, const RgbImage& data, const BwImage& update_mask)
|
71
|
+
{
|
72
|
+
// update the image buffer with the new frame and calculate new median values
|
73
|
+
if(frame_num % m_params.SamplingRate() == 0)
|
74
|
+
{
|
75
|
+
if(m_median_buffer[0].dist.size() == m_params.HistorySize())
|
76
|
+
{
|
77
|
+
// subtract distance to sample being removed from all distances
|
78
|
+
for(unsigned int r = 0; r < m_params.Height(); ++r)
|
79
|
+
{
|
80
|
+
for(unsigned int c = 0; c < m_params.Width(); ++c)
|
81
|
+
{
|
82
|
+
int i = r*m_params.Width()+c;
|
83
|
+
|
84
|
+
if(update_mask(r,c) == BACKGROUND)
|
85
|
+
{
|
86
|
+
int oldPos = m_median_buffer[i].pos;
|
87
|
+
for(unsigned int s = 0; s < m_median_buffer[i].pixels.size(); ++s)
|
88
|
+
{
|
89
|
+
int maxDist = 0;
|
90
|
+
for(int ch = 0; ch < NUM_CHANNELS; ++ch)
|
91
|
+
{
|
92
|
+
int tempDist = abs(m_median_buffer[i].pixels.at(oldPos)(ch)
|
93
|
+
- m_median_buffer[i].pixels.at(s)(ch));
|
94
|
+
if(tempDist > maxDist)
|
95
|
+
maxDist = tempDist;
|
96
|
+
}
|
97
|
+
|
98
|
+
m_median_buffer[i].dist.at(s) -= maxDist;
|
99
|
+
}
|
100
|
+
|
101
|
+
int dist;
|
102
|
+
UpdateMediod(r, c, data, dist);
|
103
|
+
m_median_buffer[i].dist.at(oldPos) = dist;
|
104
|
+
m_median_buffer[i].pixels.at(oldPos) = data(r,c);
|
105
|
+
m_median_buffer[i].pos++;
|
106
|
+
if(m_median_buffer[i].pos >= m_params.HistorySize())
|
107
|
+
m_median_buffer[i].pos = 0;
|
108
|
+
}
|
109
|
+
}
|
110
|
+
}
|
111
|
+
}
|
112
|
+
else
|
113
|
+
{
|
114
|
+
// calculate sum of L-inf distances for new point and
|
115
|
+
// add distance from each sample point to this point to their L-inf sum
|
116
|
+
int dist;
|
117
|
+
for(unsigned int r = 0; r < m_params.Height(); ++r)
|
118
|
+
{
|
119
|
+
for(unsigned int c = 0; c < m_params.Width(); ++c)
|
120
|
+
{
|
121
|
+
int index = r*m_params.Width()+c;
|
122
|
+
UpdateMediod(r, c, data, dist);
|
123
|
+
m_median_buffer[index].dist.push_back(dist);
|
124
|
+
m_median_buffer[index].pos = 0;
|
125
|
+
m_median_buffer[index].pixels.push_back(data(r,c));
|
126
|
+
}
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
void PratiMediodBGS::UpdateMediod(int r, int c, const RgbImage& new_frame, int& dist)
|
133
|
+
{
|
134
|
+
// calculate sum of L-inf distances for new point and
|
135
|
+
// add distance from each sample point to this point to their L-inf sum
|
136
|
+
unsigned int i = (r*m_params.Width()+c);
|
137
|
+
|
138
|
+
m_median_buffer[i].medianDist = INT_MAX;
|
139
|
+
|
140
|
+
int L_inf_dist = 0;
|
141
|
+
for(unsigned int s = 0; s < m_median_buffer[i].dist.size(); ++s)
|
142
|
+
{
|
143
|
+
int maxDist = 0;
|
144
|
+
for(int ch = 0; ch < NUM_CHANNELS; ++ch)
|
145
|
+
{
|
146
|
+
int tempDist = abs(m_median_buffer[i].pixels.at(s)(ch) - new_frame(r,c,ch));
|
147
|
+
if(tempDist > maxDist)
|
148
|
+
maxDist = tempDist;
|
149
|
+
}
|
150
|
+
|
151
|
+
// check if point from this frame in the image buffer is the median
|
152
|
+
m_median_buffer[i].dist.at(s) += maxDist;
|
153
|
+
if(m_median_buffer[i].dist.at(s) < m_median_buffer[i].medianDist)
|
154
|
+
{
|
155
|
+
m_median_buffer[i].medianDist = m_median_buffer[i].dist.at(s);
|
156
|
+
m_median_buffer[i].median = m_median_buffer[i].pixels.at(s);
|
157
|
+
}
|
158
|
+
|
159
|
+
L_inf_dist += maxDist;
|
160
|
+
}
|
161
|
+
|
162
|
+
dist = L_inf_dist;
|
163
|
+
|
164
|
+
// check if the new point is the median
|
165
|
+
if(L_inf_dist < m_median_buffer[i].medianDist)
|
166
|
+
{
|
167
|
+
m_median_buffer[i].medianDist = L_inf_dist;
|
168
|
+
m_median_buffer[i].median = new_frame(r,c);
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
void PratiMediodBGS::Combine(const BwImage& low_mask, const BwImage& high_mask, BwImage& output)
|
173
|
+
{
|
174
|
+
for(unsigned int r = 0; r < m_params.Height(); ++r)
|
175
|
+
{
|
176
|
+
for(unsigned int c = 0; c < m_params.Width(); ++c)
|
177
|
+
{
|
178
|
+
output(r,c) = BACKGROUND;
|
179
|
+
|
180
|
+
if(r == 0 || c == 0 || r == m_params.Height()-1 || c == m_params.Width()-1)
|
181
|
+
continue;
|
182
|
+
|
183
|
+
if(high_mask(r,c) == FOREGROUND)
|
184
|
+
{
|
185
|
+
output(r,c) = FOREGROUND;
|
186
|
+
}
|
187
|
+
else if(low_mask(r,c) == FOREGROUND)
|
188
|
+
{
|
189
|
+
// consider the pixel to be a F/G pixel if it is 8-connected to
|
190
|
+
// a F/G pixel in the high mask
|
191
|
+
// check if there is an 8-connected foreground pixel
|
192
|
+
if(high_mask(r-1,c-1))
|
193
|
+
output(r,c) = FOREGROUND;
|
194
|
+
else if(high_mask(r-1,c))
|
195
|
+
output(r,c) = FOREGROUND;
|
196
|
+
else if(high_mask(r-1,c+1))
|
197
|
+
output(r,c) = FOREGROUND;
|
198
|
+
else if(high_mask(r,c-1))
|
199
|
+
output(r,c) = FOREGROUND;
|
200
|
+
else if(high_mask(r,c+1))
|
201
|
+
output(r,c) = FOREGROUND;
|
202
|
+
else if(high_mask(r+1,c-1))
|
203
|
+
output(r,c) = FOREGROUND;
|
204
|
+
else if(high_mask(r+1,c))
|
205
|
+
output(r,c) = FOREGROUND;
|
206
|
+
else if(high_mask(r+1,c+1))
|
207
|
+
output(r,c) = FOREGROUND;
|
208
|
+
}
|
209
|
+
}
|
210
|
+
}
|
211
|
+
}
|
212
|
+
|
213
|
+
void PratiMediodBGS::CalculateMasks(int r, int c, const RgbPixel& pixel)
|
214
|
+
{
|
215
|
+
int pos = r*m_params.Width()+c;
|
216
|
+
|
217
|
+
// calculate l-inf distance between current value and median value
|
218
|
+
unsigned char dist = 0;
|
219
|
+
for(int ch = 0; ch < NUM_CHANNELS; ++ch)
|
220
|
+
{
|
221
|
+
int tempDist = abs(pixel(ch) - m_median_buffer[pos].median(ch));
|
222
|
+
if(tempDist > dist)
|
223
|
+
dist = tempDist;
|
224
|
+
}
|
225
|
+
m_background(r,c) = m_median_buffer[pos].median;
|
226
|
+
|
227
|
+
// check if pixel is a B/G or F/G pixel according to the low threshold B/G model
|
228
|
+
m_mask_low_threshold(r,c) = BACKGROUND;
|
229
|
+
if(dist > m_params.LowThreshold())
|
230
|
+
{
|
231
|
+
m_mask_low_threshold(r,c) = FOREGROUND;
|
232
|
+
}
|
233
|
+
|
234
|
+
// check if pixel is a B/G or F/G pixel according to the high threshold B/G model
|
235
|
+
m_mask_high_threshold(r,c)= BACKGROUND;
|
236
|
+
if(dist > m_params.HighThreshold())
|
237
|
+
{
|
238
|
+
m_mask_high_threshold(r,c) = FOREGROUND;
|
239
|
+
}
|
240
|
+
}
|
241
|
+
|
242
|
+
///////////////////////////////////////////////////////////////////////////////
|
243
|
+
//Input:
|
244
|
+
// data - a pointer to the data of a RGB image of the same size
|
245
|
+
//Output:
|
246
|
+
// output - a pointer to the data of a gray value image of the same size
|
247
|
+
// values: 255-foreground, 0-background
|
248
|
+
///////////////////////////////////////////////////////////////////////////////
|
249
|
+
void PratiMediodBGS::Subtract(int frame_num, const RgbImage& data,
|
250
|
+
BwImage& low_threshold_mark, BwImage& high_threshold_mark)
|
251
|
+
{
|
252
|
+
if(frame_num < m_params.HistorySize())
|
253
|
+
{
|
254
|
+
low_threshold_mark.Clear();
|
255
|
+
high_threshold_mark.Clear();
|
256
|
+
return;
|
257
|
+
}
|
258
|
+
|
259
|
+
// update each pixel of the image
|
260
|
+
for(unsigned int r = 0; r < m_params.Height(); ++r)
|
261
|
+
{
|
262
|
+
for(unsigned int c = 0; c < m_params.Width(); ++c)
|
263
|
+
{
|
264
|
+
// need at least one frame of data before we can start calculating the masks
|
265
|
+
CalculateMasks(r, c, data(r,c));
|
266
|
+
}
|
267
|
+
}
|
268
|
+
|
269
|
+
// combine low and high threshold masks
|
270
|
+
Combine(m_mask_low_threshold, m_mask_high_threshold, low_threshold_mark);
|
271
|
+
Combine(m_mask_low_threshold, m_mask_high_threshold, high_threshold_mark);
|
272
|
+
}
|
273
|
+
|
274
|
+
|
275
|
+
|
276
|
+
|