segmentation: Fix integer underflow check
[platform/upstream/gstreamer.git] / ext / opencv / gstsegmentation.cpp
1 /*
2  * GStreamer
3  * Copyright (C) 2013 Miguel Casas-Sanchez <miguelecasassanchez@gmail.com>
4  * Except: Parts of code inside the preprocessor define CODE_FROM_OREILLY_BOOK, 
5  *  which are downloaded from O'Reilly website 
6  *  [http://examples.oreilly.com/9780596516130/]
7  *  and adapted. Its license reads:
8  *  "Oct. 3, 2008
9  *   Right to use this code in any way you want without warrenty, support or 
10  *   any guarentee of it working. "
11  *
12  * 
13  * Permission is hereby granted, free of charge, to any person obtaining a
14  * copy of this software and associated documentation files (the "Software"),
15  * to deal in the Software without restriction, including without limitation
16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17  * and/or sell copies of the Software, and to permit persons to whom the
18  * Software is furnished to do so, subject to the following conditions:
19  *
20  * The above copyright notice and this permission notice shall be included in
21  * all copies or substantial portions of the Software.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29  * DEALINGS IN THE SOFTWARE.
30  *
31  * Alternatively, the contents of this file may be used under the
32  * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
33  * which case the following provisions apply instead of the ones
34  * mentioned above:
35  *
36  * This library is free software; you can redistribute it and/or
37  * modify it under the terms of the GNU Library General Public
38  * License as published by the Free Software Foundation; either
39  * version 2 of the License, or (at your option) any later version.
40  *
41  * This library is distributed in the hope that it will be useful,
42  * but WITHOUT ANY WARRANTY; without even the implied warranty of
43  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
44  * Library General Public License for more details.
45  *
46  * You should have received a copy of the GNU Library General Public
47  * License along with this library; if not, write to the
48  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
49  * Boston, MA 02110-1301, USA.
50  */
51 #define CODE_FROM_OREILLY_BOOK
52
53 /**
54  * SECTION:element-segmentation
55  *
56  * This element creates and updates a fg/bg model using one of several approaches.
57  * The one called "codebook" refers to the codebook approach following the opencv
58  * O'Reilly book [1] implementation of the algorithm described in K. Kim, 
59  * T. H. Chalidabhongse, D. Harwood and L. Davis [2]. BackgroundSubtractorMOG [3], 
60  * or MOG for shorts, refers to a Gaussian Mixture-based Background/Foreground 
61  * Segmentation Algorithm. OpenCV MOG implements the algorithm described in [4].
62  * BackgroundSubtractorMOG2 [5], refers to another Gaussian Mixture-based 
63  * Background/Foreground segmentation algorithm. OpenCV MOG2 implements the 
64  * algorithm described in [6] and [7].
65  *
66  * [1] Learning OpenCV: Computer Vision with the OpenCV Library by Gary Bradski 
67  * and Adrian Kaehler, Published by O'Reilly Media, October 3, 2008
68  * [2] "Real-time Foreground-Background Segmentation using Codebook Model", 
69  * Real-time Imaging, Volume 11, Issue 3, Pages 167-256, June 2005.
70  * [3] http://opencv.itseez.com/modules/video/doc/motion_analysis_and_object_tracking.html#backgroundsubtractormog
71  * [4] P. KadewTraKuPong and R. Bowden, "An improved adaptive background 
72  * mixture model for real-time tracking with shadow detection", Proc. 2nd 
73  * European Workshop on Advanced Video-Based Surveillance Systems, 2001
74  * [5] http://opencv.itseez.com/modules/video/doc/motion_analysis_and_object_tracking.html#backgroundsubtractormog2
75  * [6] Z.Zivkovic, "Improved adaptive Gausian mixture model for background 
76  * subtraction", International Conference Pattern Recognition, UK, August, 2004.
77  * [7] Z.Zivkovic, F. van der Heijden, "Efficient Adaptive Density Estimation 
78  * per Image Pixel for the Task of Background Subtraction", Pattern Recognition 
79  * Letters, vol. 27, no. 7, pages 773-780, 2006.
80  *
81  * <refsect2>
82  * <title>Example launch line</title>
83  * |[
84  * gst-launch-1.0  v4l2src device=/dev/video0 ! videoconvert ! video/x-raw,width=320,height=240 ! videoconvert ! segmentation test-mode=true method=2 ! videoconvert ! ximagesink
85  * ]|
86  * </refsect2>
87  */
88
89 #ifdef HAVE_CONFIG_H
90 #include <config.h>
91 #endif
92
93 #include <gst/gst.h>
94
95 #include "gstsegmentation.h"
96 #include <opencv2/video/background_segm.hpp>
97
98 GST_DEBUG_CATEGORY_STATIC (gst_segmentation_debug);
99 #define GST_CAT_DEFAULT gst_segmentation_debug
100
101 /* Filter signals and args */
102 enum
103 {
104   /* FILL ME */
105   LAST_SIGNAL
106 };
107
108 enum
109 {
110   PROP_0,
111   PROP_TEST_MODE,
112   PROP_METHOD,
113   PROP_LEARNING_RATE
114 };
115 typedef enum
116 {
117   METHOD_BOOK,
118   METHOD_MOG,
119   METHOD_MOG2
120 } GstSegmentationMethod;
121
122 #define DEFAULT_TEST_MODE FALSE
123 #define DEFAULT_METHOD  METHOD_MOG2
124 #define DEFAULT_LEARNING_RATE  0.01
125
126 #define GST_TYPE_SEGMENTATION_METHOD (gst_segmentation_method_get_type ())
127 static GType
128 gst_segmentation_method_get_type (void)
129 {
130   static GType etype = 0;
131   if (etype == 0) {
132     static const GEnumValue values[] = {
133       {METHOD_BOOK, "Codebook-based segmentation (Bradski2008)", "codebook"},
134       {METHOD_MOG, "Mixture-of-Gaussians segmentation (Bowden2001)", "mog"},
135       {METHOD_MOG2, "Mixture-of-Gaussians segmentation (Zivkovic2004)", "mog2"},
136       {0, NULL, NULL},
137     };
138     etype = g_enum_register_static ("GstSegmentationMethod", values);
139   }
140   return etype;
141 }
142
143 G_DEFINE_TYPE (GstSegmentation, gst_segmentation, GST_TYPE_VIDEO_FILTER);
144 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
145     GST_PAD_SINK,
146     GST_PAD_ALWAYS,
147     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGBA")));
148
149 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
150     GST_PAD_SRC,
151     GST_PAD_ALWAYS,
152     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGBA")));
153
154
155 static void gst_segmentation_set_property (GObject * object, guint prop_id,
156     const GValue * value, GParamSpec * pspec);
157 static void gst_segmentation_get_property (GObject * object, guint prop_id,
158     GValue * value, GParamSpec * pspec);
159
160 static GstFlowReturn gst_segmentation_transform_ip (GstVideoFilter * btrans,
161     GstVideoFrame * frame);
162
163 static gboolean gst_segmentation_stop (GstBaseTransform * basesrc);
164 static gboolean gst_segmentation_set_info (GstVideoFilter * filter,
165     GstCaps * incaps, GstVideoInfo * in_info,
166     GstCaps * outcaps, GstVideoInfo * out_info);
167 static void gst_segmentation_release_all_pointers (GstSegmentation * filter);
168
169 /* Codebook algorithm + connected components functions*/
170 static int update_codebook (unsigned char *p, codeBook * c,
171     unsigned *cbBounds, int numChannels);
172 static int clear_stale_entries (codeBook * c);
173 static unsigned char background_diff (unsigned char *p, codeBook * c,
174     int numChannels, int *minMod, int *maxMod);
175 static void find_connected_components (IplImage * mask, int poly1_hull0,
176     float perimScale, CvMemStorage * mem_storage, CvSeq * contours);
177
178 /* MOG (Mixture-of-Gaussians functions */
179 static int initialise_mog (GstSegmentation * filter);
180 static int run_mog_iteration (GstSegmentation * filter);
181 static int run_mog2_iteration (GstSegmentation * filter);
182 static int finalise_mog (GstSegmentation * filter);
183
184 /* initialize the segmentation's class */
185 static void
186 gst_segmentation_class_init (GstSegmentationClass * klass)
187 {
188   GObjectClass *gobject_class;
189   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
190   GstBaseTransformClass *basesrc_class = GST_BASE_TRANSFORM_CLASS (klass);
191   GstVideoFilterClass *video_class = (GstVideoFilterClass *) klass;
192
193   gobject_class = (GObjectClass *) klass;
194
195   gobject_class->set_property = gst_segmentation_set_property;
196   gobject_class->get_property = gst_segmentation_get_property;
197
198   basesrc_class->stop = gst_segmentation_stop;
199
200   video_class->transform_frame_ip = gst_segmentation_transform_ip;
201   video_class->set_info = gst_segmentation_set_info;
202
203   g_object_class_install_property (gobject_class, PROP_METHOD,
204       g_param_spec_enum ("method",
205           "Segmentation method to use",
206           "Segmentation method to use",
207           GST_TYPE_SEGMENTATION_METHOD, DEFAULT_METHOD,
208           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
209
210   g_object_class_install_property (gobject_class, PROP_TEST_MODE,
211       g_param_spec_boolean ("test-mode", "test-mode",
212           "If true, the output RGB is overwritten with the calculated foreground (white color)",
213           DEFAULT_TEST_MODE, (GParamFlags)
214           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
215
216   g_object_class_install_property (gobject_class, PROP_LEARNING_RATE,
217       g_param_spec_float ("learning-rate", "learning-rate",
218           "Speed with which a motionless foreground pixel would become background (inverse of number of frames)",
219           0, 1, DEFAULT_LEARNING_RATE, (GParamFlags) (G_PARAM_READWRITE)));
220
221   gst_element_class_set_static_metadata (element_class,
222       "Foreground/background video sequence segmentation",
223       "Filter/Effect/Video",
224       "Create a Foregound/Background mask applying a particular algorithm",
225       "Miguel Casas-Sanchez <miguelecasassanchez@gmail.com>");
226
227   gst_element_class_add_pad_template (element_class,
228       gst_static_pad_template_get (&src_factory));
229   gst_element_class_add_pad_template (element_class,
230       gst_static_pad_template_get (&sink_factory));
231
232 }
233
234 /* initialize the new element
235  * instantiate pads and add them to element
236  * set pad calback functions
237  * initialize instance structure
238  */
239 static void
240 gst_segmentation_init (GstSegmentation * filter)
241 {
242   filter->method = DEFAULT_METHOD;
243   filter->test_mode = DEFAULT_TEST_MODE;
244   filter->framecount = 0;
245   filter->learning_rate = DEFAULT_LEARNING_RATE;
246   gst_base_transform_set_in_place (GST_BASE_TRANSFORM (filter), TRUE);
247 }
248
249
250 static void
251 gst_segmentation_set_property (GObject * object, guint prop_id,
252     const GValue * value, GParamSpec * pspec)
253 {
254   GstSegmentation *filter = GST_SEGMENTATION (object);
255
256   switch (prop_id) {
257     case PROP_METHOD:
258       filter->method = g_value_get_enum (value);
259       break;
260     case PROP_TEST_MODE:
261       filter->test_mode = g_value_get_boolean (value);
262       break;
263     case PROP_LEARNING_RATE:
264       filter->learning_rate = g_value_get_float (value);
265       break;
266     default:
267       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
268       break;
269   }
270 }
271
272 static void
273 gst_segmentation_get_property (GObject * object, guint prop_id,
274     GValue * value, GParamSpec * pspec)
275 {
276   GstSegmentation *filter = GST_SEGMENTATION (object);
277
278   switch (prop_id) {
279     case PROP_METHOD:
280       g_value_set_enum (value, filter->method);
281       break;
282     case PROP_TEST_MODE:
283       g_value_set_boolean (value, filter->test_mode);
284       break;
285     case PROP_LEARNING_RATE:
286       g_value_set_float (value, filter->learning_rate);
287       break;
288     default:
289       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
290       break;
291   }
292 }
293
294 /* GstElement vmethod implementations */
295 /* this function handles the link with other elements */
296 static gboolean
297 gst_segmentation_set_info (GstVideoFilter * filter,
298     GstCaps * incaps, GstVideoInfo * in_info,
299     GstCaps * outcaps, GstVideoInfo * out_info)
300 {
301   GstSegmentation *segmentation = GST_SEGMENTATION (filter);
302   CvSize size;
303
304   size = cvSize (in_info->width, in_info->height);
305   segmentation->width = in_info->width;
306   segmentation->height = in_info->height;
307   /*  If cvRGB is already allocated, it means there's a cap modification, */
308   /*  so release first all the images. */
309   if (NULL != segmentation->cvRGBA)
310     gst_segmentation_release_all_pointers (segmentation);
311
312   segmentation->cvRGBA = cvCreateImageHeader (size, IPL_DEPTH_8U, 4);
313
314   segmentation->cvRGB = cvCreateImage (size, IPL_DEPTH_8U, 3);
315   segmentation->cvYUV = cvCreateImage (size, IPL_DEPTH_8U, 3);
316
317   segmentation->cvFG = cvCreateImage (size, IPL_DEPTH_8U, 1);
318   cvZero (segmentation->cvFG);
319
320   segmentation->ch1 = cvCreateImage (size, IPL_DEPTH_8U, 1);
321   segmentation->ch2 = cvCreateImage (size, IPL_DEPTH_8U, 1);
322   segmentation->ch3 = cvCreateImage (size, IPL_DEPTH_8U, 1);
323
324   /* Codebook method */
325   segmentation->TcodeBook = (codeBook *)
326       g_malloc (sizeof (codeBook) *
327       (segmentation->width * segmentation->height + 1));
328   for (int j = 0; j < segmentation->width * segmentation->height; j++) {
329     segmentation->TcodeBook[j].numEntries = 0;
330     segmentation->TcodeBook[j].t = 0;
331   }
332   segmentation->learning_interval = (int) (1.0 / segmentation->learning_rate);
333
334   /* Mixture-of-Gaussians (mog) methods */
335   initialise_mog (segmentation);
336
337   return TRUE;
338 }
339
340 /* Clean up */
341 static gboolean
342 gst_segmentation_stop (GstBaseTransform * basesrc)
343 {
344   GstSegmentation *filter = GST_SEGMENTATION (basesrc);
345
346   if (filter->cvRGBA != NULL)
347     gst_segmentation_release_all_pointers (filter);
348
349   return TRUE;
350 }
351
352 static void
353 gst_segmentation_release_all_pointers (GstSegmentation * filter)
354 {
355   cvReleaseImage (&filter->cvRGBA);
356   cvReleaseImage (&filter->cvRGB);
357   cvReleaseImage (&filter->cvYUV);
358   cvReleaseImage (&filter->cvFG);
359   cvReleaseImage (&filter->ch1);
360   cvReleaseImage (&filter->ch2);
361   cvReleaseImage (&filter->ch3);
362
363   g_free (filter->TcodeBook);
364   finalise_mog (filter);
365 }
366
367 static GstFlowReturn
368 gst_segmentation_transform_ip (GstVideoFilter * btrans, GstVideoFrame * frame)
369 {
370   GstSegmentation *filter = GST_SEGMENTATION (btrans);
371   int j;
372
373   /*  get image data from the input, which is RGBA */
374   filter->cvRGBA->imageData = (char *) GST_VIDEO_FRAME_COMP_DATA (frame, 0);
375   filter->cvRGBA->widthStep = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0);
376   filter->framecount++;
377
378   /*  Image preprocessing: color space conversion etc */
379   cvCvtColor (filter->cvRGBA, filter->cvRGB, CV_RGBA2RGB);
380   cvCvtColor (filter->cvRGB, filter->cvYUV, CV_RGB2YCrCb);
381
382   /* Create and update a fg/bg model using a codebook approach following the 
383    * opencv O'Reilly book [1] implementation of the algo described in [2].
384    *
385    * [1] Learning OpenCV: Computer Vision with the OpenCV Library by Gary 
386    * Bradski and Adrian Kaehler, Published by O'Reilly Media, October 3, 2008
387    * [2] "Real-time Foreground-Background Segmentation using Codebook Model", 
388    * Real-time Imaging, Volume 11, Issue 3, Pages 167-256, June 2005. */
389   if (METHOD_BOOK == filter->method) {
390     unsigned cbBounds[3] = { 10, 5, 5 };
391     int minMod[3] = { 20, 20, 20 }, maxMod[3] = {
392     20, 20, 20};
393
394     if (filter->framecount < 30) {
395       /* Learning background phase: update_codebook on every frame */
396       for (j = 0; j < filter->width * filter->height; j++) {
397         update_codebook ((unsigned char *) filter->cvYUV->imageData + j * 3,
398             (codeBook *) & (filter->TcodeBook[j]), cbBounds, 3);
399       }
400     } else {
401       /*  this updating is responsible for FG becoming BG again */
402       if (filter->framecount % filter->learning_interval == 0) {
403         for (j = 0; j < filter->width * filter->height; j++) {
404           update_codebook ((uchar *) filter->cvYUV->imageData + j * 3,
405               (codeBook *) & (filter->TcodeBook[j]), cbBounds, 3);
406         }
407       }
408       if (filter->framecount % 60 == 0) {
409         for (j = 0; j < filter->width * filter->height; j++)
410           clear_stale_entries ((codeBook *) & (filter->TcodeBook[j]));
411       }
412
413       for (j = 0; j < filter->width * filter->height; j++) {
414         if (background_diff
415             ((uchar *) filter->cvYUV->imageData + j * 3,
416                 (codeBook *) & (filter->TcodeBook[j]), 3, minMod, maxMod)) {
417           filter->cvFG->imageData[j] = 255;
418         } else {
419           filter->cvFG->imageData[j] = 0;
420         }
421       }
422     }
423
424     /* 3rd param is the smallest area to show: (w+h)/param , in pixels */
425     find_connected_components (filter->cvFG, 1, 10000,
426         filter->mem_storage, filter->contours);
427
428   }
429   /* Create the foreground and background masks using BackgroundSubtractorMOG [1], 
430    *  Gaussian Mixture-based Background/Foreground segmentation algorithm. OpenCV 
431    * MOG implements the algorithm described in [2].
432    * 
433    * [1] http://opencv.itseez.com/modules/video/doc/motion_analysis_and_object_tracking.html#backgroundsubtractormog
434    * [2] P. KadewTraKuPong and R. Bowden, "An improved adaptive background 
435    * mixture model for real-time tracking with shadow detection", Proc. 2nd 
436    * European Workshop on Advanced Video-Based Surveillance Systems, 2001
437    */
438   else if (METHOD_MOG == filter->method) {
439     run_mog_iteration (filter);
440   }
441   /* Create the foreground and background masks using BackgroundSubtractorMOG2
442    * [1], Gaussian Mixture-based Background/Foreground segmentation algorithm. 
443    * OpenCV MOG2 implements the algorithm described in [2] and [3].
444    * 
445    * [1] http://opencv.itseez.com/modules/video/doc/motion_analysis_and_object_tracking.html#backgroundsubtractormog2
446    * [2] Z.Zivkovic, "Improved adaptive Gausian mixture model for background 
447    * subtraction", International Conference Pattern Recognition, UK, Aug 2004.
448    * [3] Z.Zivkovic, F. van der Heijden, "Efficient Adaptive Density Estimation 
449    * per Image Pixel for the Task of Background Subtraction", Pattern 
450    * Recognition Letters, vol. 27, no. 7, pages 773-780, 2006.   */
451   else if (METHOD_MOG2 == filter->method) {
452     run_mog2_iteration (filter);
453   }
454
455   /*  if we want to test_mode, just overwrite the output */
456   if (filter->test_mode) {
457     cvCvtColor (filter->cvFG, filter->cvRGB, CV_GRAY2RGB);
458
459     cvSplit (filter->cvRGB, filter->ch1, filter->ch2, filter->ch3, NULL);
460   } else
461     cvSplit (filter->cvRGBA, filter->ch1, filter->ch2, filter->ch3, NULL);
462
463   /*  copy anyhow the fg/bg to the alpha channel in the output image */
464   cvMerge (filter->ch1, filter->ch2, filter->ch3, filter->cvFG, filter->cvRGBA);
465
466
467   return GST_FLOW_OK;
468 }
469
470 /* entry point to initialize the plug-in
471  * initialize the plug-in itself
472  * register the element factories and other features
473  */
474 gboolean
475 gst_segmentation_plugin_init (GstPlugin * plugin)
476 {
477   GST_DEBUG_CATEGORY_INIT (gst_segmentation_debug, "segmentation",
478       0, "Performs Foreground/Background segmentation in video sequences");
479
480   return gst_element_register (plugin, "segmentation", GST_RANK_NONE,
481       GST_TYPE_SEGMENTATION);
482 }
483
484
485
486 #ifdef CODE_FROM_OREILLY_BOOK   /* See license at the beginning of the page */
487 /* 
488   int update_codebook(uchar *p, codeBook &c, unsigned cbBounds) 
489   Updates the codebook entry with a new data point 
490   
491   p Pointer to a YUV or HSI pixel 
492   c Codebook for this pixel 
493   cbBounds Learning bounds for codebook (Rule of thumb: 10) 
494   numChannels Number of color channels we¡¯re learning 
495   
496   NOTES: 
497   cvBounds must be of length equal to numChannels 
498   
499   RETURN 
500   codebook index 
501 */
502 int
503 update_codebook (unsigned char *p, codeBook * c, unsigned *cbBounds,
504     int numChannels)
505 {
506 /* c->t+=1; */
507   unsigned int high[3], low[3];
508   int n, i;
509   int matchChannel;
510
511   for (n = 0; n < numChannels; n++) {
512     high[n] = p[n] + cbBounds[n];
513     if (high[n] > 255)
514       high[n] = 255;
515
516     if (p[n] > cbBounds[n])
517       low[n] = p[n] - cbBounds[n];
518     else
519       low[n] = 0;
520   }
521
522 /*  SEE IF THIS FITS AN EXISTING CODEWORD */
523   for (i = 0; i < c->numEntries; i++) {
524     matchChannel = 0;
525     for (n = 0; n < numChannels; n++) {
526       if ((c->cb[i]->learnLow[n] <= *(p + n)) &&
527 /* Found an entry for this channel */
528           (*(p + n) <= c->cb[i]->learnHigh[n])) {
529         matchChannel++;
530       }
531     }
532     if (matchChannel == numChannels) {  /* If an entry was found */
533       c->cb[i]->t_last_update = c->t;
534 /* adjust this codeword for the first channel */
535       for (n = 0; n < numChannels; n++) {
536         if (c->cb[i]->max[n] < *(p + n)) {
537           c->cb[i]->max[n] = *(p + n);
538         } else if (c->cb[i]->min[n] > *(p + n)) {
539           c->cb[i]->min[n] = *(p + n);
540         }
541       }
542       break;
543     }
544   }
545 /*  OVERHEAD TO TRACK POTENTIAL STALE ENTRIES */
546   for (int s = 0; s < c->numEntries; s++) {
547 /*  Track which codebook entries are going stale: */
548     int negRun = c->t - c->cb[s]->t_last_update;
549     if (c->cb[s]->stale < negRun)
550       c->cb[s]->stale = negRun;
551   }
552 /*  ENTER A NEW CODEWORD IF NEEDED */
553   if (i == c->numEntries) {     /* if no existing codeword found, make one */
554     code_element **foo =
555         (code_element **) g_malloc (sizeof (code_element *) *
556         (c->numEntries + 1));
557     for (int ii = 0; ii < c->numEntries; ii++) {
558       foo[ii] = c->cb[ii];      /* copy all pointers */
559     }
560     foo[c->numEntries] = (code_element *) g_malloc (sizeof (code_element));
561     if (c->numEntries)
562       g_free (c->cb);
563     c->cb = foo;
564     for (n = 0; n < numChannels; n++) {
565       c->cb[c->numEntries]->learnHigh[n] = high[n];
566       c->cb[c->numEntries]->learnLow[n] = low[n];
567       c->cb[c->numEntries]->max[n] = *(p + n);
568       c->cb[c->numEntries]->min[n] = *(p + n);
569     }
570     c->cb[c->numEntries]->t_last_update = c->t;
571     c->cb[c->numEntries]->stale = 0;
572     c->numEntries += 1;
573   }
574 /*  SLOWLY ADJUST LEARNING BOUNDS */
575   for (n = 0; n < numChannels; n++) {
576     if (c->cb[i]->learnHigh[n] < high[n])
577       c->cb[i]->learnHigh[n] += 1;
578     if (c->cb[i]->learnLow[n] > low[n])
579       c->cb[i]->learnLow[n] -= 1;
580   }
581   return (i);
582 }
583
584
585
586
587
588 /*
589  int clear_stale_entries(codeBook &c) 
590   During learning, after you've learned for some period of time, 
591   periodically call this to clear out stale codebook entries 
592   
593   c Codebook to clean up 
594   
595   Return 
596   number of entries cleared 
597 */
598 int
599 clear_stale_entries (codeBook * c)
600 {
601   int staleThresh = c->t >> 1;
602   int *keep = (int *) g_malloc (sizeof (int) * (c->numEntries));
603   int keepCnt = 0;
604   code_element **foo;
605   int k;
606   int numCleared;
607 /*  SEE WHICH CODEBOOK ENTRIES ARE TOO STALE */
608   for (int i = 0; i < c->numEntries; i++) {
609     if (c->cb[i]->stale > staleThresh)
610       keep[i] = 0;              /* Mark for destruction */
611     else {
612       keep[i] = 1;              /* Mark to keep */
613       keepCnt += 1;
614     }
615   }
616   /*  KEEP ONLY THE GOOD */
617   c->t = 0;                     /* Full reset on stale tracking */
618   foo = (code_element **) g_malloc (sizeof (code_element *) * keepCnt);
619   k = 0;
620   for (int ii = 0; ii < c->numEntries; ii++) {
621     if (keep[ii]) {
622       foo[k] = c->cb[ii];
623       /* We have to refresh these entries for next clearStale */
624       foo[k]->t_last_update = 0;
625       k++;
626     }
627   }
628   /*  CLEAN UP */
629   g_free (keep);
630   g_free (c->cb);
631   c->cb = foo;
632   numCleared = c->numEntries - keepCnt;
633   c->numEntries = keepCnt;
634   return (numCleared);
635 }
636
637
638
639 /*
640   uchar background_diff( uchar *p, codeBook &c, 
641   int minMod, int maxMod) 
642   Given a pixel and a codebook, determine if the pixel is 
643   covered by the codebook 
644   
645   p Pixel pointer (YUV interleaved) 
646   c Codebook reference 
647   numChannels Number of channels we are testing 
648   maxMod Add this (possibly negative) number onto 
649
650   max level when determining if new pixel is foreground 
651   minMod Subract this (possibly negative) number from 
652   min level when determining if new pixel is foreground 
653   
654   NOTES: 
655   minMod and maxMod must have length numChannels, 
656   e.g. 3 channels => minMod[3], maxMod[3]. There is one min and 
657   one max threshold per channel. 
658   
659   Return 
660   0 => background, 255 => foreground 
661 */
662 unsigned char
663 background_diff (unsigned char *p, codeBook * c, int numChannels,
664     int *minMod, int *maxMod)
665 {
666   int matchChannel;
667 /*  SEE IF THIS FITS AN EXISTING CODEWORD */
668   int i;
669   for (i = 0; i < c->numEntries; i++) {
670     matchChannel = 0;
671     for (int n = 0; n < numChannels; n++) {
672       if ((c->cb[i]->min[n] - minMod[n] <= *(p + n)) &&
673           (*(p + n) <= c->cb[i]->max[n] + maxMod[n])) {
674         matchChannel++;         /* Found an entry for this channel */
675       } else {
676         break;
677       }
678     }
679     if (matchChannel == numChannels) {
680       break;                    /* Found an entry that matched all channels */
681     }
682   }
683   if (i >= c->numEntries)
684     return (255);
685   return (0);
686 }
687
688
689
690
691 /*
692  void find_connected_components(IplImage *mask, int poly1_hull0,
693  float perimScale, int *num,
694  CvRect *bbs, CvPoint *centers)
695  This cleans up the foreground segmentation mask derived from calls
696  to backgroundDiff
697
698  mask Is a grayscale (8-bit depth) â€œrawâ€\9d mask image that
699  will be cleaned up
700
701  OPTIONAL PARAMETERS:
702  poly1_hull0 If set, approximate connected component by
703  (DEFAULT) polygon, or else convex hull (0)
704  perimScale Len = image (width+height)/perimScale. If contour
705  len < this, delete that contour (DEFAULT: 4)
706  num Maximum number of rectangles and/or centers to
707  return; on return, will contain number filled
708  (DEFAULT: NULL)
709  bbs Pointer to bounding box rectangle vector of
710  length num. (DEFAULT SETTING: NULL)
711  centers Pointer to contour centers vector of length
712  num (DEFAULT: NULL)
713 */
714
715 /* Approx.threshold - the bigger it is, the simpler is the boundary */
716 #define CVCONTOUR_APPROX_LEVEL 1
717 /* How many iterations of erosion and/or dilation there should be */
718 #define CVCLOSE_ITR 1
719 static void
720 find_connected_components (IplImage * mask, int poly1_hull0, float perimScale,
721     CvMemStorage * mem_storage, CvSeq * contours)
722 {
723   CvContourScanner scanner;
724   CvSeq *c;
725   int numCont = 0;
726   /* Just some convenience variables */
727   const CvScalar CVX_WHITE = CV_RGB (0xff, 0xff, 0xff);
728   const CvScalar CVX_BLACK = CV_RGB (0x00, 0x00, 0x00);
729
730   /* CLEAN UP RAW MASK */
731   cvMorphologyEx (mask, mask, 0, 0, CV_MOP_OPEN, CVCLOSE_ITR);
732   cvMorphologyEx (mask, mask, 0, 0, CV_MOP_CLOSE, CVCLOSE_ITR);
733   /* FIND CONTOURS AROUND ONLY BIGGER REGIONS */
734   if (mem_storage == NULL) {
735     mem_storage = cvCreateMemStorage (0);
736   } else {
737     cvClearMemStorage (mem_storage);
738   }
739
740   scanner = cvStartFindContours (mask, mem_storage, sizeof (CvContour),
741       CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cvPoint (0, 0));
742
743   while ((c = cvFindNextContour (scanner)) != NULL) {
744     double len = cvContourArea (c, CV_WHOLE_SEQ, 0);
745     /* calculate perimeter len threshold: */
746     double q = (mask->height + mask->width) / perimScale;
747     /* Get rid of blob if its perimeter is too small: */
748     if (len < q) {
749       cvSubstituteContour (scanner, NULL);
750     } else {
751       /* Smooth its edges if its large enough */
752       CvSeq *c_new;
753       if (poly1_hull0) {
754         /* Polygonal approximation */
755         c_new =
756             cvApproxPoly (c, sizeof (CvContour), mem_storage, CV_POLY_APPROX_DP,
757             CVCONTOUR_APPROX_LEVEL, 0);
758       } else {
759         /* Convex Hull of the segmentation */
760         c_new = cvConvexHull2 (c, mem_storage, CV_CLOCKWISE, 1);
761       }
762       cvSubstituteContour (scanner, c_new);
763       numCont++;
764     }
765   }
766   contours = cvEndFindContours (&scanner);
767
768   /* PAINT THE FOUND REGIONS BACK INTO THE IMAGE */
769   cvZero (mask);
770   /* DRAW PROCESSED CONTOURS INTO THE MASK */
771   for (c = contours; c != NULL; c = c->h_next)
772     cvDrawContours (mask, c, CVX_WHITE, CVX_BLACK, -1, CV_FILLED, 8, cvPoint (0,
773             0));
774 }
775 #endif /*ifdef CODE_FROM_OREILLY_BOOK */
776
777
778 int
779 initialise_mog (GstSegmentation * filter)
780 {
781   filter->img_input_as_cvMat = (void *) new cv::Mat (filter->cvYUV, false);
782   filter->img_fg_as_cvMat = (void *) new cv::Mat (filter->cvFG, false);
783
784   filter->mog = (void *) new cv::BackgroundSubtractorMOG ();
785   filter->mog2 = (void *) new cv::BackgroundSubtractorMOG2 ();
786
787   return (0);
788 }
789
790 int
791 run_mog_iteration (GstSegmentation * filter)
792 {
793   ((cv::Mat *) filter->img_input_as_cvMat)->data =
794       (uchar *) filter->cvYUV->imageData;
795   ((cv::Mat *) filter->img_fg_as_cvMat)->data =
796       (uchar *) filter->cvFG->imageData;
797
798   /*
799      BackgroundSubtractorMOG [1], Gaussian Mixture-based Background/Foreground 
800      Segmentation Algorithm. OpenCV MOG implements the algorithm described in [2].
801
802      [1] http://opencv.itseez.com/modules/video/doc/motion_analysis_and_object_tracking.html#backgroundsubtractormog
803      [2] P. KadewTraKuPong and R. Bowden, "An improved adaptive background 
804      mixture model for real-time tracking with shadow detection", Proc. 2nd 
805      European Workshop on Advanced Video-Based Surveillance Systems, 2001
806    */
807
808   (*((cv::BackgroundSubtractorMOG *) filter->mog)) (*((cv::Mat *) filter->
809           img_input_as_cvMat), *((cv::Mat *) filter->img_fg_as_cvMat),
810       filter->learning_rate);
811
812   return (0);
813 }
814
815 int
816 run_mog2_iteration (GstSegmentation * filter)
817 {
818   ((cv::Mat *) filter->img_input_as_cvMat)->data =
819       (uchar *) filter->cvYUV->imageData;
820   ((cv::Mat *) filter->img_fg_as_cvMat)->data =
821       (uchar *) filter->cvFG->imageData;
822
823   /*
824      BackgroundSubtractorMOG2 [1], Gaussian Mixture-based Background/Foreground 
825      segmentation algorithm. OpenCV MOG2 implements the algorithm described in 
826      [2] and [3].
827
828      [1] http://opencv.itseez.com/modules/video/doc/motion_analysis_and_object_tracking.html#backgroundsubtractormog2
829      [2] Z.Zivkovic, "Improved adaptive Gausian mixture model for background 
830      subtraction", International Conference Pattern Recognition, UK, August, 2004.
831      [3] Z.Zivkovic, F. van der Heijden, "Efficient Adaptive Density Estimation per 
832      Image Pixel for the Task of Background Subtraction", Pattern Recognition 
833      Letters, vol. 27, no. 7, pages 773-780, 2006.
834    */
835
836   (*((cv::BackgroundSubtractorMOG *) filter->mog2)) (*((cv::Mat *) filter->
837           img_input_as_cvMat), *((cv::Mat *) filter->img_fg_as_cvMat),
838       filter->learning_rate);
839
840   return (0);
841 }
842
843 int
844 finalise_mog (GstSegmentation * filter)
845 {
846   delete (cv::Mat *) filter->img_input_as_cvMat;
847   delete (cv::Mat *) filter->img_fg_as_cvMat;
848   delete (cv::BackgroundSubtractorMOG *) filter->mog;
849   delete (cv::BackgroundSubtractorMOG2 *) filter->mog2;
850   return (0);
851 }