2 * GStreamer MotioCells detect areas of motion
3 * Copyright (C) 2011 Robert Jobbagy <jobbagy.robert@gmail.com>
4 * Copyright (C) 2011 Nicola Murino <nicola.murino@gmail.com>
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
24 * Alternatively, the contents of this file may be used under the
25 * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
26 * which case the following provisions apply instead of the ones
29 * This library is free software; you can redistribute it and/or
30 * modify it under the terms of the GNU Library General Public
31 * License as published by the Free Software Foundation; either
32 * version 2 of the License, or (at your option) any later version.
34 * This library is distributed in the hope that it will be useful,
35 * but WITHOUT ANY WARRANTY; without even the implied warranty of
36 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
37 * Library General Public License for more details.
39 * You should have received a copy of the GNU Library General Public
40 * License along with this library; if not, write to the
41 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
42 * Boston, MA 02111-1307, USA.
46 * SECTION:element-motioncells
48 * Performs motion detection on videos.
51 * <title>Example launch line</title>
53 * gst-launch-0.10 videotestsrc pattern=18 ! videorate ! videoscale ! video/x-raw-yuv,width=320,height=240,framerate=5/1 ! ffmpegcolorspace ! motioncells ! ffmpegcolorspace ! xvimagesink
65 #include "gstmotioncells.h"
66 #include "motioncells_wrapper.h"
71 GST_DEBUG_CATEGORY_STATIC (gst_motion_cells_debug);
72 #define GST_CAT_DEFAULT gst_motion_cells_debug
77 #define SENSITIVITY_DEFAULT 0.5
78 #define SENSITIVITY_MIN 0
79 #define SENSITIVITY_MAX 1
80 #define THRESHOLD_MIN 0
81 #define THRESHOLD_DEFAULT 0.01
82 #define THRESHOLD_MAX 1.0
86 #define POST_NO_MOTION_MIN 0
87 #define POST_NO_MOTION_DEF 0
88 #define POST_NO_MOTION_MAX 180
89 #define MINIMUM_MOTION_FRAMES_MIN 1
90 #define MINIMUM_MOTION_FRAMES_DEF 1
91 #define MINIMUM_MOTION_FRAMES_MAX 60
92 #define THICKNESS_MIN -1
93 #define THICKNESS_DEF 1
94 #define THICKNESS_MAX 5
97 #define DATE_MAX LONG_MAX
98 #define DEF_DATAFILEEXT "vamc"
102 #define GFREE(POINTER)\
108 int instanceCounter = 0;
109 gboolean element_id_was_max = false;
111 /* Filter signals and args */
129 PROP_MOTIONMASKCOORD,
130 PROP_MOTIONMASKCELLSPOS,
135 PROP_MINIMUNMOTIONFRAMES,
136 PROP_CALCULATEMOTION,
139 PROP_MOTIONCELLTHICKNESS
142 /* the capabilities of the inputs and outputs.
144 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
145 GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("video/x-raw-rgb"));
147 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
148 GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS ("video/x-raw-rgb"));
150 GST_BOILERPLATE (GstMotioncells, gst_motion_cells, GstElement,
153 static void gst_motion_cells_set_property (GObject * object, guint prop_id,
154 const GValue * value, GParamSpec * pspec);
155 static void gst_motion_cells_get_property (GObject * object, guint prop_id,
156 GValue * value, GParamSpec * pspec);
158 static gboolean gst_motion_cells_set_caps (GstPad * pad, GstCaps * caps);
159 static GstFlowReturn gst_motion_cells_chain (GstPad * pad, GstBuffer * buf);
161 static void gst_motioncells_update_motion_cells (GstMotioncells * filter);
162 static void gst_motioncells_update_motion_masks (GstMotioncells * filter);
166 gst_motion_cells_finalize (GObject * obj)
168 GstMotioncells *filter = gst_motion_cells (obj);
170 motion_cells_free (filter->id);
172 //freeing previously allocated dynamic array
173 if (filter->motionmaskcoord_count > 0) {
174 GFREE (filter->motionmaskcoords);
177 if (filter->motionmaskcells_count > 0) {
178 GFREE (filter->motionmaskcellsidx);
180 if (filter->motioncells_count > 0) {
181 GFREE (filter->motioncellsidx);
184 if (filter->cvImage) {
185 cvReleaseImage (&filter->cvImage);
188 GFREE (filter->motioncellscolor);
189 GFREE (filter->prev_datafile);
190 GFREE (filter->cur_datafile);
191 GFREE (filter->basename_datafile);
192 GFREE (filter->datafile_extension);
194 g_mutex_free (filter->propset_mutex);
196 G_OBJECT_CLASS (parent_class)->finalize (obj);
199 /* GObject vmethod implementations */
201 gst_motion_cells_base_init (gpointer gclass)
203 GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
205 gst_element_class_set_details_simple (element_class,
207 "Filter/Effect/Video",
208 "Performs motion detection on videos and images, providing detected motion cells index via bus messages",
209 "Robert Jobbagy <jobbagy dot robert at gmail dot com>, Nicola Murino <nicola dot murino at gmail.com>");
211 gst_element_class_add_pad_template (element_class,
212 gst_static_pad_template_get (&src_factory));
213 gst_element_class_add_pad_template (element_class,
214 gst_static_pad_template_get (&sink_factory));
217 /* initialize the motioncells's class */
219 gst_motion_cells_class_init (GstMotioncellsClass * klass)
221 GObjectClass *gobject_class;
223 gobject_class = (GObjectClass *) klass;
224 parent_class = g_type_class_peek_parent (klass);
226 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_motion_cells_finalize);
227 gobject_class->set_property = gst_motion_cells_set_property;
228 gobject_class->get_property = gst_motion_cells_get_property;
230 g_object_class_install_property (gobject_class, PROP_GRID_X,
231 g_param_spec_int ("gridx", "Number of Horizontal Grids",
232 "You can give number of horizontal grid cells.", GRID_MIN, GRID_MAX,
233 GRID_DEF, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
234 g_object_class_install_property (gobject_class, PROP_GRID_Y,
235 g_param_spec_int ("gridy", "Number of Vertical Grids",
236 "You can give number of vertical grid cells.", GRID_MIN, GRID_MAX,
237 GRID_DEF, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
238 g_object_class_install_property (gobject_class, PROP_SENSITIVITY,
239 g_param_spec_double ("sensitivity", "Motion Sensitivity",
240 "You can tunning the element motion sensitivity.", SENSITIVITY_MIN,
241 SENSITIVITY_MAX, SENSITIVITY_DEFAULT,
242 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
243 g_object_class_install_property (gobject_class, PROP_THRESHOLD,
244 g_param_spec_double ("threshold", "Lower bound of motion cells number",
245 "Threshold value for motion, when motion cells number greater sum cells * threshold, we show motion.",
246 THRESHOLD_MIN, THRESHOLD_MAX, THRESHOLD_DEFAULT,
247 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
248 g_object_class_install_property (gobject_class, PROP_GAP,
249 g_param_spec_int ("gap",
250 "Gap is time in second, elapsed time from last motion timestamp. ",
251 "If elapsed time minus form last motion timestamp is greater or equal than gap then we post motion finished bus message. ",
252 GAP_MIN, GAP_MAX, GAP_DEF,
253 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
254 g_object_class_install_property (gobject_class, PROP_POSTNOMOTION,
255 g_param_spec_int ("postnomotion", "POSTNOMOTION",
256 "If non 0 post a no_motion event is posted on the bus if no motion is detected for N seconds",
257 POST_NO_MOTION_MIN, POST_NO_MOTION_MAX, POST_NO_MOTION_DEF,
258 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
259 g_object_class_install_property (gobject_class, PROP_MINIMUNMOTIONFRAMES,
260 g_param_spec_int ("minimummotionframes", "MINIMUN MOTION FRAMES",
261 "Define the minimum number of motion frames that trigger a motion event",
262 MINIMUM_MOTION_FRAMES_MIN, MINIMUM_MOTION_FRAMES_MAX,
263 MINIMUM_MOTION_FRAMES_DEF,
264 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
265 g_object_class_install_property (gobject_class, PROP_DISPLAY,
266 g_param_spec_boolean ("display", "Display",
267 "Motion Cells visible or not on Current Frame", FALSE,
268 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
269 g_object_class_install_property (gobject_class, PROP_POSTALLMOTION,
270 g_param_spec_boolean ("postallmotion", "Post All Motion",
271 "Element post bus msg for every motion frame or just motion start and motion stop",
272 FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
273 g_object_class_install_property (gobject_class, PROP_USEALPHA,
274 g_param_spec_boolean ("usealpha", "Use alpha",
275 "Use or not alpha blending on frames with motion cells", TRUE,
276 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
277 g_object_class_install_property (gobject_class, PROP_DATE,
278 g_param_spec_long ("date", "Motion Cell Date",
279 "Current Date in milliseconds", DATE_MIN, DATE_MAX, DATE_DEF,
280 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
281 g_object_class_install_property (gobject_class, PROP_DATAFILE,
282 g_param_spec_string ("datafile", "DataFile",
283 "Location of motioncells data file (empty string means no saving)",
284 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
285 g_object_class_install_property (gobject_class, PROP_DATAFILE_EXT,
286 g_param_spec_string ("datafileextension", "DataFile Extension",
287 "Extension of datafile", DEF_DATAFILEEXT,
288 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
289 g_object_class_install_property (gobject_class, PROP_MOTIONMASKCOORD,
290 g_param_spec_string ("motionmaskcoords", "Motion Mask with Coordinates",
291 "The upper left x, y and lower right x, y coordinates separated with \":\", "
292 "describe a region. Regions separated with \",\"", NULL,
293 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
294 g_object_class_install_property (gobject_class, PROP_MOTIONMASKCELLSPOS,
295 g_param_spec_string ("motionmaskcellspos",
296 "Motion Mask with Cells Position",
297 "The line and column idx separated with \":\" what cells want we mask-out, "
298 "describe a cell. Cells separated with \",\"", NULL,
299 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
300 g_object_class_install_property (gobject_class, PROP_CELLSCOLOR,
301 g_param_spec_string ("cellscolor", "Color of Motion Cells",
302 "The color of motion cells separated with \",\"", "255,255,0",
303 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
304 g_object_class_install_property (gobject_class, PROP_MOTIONCELLSIDX,
305 g_param_spec_string ("motioncellsidx", "Motion Cells Of Interest(MOCI)",
306 "The line and column idx separated with \":\", "
307 "describe a cell. Cells separated with \",\"", NULL,
308 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
309 g_object_class_install_property (gobject_class, PROP_CALCULATEMOTION,
310 g_param_spec_boolean ("calculatemotion", "Calculate Motion",
311 "If needs calculate motion on frame you need this property setting true otherwise false",
312 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
313 g_object_class_install_property (gobject_class, PROP_MOTIONCELLTHICKNESS,
314 g_param_spec_int ("motioncellthickness", "Motion Cell Thickness",
315 "Motion Cell Border Thickness, if it's -1 then motion cell will be fill",
316 THICKNESS_MIN, THICKNESS_MAX, THICKNESS_DEF,
317 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
320 /* initialize the new element
321 * instantiate pads and add them to element
322 * set pad callback functions
323 * initialize instance structure
326 gst_motion_cells_init (GstMotioncells * filter, GstMotioncellsClass * gclass)
328 filter->propset_mutex = g_mutex_new ();
329 filter->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
330 gst_pad_set_setcaps_function (filter->sinkpad,
331 GST_DEBUG_FUNCPTR (gst_motion_cells_set_caps));
332 gst_pad_set_getcaps_function (filter->sinkpad,
333 GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
334 gst_pad_set_chain_function (filter->sinkpad,
335 GST_DEBUG_FUNCPTR (gst_motion_cells_chain));
337 filter->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
338 gst_pad_set_getcaps_function (filter->srcpad,
339 GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
341 gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
342 gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
344 filter->display = TRUE;
345 filter->calculate_motion = TRUE;
347 filter->prevgridx = 0;
348 filter->prevgridy = 0;
349 filter->gridx = GRID_DEF;
350 filter->gridy = GRID_DEF;
351 filter->gap = GAP_DEF;
352 filter->postnomotion = POST_NO_MOTION_DEF;
353 filter->minimum_motion_frames = MINIMUM_MOTION_FRAMES_DEF;
355 filter->prev_datafile = g_strdup (NULL);
356 filter->cur_datafile = g_strdup (NULL);
357 filter->basename_datafile = g_strdup (NULL);
358 filter->datafile_extension = g_strdup (DEF_DATAFILEEXT);
359 filter->sensitivity = SENSITIVITY_DEFAULT;
360 filter->threshold = THRESHOLD_DEFAULT;
362 filter->motionmaskcoord_count = 0;
363 filter->motionmaskcoords = NULL;
364 filter->motionmaskcells_count = 0;
365 filter->motionmaskcellsidx = NULL;
366 filter->motioncellscolor = g_new0 (cellscolor, 1);
367 filter->motioncellscolor->R_channel_value = 255;
368 filter->motioncellscolor->G_channel_value = 255;
369 filter->motioncellscolor->B_channel_value = 0;
370 filter->motioncellsidx = NULL;
371 filter->motioncells_count = 0;
372 filter->motion_begin_timestamp = 0;
373 filter->last_motion_timestamp = 0;
374 filter->last_nomotion_notified = 0;
375 filter->consecutive_motion = 0;
376 filter->motion_timestamp = 0;
377 filter->prev_buff_timestamp = 0;
378 filter->cur_buff_timestamp = 0;
379 filter->diff_timestamp = -1;
380 gettimeofday (&filter->tv, NULL);
381 filter->starttime = 1000 * filter->tv.tv_sec;
382 filter->previous_motion = false;
383 filter->changed_datafile = false;
384 filter->postallmotion = false;
385 filter->usealpha = true;
386 filter->firstdatafile = false;
387 filter->firstgridx = true;
388 filter->firstgridy = true;
389 filter->changed_gridx = false;
390 filter->changed_gridy = false;
391 filter->firstframe = true;
392 filter->changed_startime = false;
393 filter->sent_init_error_msg = false;
394 filter->sent_save_error_msg = false;
395 filter->thickness = THICKNESS_DEF;
397 filter->datafileidx = 0;
398 g_mutex_lock (filter->propset_mutex);
399 filter->id = instanceCounter;
400 motion_cells_init ();
401 g_mutex_unlock (filter->propset_mutex);
406 gst_motion_cells_set_property (GObject * object, guint prop_id,
407 const GValue * value, GParamSpec * pspec)
409 GstMotioncells *filter = gst_motion_cells (object);
410 //variables for overlay regions setup
411 gchar **strs, **colorstr, **motioncellsstr, **motionmaskcellsstr;
412 int i, ux, uy, lx, ly;
414 int cellscolorscnt = 0;
415 int linidx, colidx, masklinidx, maskcolidx;
420 GstStateChangeReturn ret;
422 g_mutex_lock (filter->propset_mutex);
425 ret = gst_element_get_state (GST_ELEMENT (filter),
426 &filter->state, NULL, 250 * GST_NSECOND);
427 filter->gridx = g_value_get_int (value);
428 if (filter->prevgridx != filter->gridx
429 && ret == GST_STATE_CHANGE_SUCCESS
430 && filter->state == GST_STATE_PLAYING) {
431 filter->changed_gridx = true;
433 filter->prevgridx = filter->gridx;
436 ret = gst_element_get_state (GST_ELEMENT (filter),
437 &filter->state, NULL, 250 * GST_NSECOND);
438 filter->gridy = g_value_get_int (value);
439 if (filter->prevgridy != filter->gridy
440 && ret == GST_STATE_CHANGE_SUCCESS
441 && filter->state == GST_STATE_PLAYING) {
442 filter->changed_gridy = true;
444 filter->prevgridy = filter->gridy;
447 filter->gap = g_value_get_int (value);
449 case PROP_POSTNOMOTION:
450 filter->postnomotion = g_value_get_int (value);
452 case PROP_MINIMUNMOTIONFRAMES:
453 filter->minimum_motion_frames = g_value_get_int (value);
455 case PROP_SENSITIVITY:
456 filter->sensitivity = g_value_get_double (value);
459 filter->threshold = g_value_get_double (value);
462 filter->display = g_value_get_boolean (value);
464 case PROP_POSTALLMOTION:
465 filter->postallmotion = g_value_get_boolean (value);
468 filter->usealpha = g_value_get_boolean (value);
470 case PROP_CALCULATEMOTION:
471 filter->calculate_motion = g_value_get_boolean (value);
474 ret = gst_element_get_state (GST_ELEMENT (filter),
475 &filter->state, NULL, 250 * GST_NSECOND);
476 if (ret == GST_STATE_CHANGE_SUCCESS && filter->state == GST_STATE_PLAYING) {
477 filter->changed_startime = true;
479 filter->starttime = g_value_get_long (value);
482 GFREE (filter->cur_datafile);
483 GFREE (filter->basename_datafile);
484 filter->basename_datafile = g_value_dup_string (value);
486 if (strlen (filter->basename_datafile) == 0) {
487 filter->cur_datafile = g_strdup (NULL);
490 filter->cur_datafile =
491 g_strdup_printf ("%s-0.%s", filter->basename_datafile,
492 filter->datafile_extension);
493 if (g_strcmp0 (filter->prev_datafile, filter->basename_datafile) != 0) {
494 filter->changed_datafile = TRUE;
495 filter->sent_init_error_msg = FALSE;
496 filter->sent_save_error_msg = FALSE;
497 filter->datafileidx = 0;
498 motion_cells_free_resources (filter->id);
500 filter->changed_datafile = FALSE;
503 GFREE (filter->prev_datafile);
504 filter->prev_datafile = g_strdup (filter->basename_datafile);
506 case PROP_DATAFILE_EXT:
507 GFREE (filter->datafile_extension);
508 filter->datafile_extension = g_value_dup_string (value);
510 case PROP_MOTIONMASKCOORD:
511 strs = g_strsplit (g_value_get_string (value), ",", 255);
512 GFREE (filter->motionmaskcoords);
513 //setting number of regions
514 for (filter->motionmaskcoord_count = 0;
515 strs[filter->motionmaskcoord_count] != NULL;
516 ++filter->motionmaskcoord_count);
517 if (filter->motionmaskcoord_count > 0) {
518 sscanf (strs[0], "%d:%d:%d:%d", &tmpux, &tmpuy, &tmplx, &tmply);
519 if (tmpux > -1 && tmpuy > -1 && tmplx > -1 && tmply > -1) {
520 filter->motionmaskcoords =
521 g_new0 (motionmaskcoordrect, filter->motionmaskcoord_count);
523 for (i = 0; i < filter->motionmaskcoord_count; ++i) {
524 sscanf (strs[i], "%d:%d:%d:%d", &ux, &uy, &lx, &ly);
525 ux = CLAMP (ux, 0, filter->width - 1);
526 uy = CLAMP (uy, 0, filter->height - 1);
527 lx = CLAMP (lx, 0, filter->width - 1);
528 ly = CLAMP (ly, 0, filter->height - 1);
529 filter->motionmaskcoords[i].upper_left_x = ux;
530 filter->motionmaskcoords[i].upper_left_y = uy;
531 filter->motionmaskcoords[i].lower_right_x = lx;
532 filter->motionmaskcoords[i].lower_right_y = ly;
535 filter->motionmaskcoord_count = 0;
545 case PROP_MOTIONMASKCELLSPOS:
546 motionmaskcellsstr = g_strsplit (g_value_get_string (value), ",", 255);
547 GFREE (filter->motionmaskcellsidx);
548 //setting number of regions
549 for (filter->motionmaskcells_count = 0;
550 motionmaskcellsstr[filter->motionmaskcells_count] != NULL;
551 ++filter->motionmaskcells_count);
552 if (filter->motionmaskcells_count > 0) {
553 sscanf (motionmaskcellsstr[0], "%d:%d", &tmpux, &tmpuy);
554 if (tmpux > -1 && tmpuy > -1) {
555 filter->motionmaskcellsidx =
556 g_new0 (motioncellidx, filter->motionmaskcells_count);
557 for (i = 0; i < filter->motionmaskcells_count; ++i) {
558 sscanf (motionmaskcellsstr[i], "%d:%d", &masklinidx, &maskcolidx);
559 filter->motionmaskcellsidx[i].lineidx = masklinidx;
560 filter->motionmaskcellsidx[i].columnidx = maskcolidx;
563 filter->motionmaskcells_count = 0;
566 if (motionmaskcellsstr)
567 g_strfreev (motionmaskcellsstr);
573 case PROP_CELLSCOLOR:
574 colorstr = g_strsplit (g_value_get_string (value), ",", 255);
575 for (cellscolorscnt = 0; colorstr[cellscolorscnt] != NULL;
577 if (cellscolorscnt == 3) {
578 sscanf (colorstr[0], "%d", &r);
579 sscanf (colorstr[1], "%d", &g);
580 sscanf (colorstr[2], "%d", &b);
581 //check right RGB color format
582 r = CLAMP (r, 1, 255);
583 g = CLAMP (g, 1, 255);
584 b = CLAMP (b, 1, 255);
585 filter->motioncellscolor->R_channel_value = r;
586 filter->motioncellscolor->G_channel_value = g;
587 filter->motioncellscolor->B_channel_value = b;
590 g_strfreev (colorstr);
592 case PROP_MOTIONCELLSIDX:
593 motioncellsstr = g_strsplit (g_value_get_string (value), ",", 255);
595 //setting number of regions
596 for (filter->motioncells_count = 0;
597 motioncellsstr[filter->motioncells_count] != NULL;
598 ++filter->motioncells_count);
599 if (filter->motioncells_count > 0) {
600 sscanf (motioncellsstr[0], "%d:%d", &tmpux, &tmpuy);
601 if (tmpux > -1 && tmpuy > -1) {
602 GFREE (filter->motioncellsidx);
604 filter->motioncellsidx =
605 g_new0 (motioncellidx, filter->motioncells_count);
607 for (i = 0; i < filter->motioncells_count; ++i) {
608 sscanf (motioncellsstr[i], "%d:%d", &linidx, &colidx);
609 filter->motioncellsidx[i].lineidx = linidx;
610 filter->motioncellsidx[i].columnidx = colidx;
613 filter->motioncells_count = 0;
617 g_strfreev (motioncellsstr);
623 case PROP_MOTIONCELLTHICKNESS:
624 filter->thickness = g_value_get_int (value);
627 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
630 g_mutex_unlock (filter->propset_mutex);
634 gst_motion_cells_get_property (GObject * object, guint prop_id,
635 GValue * value, GParamSpec * pspec)
637 GstMotioncells *filter = gst_motion_cells (object);
643 g_value_set_int (value, filter->gridx);
646 g_value_set_int (value, filter->gridy);
649 g_value_set_int (value, filter->gap);
651 case PROP_POSTNOMOTION:
652 g_value_set_int (value, filter->postnomotion);
654 case PROP_MINIMUNMOTIONFRAMES:
655 g_value_set_int (value, filter->minimum_motion_frames);
657 case PROP_SENSITIVITY:
658 g_value_set_double (value, filter->sensitivity);
661 g_value_set_double (value, filter->threshold);
664 g_value_set_boolean (value, filter->display);
666 case PROP_POSTALLMOTION:
667 g_value_set_boolean (value, filter->postallmotion);
670 g_value_set_boolean (value, filter->usealpha);
672 case PROP_CALCULATEMOTION:
673 g_value_set_boolean (value, filter->calculate_motion);
676 g_value_set_long (value, filter->starttime);
679 g_value_set_string (value, filter->basename_datafile);
681 case PROP_DATAFILE_EXT:
682 g_value_set_string (value, filter->datafile_extension);
684 case PROP_MOTIONMASKCOORD:
685 str = g_string_new ("");
686 for (i = 0; i < filter->motionmaskcoord_count; ++i) {
687 if (i < filter->motionmaskcoord_count - 1)
688 g_string_append_printf (str, "%d:%d:%d:%d,",
689 filter->motionmaskcoords[i].upper_left_x,
690 filter->motionmaskcoords[i].upper_left_y,
691 filter->motionmaskcoords[i].lower_right_x,
692 filter->motionmaskcoords[i].lower_right_y);
694 g_string_append_printf (str, "%d:%d:%d:%d",
695 filter->motionmaskcoords[i].upper_left_x,
696 filter->motionmaskcoords[i].upper_left_y,
697 filter->motionmaskcoords[i].lower_right_x,
698 filter->motionmaskcoords[i].lower_right_y);
701 g_value_set_string (value, str->str);
702 g_string_free (str, TRUE);
704 case PROP_MOTIONMASKCELLSPOS:
705 str = g_string_new ("");
706 for (i = 0; i < filter->motionmaskcells_count; ++i) {
707 if (i < filter->motionmaskcells_count - 1)
708 g_string_append_printf (str, "%d:%d,",
709 filter->motionmaskcellsidx[i].lineidx,
710 filter->motionmaskcellsidx[i].columnidx);
712 g_string_append_printf (str, "%d:%d",
713 filter->motionmaskcellsidx[i].lineidx,
714 filter->motionmaskcellsidx[i].columnidx);
716 g_value_set_string (value, str->str);
717 g_string_free (str, TRUE);
719 case PROP_CELLSCOLOR:
720 str = g_string_new ("");
722 g_string_printf (str, "%d,%d,%d",
723 filter->motioncellscolor->R_channel_value,
724 filter->motioncellscolor->G_channel_value,
725 filter->motioncellscolor->B_channel_value);
727 g_value_set_string (value, str->str);
728 g_string_free (str, TRUE);
730 case PROP_MOTIONCELLSIDX:
731 str = g_string_new ("");
732 for (i = 0; i < filter->motioncells_count; ++i) {
733 if (i < filter->motioncells_count - 1)
734 g_string_append_printf (str, "%d:%d,",
735 filter->motioncellsidx[i].lineidx,
736 filter->motioncellsidx[i].columnidx);
738 g_string_append_printf (str, "%d:%d",
739 filter->motioncellsidx[i].lineidx,
740 filter->motioncellsidx[i].columnidx);
742 g_value_set_string (value, str->str);
743 g_string_free (str, TRUE);
745 case PROP_MOTIONCELLTHICKNESS:
746 g_value_set_int (value, filter->thickness);
749 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
755 gst_motioncells_update_motion_cells (GstMotioncells * filter)
761 motioncellidx *motioncellsidx;
762 for (i = 0; i < filter->motioncells_count; i++) {
763 if ((filter->gridx <= filter->motioncellsidx[i].columnidx) ||
764 (filter->gridy <= filter->motioncellsidx[i].lineidx)) {
768 newcellscnt = filter->motioncells_count - cellscnt;
769 motioncellsidx = g_new0 (motioncellidx, newcellscnt);
770 for (i = 0; i < filter->motioncells_count; i++) {
771 if ((filter->motioncellsidx[i].lineidx < filter->gridy) &&
772 (filter->motioncellsidx[i].columnidx < filter->gridx)) {
773 motioncellsidx[j].lineidx = filter->motioncellsidx[i].lineidx;
774 motioncellsidx[j].columnidx = filter->motioncellsidx[i].columnidx;
778 GFREE (filter->motioncellsidx);
779 filter->motioncells_count = newcellscnt;
780 filter->motioncellsidx = g_new0 (motioncellidx, filter->motioncells_count);
782 for (i = 0; i < filter->motioncells_count; i++) {
783 filter->motioncellsidx[i].lineidx = motioncellsidx[j].lineidx;
784 filter->motioncellsidx[i].columnidx = motioncellsidx[j].columnidx;
787 GFREE (motioncellsidx);
791 gst_motioncells_update_motion_masks (GstMotioncells * filter)
798 motioncellidx *motionmaskcellsidx;
799 for (i = 0; i < filter->motionmaskcells_count; i++) {
800 if ((filter->gridx <= filter->motionmaskcellsidx[i].columnidx) ||
801 (filter->gridy <= filter->motionmaskcellsidx[i].lineidx)) {
805 newmaskcnt = filter->motionmaskcells_count - maskcnt;
806 motionmaskcellsidx = g_new0 (motioncellidx, newmaskcnt);
807 for (i = 0; i < filter->motionmaskcells_count; i++) {
808 if ((filter->motionmaskcellsidx[i].lineidx < filter->gridy) &&
809 (filter->motionmaskcellsidx[i].columnidx < filter->gridx)) {
810 motionmaskcellsidx[j].lineidx = filter->motionmaskcellsidx[i].lineidx;
811 motionmaskcellsidx[j].columnidx = filter->motionmaskcellsidx[i].columnidx;
815 GFREE (filter->motionmaskcellsidx);
816 filter->motionmaskcells_count = newmaskcnt;
817 filter->motionmaskcellsidx =
818 g_new0 (motioncellidx, filter->motionmaskcells_count);
820 for (i = 0; i < filter->motionmaskcells_count; i++) {
821 filter->motionmaskcellsidx[i].lineidx = motionmaskcellsidx[j].lineidx;
822 filter->motionmaskcellsidx[i].columnidx = motionmaskcellsidx[j].columnidx;
825 GFREE (motionmaskcellsidx);
828 /* GstElement vmethod implementations */
830 /* this function handles the link with other elements */
832 gst_motion_cells_set_caps (GstPad * pad, GstCaps * caps)
834 GstMotioncells *filter;
836 GstStructure *structure;
837 int numerator, denominator;
839 filter = gst_motion_cells (gst_pad_get_parent (pad));
840 structure = gst_caps_get_structure (caps, 0);
841 gst_structure_get_int (structure, "width", &filter->width);
842 gst_structure_get_int (structure, "height", &filter->height);
843 gst_structure_get_fraction (structure, "framerate", &numerator, &denominator);
844 filter->framerate = (double) numerator / (double) denominator;
846 cvReleaseImage (&filter->cvImage);
848 cvCreateImage (cvSize (filter->width, filter->height), IPL_DEPTH_8U, 3);
850 otherpad = (pad == filter->srcpad) ? filter->sinkpad : filter->srcpad;
851 gst_object_unref (filter);
853 return gst_pad_set_caps (otherpad, caps);
857 * this function does the actual processing
860 gst_motion_cells_chain (GstPad * pad, GstBuffer * buf)
863 GstMotioncells *filter;
865 filter = gst_motion_cells (GST_OBJECT_PARENT (pad));
866 if (filter->calculate_motion) {
868 int framerate, gridx, gridy, motionmaskcells_count, motionmaskcoord_count,
869 motioncells_count, i;
870 int thickness, success, motioncellsidxcnt, numberOfCells,
871 motioncellsnumber, cellsOfInterestNumber;
872 int mincellsOfInterestNumber, motiondetect;
874 bool display, changed_datafile, useAlpha;
876 motionmaskcoordrect *motionmaskcoords;
877 motioncellidx *motionmaskcellsidx;
878 cellscolor motioncellscolor;
879 motioncellidx *motioncellsidx;
880 g_mutex_lock (filter->propset_mutex);
881 buf = gst_buffer_make_writable (buf);
882 filter->cvImage->imageData = (char *) GST_BUFFER_DATA (buf);
883 if (filter->firstframe) {
884 setPrevFrame (filter->cvImage, filter->id);
885 filter->firstframe = FALSE;
888 sensitivity = filter->sensitivity;
889 framerate = filter->framerate;
890 gridx = filter->gridx;
891 gridy = filter->gridy;
892 display = filter->display;
893 motionmaskcoord_count = filter->motionmaskcoord_count;
895 g_new0 (motionmaskcoordrect, filter->motionmaskcoord_count);
896 for (i = 0; i < filter->motionmaskcoord_count; i++) { //we need divide 2 because we use gauss pyramid in C++ side
897 motionmaskcoords[i].upper_left_x =
898 filter->motionmaskcoords[i].upper_left_x / 2;
899 motionmaskcoords[i].upper_left_y =
900 filter->motionmaskcoords[i].upper_left_y / 2;
901 motionmaskcoords[i].lower_right_x =
902 filter->motionmaskcoords[i].lower_right_x / 2;
903 motionmaskcoords[i].lower_right_y =
904 filter->motionmaskcoords[i].lower_right_y / 2;
907 motioncellscolor.R_channel_value =
908 filter->motioncellscolor->R_channel_value;
909 motioncellscolor.G_channel_value =
910 filter->motioncellscolor->G_channel_value;
911 motioncellscolor.B_channel_value =
912 filter->motioncellscolor->B_channel_value;
914 if ((filter->changed_gridx || filter->changed_gridy
915 || filter->changed_startime)) {
916 if ((g_strcmp0 (filter->cur_datafile, NULL) != 0)) {
917 GFREE (filter->cur_datafile);
918 filter->datafileidx++;
919 filter->cur_datafile =
920 g_strdup_printf ("%s-%d.%s", filter->basename_datafile,
921 filter->datafileidx, filter->datafile_extension);
922 filter->changed_datafile = TRUE;
923 motion_cells_free_resources (filter->id);
925 if (filter->motioncells_count > 0)
926 gst_motioncells_update_motion_cells (filter);
927 if (filter->motionmaskcells_count > 0)
928 gst_motioncells_update_motion_masks (filter);
929 filter->changed_gridx = FALSE;
930 filter->changed_gridy = FALSE;
931 filter->changed_startime = FALSE;
933 datafile = g_strdup (filter->cur_datafile);
934 filter->cur_buff_timestamp = (GST_BUFFER_TIMESTAMP (buf) / GST_MSECOND);
936 (filter->cur_buff_timestamp - filter->prev_buff_timestamp);
937 starttime = filter->starttime;
938 if (filter->changed_datafile || filter->diff_timestamp < 0)
939 filter->diff_timestamp =
940 (gint64) (GST_BUFFER_TIMESTAMP (buf) / GST_MSECOND);
941 changed_datafile = filter->changed_datafile;
942 motionmaskcells_count = filter->motionmaskcells_count;
943 motionmaskcellsidx = g_new0 (motioncellidx, filter->motionmaskcells_count);
944 for (i = 0; i < filter->motionmaskcells_count; i++) {
945 motionmaskcellsidx[i].lineidx = filter->motionmaskcellsidx[i].lineidx;
946 motionmaskcellsidx[i].columnidx = filter->motionmaskcellsidx[i].columnidx;
948 motioncells_count = filter->motioncells_count;
949 motioncellsidx = g_new0 (motioncellidx, filter->motioncells_count);
950 for (i = 0; i < filter->motioncells_count; i++) {
951 motioncellsidx[i].lineidx = filter->motioncellsidx[i].lineidx;
952 motioncellsidx[i].columnidx = filter->motioncellsidx[i].columnidx;
954 useAlpha = filter->usealpha;
955 thickness = filter->thickness;
957 perform_detection_motion_cells (filter->cvImage, sensitivity, framerate,
959 (gint64) (GST_BUFFER_TIMESTAMP (buf) / GST_MSECOND) -
960 filter->diff_timestamp, display, useAlpha, motionmaskcoord_count,
961 motionmaskcoords, motionmaskcells_count, motionmaskcellsidx,
962 motioncellscolor, motioncells_count, motioncellsidx, starttime,
963 datafile, changed_datafile, thickness, filter->id);
964 if ((success == 1) && (filter->sent_init_error_msg == false)) {
965 char *initfailedreason;
969 initfailedreason = getInitDataFileFailed (filter->id);
970 initerrorcode = getInitErrorCode (filter->id);
971 s = gst_structure_new ("motion", "init_error_code", G_TYPE_INT,
972 initerrorcode, "details", G_TYPE_STRING, initfailedreason, NULL);
973 m = gst_message_new_element (GST_OBJECT (filter), s);
974 gst_element_post_message (GST_ELEMENT (filter), m);
975 filter->sent_init_error_msg = TRUE;
977 if ((success == -1) && (filter->sent_save_error_msg == false)) {
978 char *savefailedreason;
982 savefailedreason = getSaveDataFileFailed (filter->id);
983 saveerrorcode = getSaveErrorCode (filter->id);
984 s = gst_structure_new ("motion", "save_error_code", G_TYPE_INT,
985 saveerrorcode, "details", G_TYPE_STRING, savefailedreason, NULL);
986 m = gst_message_new_element (GST_OBJECT (filter), s);
987 gst_element_post_message (GST_ELEMENT (filter), m);
988 filter->sent_save_error_msg = TRUE;
990 if (success == -2) { //frame dropped
991 filter->prev_buff_timestamp = filter->cur_buff_timestamp;
994 GFREE (motionmaskcoords);
995 GFREE (motionmaskcellsidx);
996 GFREE (motioncellsidx);
997 g_mutex_unlock (filter->propset_mutex);
998 return gst_pad_push (filter->srcpad, buf);
1000 filter->changed_datafile = getChangedDataFile (filter->id);
1001 motioncellsidxcnt = getMotionCellsIdxCnt (filter->id);
1002 numberOfCells = filter->gridx * filter->gridy;
1003 motioncellsnumber = motioncellsidxcnt / MSGLEN;
1004 cellsOfInterestNumber = (filter->motioncells_count > 0) ? //how many cells interest for us
1005 (filter->motioncells_count) : (numberOfCells);
1006 mincellsOfInterestNumber =
1007 floor ((double) cellsOfInterestNumber * filter->threshold);
1008 motiondetect = (motioncellsnumber >= mincellsOfInterestNumber) ? 1 : 0;
1009 if ((motioncellsidxcnt > 0) && (motiondetect == 1)) {
1010 char *detectedmotioncells;
1011 filter->last_motion_timestamp = GST_BUFFER_TIMESTAMP (buf);
1012 detectedmotioncells = getMotionCellsIdx (filter->id);
1013 if (detectedmotioncells) {
1014 filter->consecutive_motion++;
1015 if ((filter->previous_motion == false)
1016 && (filter->consecutive_motion >= filter->minimum_motion_frames)) {
1019 filter->previous_motion = true;
1020 filter->motion_begin_timestamp = GST_BUFFER_TIMESTAMP (buf);
1021 s = gst_structure_new ("motion", "motion_cells_indices",
1022 G_TYPE_STRING, detectedmotioncells, "motion_begin", G_TYPE_UINT64,
1023 filter->motion_begin_timestamp, NULL);
1024 m = gst_message_new_element (GST_OBJECT (filter), s);
1025 gst_element_post_message (GST_ELEMENT (filter), m);
1026 } else if (filter->postallmotion) {
1029 filter->motion_timestamp = GST_BUFFER_TIMESTAMP (buf);
1030 s = gst_structure_new ("motion", "motion_cells_indices",
1031 G_TYPE_STRING, detectedmotioncells, "motion", G_TYPE_UINT64,
1032 filter->motion_timestamp, NULL);
1033 m = gst_message_new_element (GST_OBJECT (filter), s);
1034 gst_element_post_message (GST_ELEMENT (filter), m);
1039 s = gst_structure_new ("motion", "motion_cells_indices", G_TYPE_STRING,
1041 m = gst_message_new_element (GST_OBJECT (filter), s);
1042 gst_element_post_message (GST_ELEMENT (filter), m);
1045 filter->consecutive_motion = 0;
1046 if ((((GST_BUFFER_TIMESTAMP (buf) -
1047 filter->last_motion_timestamp) / 1000000000l) >=
1049 && (filter->last_motion_timestamp > 0)) {
1050 GST_DEBUG ("POST MOTION FINISHED MSG\n");
1051 if (filter->previous_motion) {
1054 filter->previous_motion = false;
1055 s = gst_structure_new ("motion", "motion_finished", G_TYPE_UINT64,
1056 filter->last_motion_timestamp, NULL);
1057 m = gst_message_new_element (GST_OBJECT (filter), s);
1058 gst_element_post_message (GST_ELEMENT (filter), m);
1062 if (filter->postnomotion > 0) {
1063 guint64 last_buf_timestamp = GST_BUFFER_TIMESTAMP (buf) / 1000000000l;
1064 if ((last_buf_timestamp -
1065 (filter->last_motion_timestamp / 1000000000l)) >=
1066 filter->postnomotion) {
1067 GST_DEBUG ("POST NO MOTION MSG\n");
1068 if ((last_buf_timestamp -
1069 (filter->last_nomotion_notified / 1000000000l)) >=
1070 filter->postnomotion) {
1073 filter->last_nomotion_notified = GST_BUFFER_TIMESTAMP (buf);
1074 s = gst_structure_new ("motion", "no_motion", G_TYPE_UINT64,
1075 filter->last_motion_timestamp, NULL);
1076 m = gst_message_new_element (GST_OBJECT (filter), s);
1077 gst_element_post_message (GST_ELEMENT (filter), m);
1081 filter->prev_buff_timestamp = filter->cur_buff_timestamp;
1084 GFREE (motionmaskcoords);
1085 GFREE (motionmaskcellsidx);
1086 GFREE (motioncellsidx);
1088 g_mutex_unlock (filter->propset_mutex);
1091 return gst_pad_push (filter->srcpad, buf);
1094 /* entry point to initialize the plug-in
1095 * initialize the plug-in itself
1096 * register the element factories and other features
1099 gst_motioncells_plugin_init (GstPlugin * plugin)
1101 /* debug category for fltering log messages */
1102 GST_DEBUG_CATEGORY_INIT (gst_motion_cells_debug,
1105 "Performs motion detection on videos, providing detected positions via bus messages");
1107 return gst_element_register (plugin, "motioncells", GST_RANK_NONE,
1108 GST_TYPE_MOTIONCELLS);