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