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