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