libvibe++ : a generic C++ library for the ViBe algorithm
ViBe.t
Go to the documentation of this file.
1 /* Copyright - Benjamin Laugraud <blaugraud@ulg.ac.be> - 2016
2  * Copyright - Marc Van Droogenbroeck <m.vandroogenbroeck@ulg.ac.be> - 2016
3  *
4  * ViBe is covered by a patent (see http://www.telecom.ulg.ac.be/research/vibe).
5  *
6  * Permission to use ViBe without payment of fee is granted for nonprofit
7  * educational and research purposes only.
8  *
9  * This work may not be copied or reproduced in whole or in part for any
10  * purpose.
11  *
12  * Copying, reproduction, or republishing for any purpose shall require a
13  * license. Please contact the authors in such cases. All the code is provided
14  * without any guarantee.
15  */
16 
17 
18 /**
19  @file ViBe.t
20  @brief Template-based C++ implementation of the ViBe algorithm.
21 
22  @author Benjamin Laungraud and Marc Van Droogenbroeck
23 
24  @date June 2016
25 
26  @details
27 
28  Full documentation is available online at:
29  http://www.telecom.ulg.ac.be/research/vibe/doc2
30 */
31 
32 #ifdef _LIB_VIBE_XX_VIBE_H_
33 
34 /* ========================================================================== *
35  * ViBeSequential<Channels, Distance> *
36  * ========================================================================== */
37 
38 template <int32_t Channels, class Distance>
39 ViBeSequential<Channels, Distance>::ViBeSequential(
40  int32_t height,
41  int32_t width,
42  const uint8_t* buffer
43 ) : Base(
44  height,
45  width,
46  Channels,
47  buffer
48  ) {}
49 
50 /******************************************************************************/
51 
52 template <int32_t Channels, class Distance>
53 void ViBeSequential<Channels, Distance>::_CRTP_segmentation(
54  const uint8_t* buffer,
55  uint8_t* segmentationMap
56 ) {
57 #ifndef NDEBUG
58  if (buffer == NULL)
59  throw; // TODO Exception
60 
61  if (segmentationMap == NULL)
62  throw; // TODO Exception
63 #endif /* NDEBUG */
64 
65  /* Even though those variables/contents are redundant with the variables of
66  * ViBeBase, they avoid additional dereference instructions.
67  */
68  static const uint32_t NUMBER_OF_HISTORY_IMAGES =
69  this->NUMBER_OF_HISTORY_IMAGES;
70 
71  static const uint8_t FOREGROUND = this->FOREGROUND;
72 
73  uint32_t pixels = this->pixels;
74  uint32_t numValues = this->numValues;
75  uint32_t matchingNumber = this->matchingNumber;
76  uint32_t matchingThreshold = this->matchingThreshold;
77 
78  uint8_t* historyImage = this->historyImage;
79  uint8_t* historyBuffer = this->historyBuffer;
80 
81  /* Initialize segmentation map. */
82  memset(segmentationMap, matchingNumber - 1, pixels);
83 
84  /* First history Image structure. */
85  for (int32_t index = pixels - 1; index >= 0; --index) {
86  if (
87  !Distance::distance(
88  buffer + (Channels * index),
89  historyImage + (Channels * index),
90  matchingThreshold
91  )
92  )
93  segmentationMap[index] = matchingNumber;
94  }
95 
96  /* Next historyImages. */
97  for (uint32_t i = 1; i < NUMBER_OF_HISTORY_IMAGES; ++i) {
98  uint8_t* pels = historyImage + i * numValues;
99 
100  for (int32_t index = pixels - 1; index >= 0; --index) {
101  if (
102  Distance::distance(
103  buffer + (Channels * index),
104  pels + (Channels * index),
105  matchingThreshold
106  )
107  )
108  --segmentationMap[index];
109  }
110  }
111 
112  /* For swapping. */
113  this->lastHistoryImageSwapped =
114  (this->lastHistoryImageSwapped + 1) % NUMBER_OF_HISTORY_IMAGES;
115 
116  uint8_t* swappingImageBuffer =
117  historyImage + (this->lastHistoryImageSwapped) * numValues;
118 
119  /* Now, we move in the buffer and leave the historyImages. */
120  int32_t numberOfTests = (this->numberOfSamples - NUMBER_OF_HISTORY_IMAGES);
121 
122  for (int32_t index = pixels - 1; index >= 0; --index) {
123  if (segmentationMap[index] > 0) {
124  /* We need to check the full border and swap values with the first or
125  * second historyImage. We still need to find a match before we can stop
126  * our search.
127  */
128  uint32_t indexHistoryBuffer = (Channels * index) * numberOfTests;
129  uint8_t currentValue[Channels];
130 
131  internals::CopyPixel<Channels>::copy(
132  &(currentValue[0]),
133  buffer + (Channels * index)
134  );
135 
136  for (int i = numberOfTests; i > 0; --i, indexHistoryBuffer += Channels) {
137  if (
138  Distance::distance(
139  &(currentValue[0]),
140  historyBuffer + indexHistoryBuffer,
141  matchingThreshold
142  )
143  ) {
144  --segmentationMap[index];
145 
146  /* Swaping: Putting found value in history image buffer. */
147  internals::SwapPixels<Channels>::swap(
148  swappingImageBuffer + (Channels * index),
149  historyBuffer + indexHistoryBuffer
150  );
151 
152  /* Exit inner loop. */
153  if (segmentationMap[index] <= 0)
154  break;
155  }
156  }
157  }
158  }
159 
160  /* Produces the output. Note that this step is application-dependent. */
161  for (
162  uint8_t* mask = segmentationMap;
163  mask < segmentationMap + pixels;
164  ++mask
165  ) {
166  if (*mask > 0)
167  *mask = FOREGROUND;
168  }
169 }
170 
171 /******************************************************************************/
172 
173 template <int32_t Channels, class Distance>
174 void ViBeSequential<Channels, Distance>::_CRTP_update(
175  const uint8_t* buffer,
176  uint8_t* updatingMask
177 ) {
178 #ifndef NDEBUG
179  if (buffer == NULL)
180  throw; // TODO Exception
181 
182  if (updatingMask == NULL)
183  throw; // TODO Exception
184 #endif /* NDEBUG */
185 
186  /* Some variables. */
187  static const uint32_t NUMBER_OF_HISTORY_IMAGES =
188  this->NUMBER_OF_HISTORY_IMAGES;
189 
190  static const uint8_t BACKGROUND = this->BACKGROUND;
191 
192  uint32_t height = this->height;
193  uint32_t width = this->width;
194  uint32_t numValues = this->numValues;
195 
196  uint8_t* historyImage = this->historyImage;
197  uint8_t* historyBuffer = this->historyBuffer;
198 
199  /* Some utility variable. */
200  int numberOfTests = (this->numberOfSamples - NUMBER_OF_HISTORY_IMAGES);
201 
202  uint32_t* jump = this->jump;
203  int32_t* neighbor = this->neighbor;
204  uint32_t* position = this->position;
205 
206  /* All the frame, except the border. */
207  uint32_t shift, indX, indY;
208  uint32_t x, y;
209 
210  for (y = 1; y < height - 1; ++y) {
211  shift = rand() % width;
212  indX = jump[shift]; // index_jump should never be zero (> 1).
213 
214  while (indX < width - 1) {
215  int index = indX + y * width;
216 
217  if (updatingMask[index] == BACKGROUND) {
218  /* In-place substitution. */
219  uint8_t currentValue[Channels];
220 
221  internals::CopyPixel<Channels>::copy(
222  &(currentValue[0]),
223  buffer + (Channels * index)
224  );
225 
226  int indexNeighbor = Channels * (index + neighbor[shift]);
227 
228  if (position[shift] < NUMBER_OF_HISTORY_IMAGES) {
229  internals::CopyPixel<Channels>::copy(
230  historyImage + (Channels * index + position[shift] * numValues),
231  &(currentValue[0])
232  );
233 
234  internals::CopyPixel<Channels>::copy(
235  historyImage + (indexNeighbor + position[shift] * numValues),
236  &(currentValue[0])
237  );
238  }
239  else {
240  int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES;
241 
242  internals::CopyPixel<Channels>::copy(
243  historyBuffer + (
244  (Channels * index) * numberOfTests + Channels * pos
245  ),
246  &(currentValue[0])
247  );
248 
249  internals::CopyPixel<Channels>::copy(
250  historyBuffer + (indexNeighbor * numberOfTests + Channels * pos),
251  &(currentValue[0])
252  );
253  }
254  }
255 
256  ++shift;
257  indX += jump[shift];
258  }
259  }
260 
261  /* First row. */
262  y = 0;
263  shift = rand() % width;
264  indX = jump[shift]; // index_jump should never be zero (> 1).
265 
266  while (indX <= width - 1) {
267  int index = indX + y * width;
268 
269  if (updatingMask[index] == BACKGROUND) {
270  if (position[shift] < NUMBER_OF_HISTORY_IMAGES) {
271  internals::CopyPixel<Channels>::copy(
272  historyImage + (Channels * index + position[shift] * numValues),
273  buffer + (Channels * index)
274  );
275  }
276  else {
277  int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES;
278 
279  internals::CopyPixel<Channels>::copy(
280  historyBuffer + ((Channels * index) * numberOfTests + Channels * pos),
281  buffer + (Channels * index)
282  );
283  }
284  }
285 
286  ++shift;
287  indX += jump[shift];
288  }
289 
290  /* Last row. */
291  y = height - 1;
292  shift = rand() % width;
293  indX = jump[shift]; // index_jump should never be zero (> 1).
294 
295  while (indX <= width - 1) {
296  int index = indX + y * width;
297 
298  if (updatingMask[index] == BACKGROUND) {
299  if (position[shift] < NUMBER_OF_HISTORY_IMAGES) {
300  internals::CopyPixel<Channels>::copy(
301  historyImage + (Channels * index + position[shift] * numValues),
302  buffer + (Channels * index)
303  );
304  }
305  else {
306  int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES;
307 
308  internals::CopyPixel<Channels>::copy(
309  historyBuffer + ((Channels * index) * numberOfTests + Channels * pos),
310  buffer + (Channels * index)
311  );
312  }
313  }
314 
315  ++shift;
316  indX += jump[shift];
317  }
318 
319  /* First column. */
320  x = 0;
321  shift = rand() % height;
322  indY = jump[shift]; // index_jump should never be zero (> 1).
323 
324  while (indY <= height - 1) {
325  int index = x + indY * width;
326 
327  if (updatingMask[index] == BACKGROUND) {
328  if (position[shift] < NUMBER_OF_HISTORY_IMAGES) {
329  internals::CopyPixel<Channels>::copy(
330  historyImage + (Channels * index + position[shift] * numValues),
331  buffer + (Channels * index)
332  );
333  }
334  else {
335  int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES;
336 
337  internals::CopyPixel<Channels>::copy(
338  historyBuffer + ((Channels * index) * numberOfTests + Channels * pos),
339  buffer + (Channels * index)
340  );
341  }
342  }
343 
344  ++shift;
345  indY += jump[shift];
346  }
347 
348  /* Last column. */
349  x = width - 1;
350  shift = rand() % height;
351  indY = jump[shift]; // index_jump should never be zero (> 1).
352 
353  while (indY <= height - 1) {
354  int index = x + indY * width;
355 
356  if (updatingMask[index] == BACKGROUND) {
357  if (position[shift] < NUMBER_OF_HISTORY_IMAGES) {
358  internals::CopyPixel<Channels>::copy(
359  historyImage + (Channels * index + position[shift] * numValues),
360  buffer + (Channels * index)
361  );
362  }
363  else {
364  int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES;
365 
366  internals::CopyPixel<Channels>::copy(
367  historyBuffer + ((Channels * index) * numberOfTests + Channels * pos),
368  buffer + (Channels * index)
369  );
370  }
371  }
372 
373  ++shift;
374  indY += jump[shift];
375  }
376 
377  /* The first pixel! */
378  if (rand() % this->updateFactor == 0) {
379  if (updatingMask[0] == 0) {
380  uint32_t position = rand() % this->numberOfSamples;
381 
382  if (position < NUMBER_OF_HISTORY_IMAGES) {
383  internals::CopyPixel<Channels>::copy(
384  historyImage + (position * numValues),
385  buffer
386  );
387  }
388  else {
389  int pos = position - NUMBER_OF_HISTORY_IMAGES;
390 
391  internals::CopyPixel<Channels>::copy(
392  historyBuffer + (Channels * pos),
393  buffer
394  );
395  }
396  }
397  }
398 }
399 
400 #endif /* _LIB_VIBE_XX_VIBE_H_ */

License/Copyright

This code is copyrighted by the University of Liège, Belgium. 
It is only shared for research purposes. Please do not distribute it. 
B. Laugraud and M. Van Droogenbroeck, May 2016.