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;
222 GstElementClass *gstelement_class;
224 gobject_class = (GObjectClass *) klass;
225 gstelement_class = (GstElementClass *) klass;
226 parent_class = g_type_class_peek_parent (klass);
228 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_motion_cells_finalize);
229 gobject_class->set_property = gst_motion_cells_set_property;
230 gobject_class->get_property = gst_motion_cells_get_property;
232 g_object_class_install_property (gobject_class, PROP_GRID_X,
233 g_param_spec_int ("gridx", "Number of Horizontal Grids",
234 "You can give number of horizontal grid cells.", GRID_MIN, GRID_MAX,
235 GRID_DEF, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
236 g_object_class_install_property (gobject_class, PROP_GRID_Y,
237 g_param_spec_int ("gridy", "Number of Vertical Grids",
238 "You can give number of vertical grid cells.", GRID_MIN, GRID_MAX,
239 GRID_DEF, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
240 g_object_class_install_property (gobject_class, PROP_SENSITIVITY,
241 g_param_spec_double ("sensitivity", "Motion Sensitivity",
242 "You can tunning the element motion sensitivity.", SENSITIVITY_MIN,
243 SENSITIVITY_MAX, SENSITIVITY_DEFAULT,
244 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
245 g_object_class_install_property (gobject_class, PROP_THRESHOLD,
246 g_param_spec_double ("threshold", "Lower bound of motion cells number",
247 "Threshold value for motion, when motion cells number greater sum cells * threshold, we show motion.",
248 THRESHOLD_MIN, THRESHOLD_MAX, THRESHOLD_DEFAULT,
249 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
250 g_object_class_install_property (gobject_class, PROP_GAP,
251 g_param_spec_int ("gap",
252 "Gap is time in second, elapsed time from last motion timestamp. ",
253 "If elapsed time minus form last motion timestamp is greater or equal than gap then we post motion finished bus message. ",
254 GAP_MIN, GAP_MAX, GAP_DEF,
255 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
256 g_object_class_install_property (gobject_class, PROP_POSTNOMOTION,
257 g_param_spec_int ("postnomotion", "POSTNOMOTION",
258 "If non 0 post a no_motion event is posted on the bus if no motion is detected for N seconds",
259 POST_NO_MOTION_MIN, POST_NO_MOTION_MAX, POST_NO_MOTION_DEF,
260 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
261 g_object_class_install_property (gobject_class, PROP_MINIMUNMOTIONFRAMES,
262 g_param_spec_int ("minimummotionframes", "MINIMUN MOTION FRAMES",
263 "Define the minimum number of motion frames that trigger a motion event",
264 MINIMUM_MOTION_FRAMES_MIN, MINIMUM_MOTION_FRAMES_MAX,
265 MINIMUM_MOTION_FRAMES_DEF,
266 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
267 g_object_class_install_property (gobject_class, PROP_DISPLAY,
268 g_param_spec_boolean ("display", "Display",
269 "Motion Cells visible or not on Current Frame", FALSE,
270 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
271 g_object_class_install_property (gobject_class, PROP_POSTALLMOTION,
272 g_param_spec_boolean ("postallmotion", "Post All Motion",
273 "Element post bus msg for every motion frame or just motion start and motion stop",
274 FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
275 g_object_class_install_property (gobject_class, PROP_USEALPHA,
276 g_param_spec_boolean ("usealpha", "Use alpha",
277 "Use or not alpha blending on frames with motion cells", TRUE,
278 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
279 g_object_class_install_property (gobject_class, PROP_DATE,
280 g_param_spec_long ("date", "Motion Cell Date",
281 "Current Date in milliseconds", DATE_MIN, DATE_MAX, DATE_DEF,
282 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
283 g_object_class_install_property (gobject_class, PROP_DATAFILE,
284 g_param_spec_string ("datafile", "DataFile",
285 "Location of motioncells data file (empty string means no saving)",
286 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
287 g_object_class_install_property (gobject_class, PROP_DATAFILE_EXT,
288 g_param_spec_string ("datafileextension", "DataFile Extension",
289 "Extension of datafile", DEF_DATAFILEEXT,
290 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
291 g_object_class_install_property (gobject_class, PROP_MOTIONMASKCOORD,
292 g_param_spec_string ("motionmaskcoords", "Motion Mask with Coordinates",
293 "The upper left x, y and lower right x, y coordinates separated with \":\", "
294 "describe a region. Regions separated with \",\"", NULL,
295 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
296 g_object_class_install_property (gobject_class, PROP_MOTIONMASKCELLSPOS,
297 g_param_spec_string ("motionmaskcellspos",
298 "Motion Mask with Cells Position",
299 "The line and column idx separated with \":\" what cells want we mask-out, "
300 "describe a cell. Cells separated with \",\"", NULL,
301 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
302 g_object_class_install_property (gobject_class, PROP_CELLSCOLOR,
303 g_param_spec_string ("cellscolor", "Color of Motion Cells",
304 "The color of motion cells separated with \",\"", "255,255,0",
305 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
306 g_object_class_install_property (gobject_class, PROP_MOTIONCELLSIDX,
307 g_param_spec_string ("motioncellsidx", "Motion Cells Of Interest(MOCI)",
308 "The line and column idx separated with \":\", "
309 "describe a cell. Cells separated with \",\"", NULL,
310 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
311 g_object_class_install_property (gobject_class, PROP_CALCULATEMOTION,
312 g_param_spec_boolean ("calculatemotion", "Calculate Motion",
313 "If needs calculate motion on frame you need this property setting true otherwise false",
314 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
315 g_object_class_install_property (gobject_class, PROP_MOTIONCELLTHICKNESS,
316 g_param_spec_int ("motioncellthickness", "Motion Cell Thickness",
317 "Motion Cell Border Thickness, if it's -1 then motion cell will be fill",
318 THICKNESS_MIN, THICKNESS_MAX, THICKNESS_DEF,
319 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
322 /* initialize the new element
323 * instantiate pads and add them to element
324 * set pad callback functions
325 * initialize instance structure
328 gst_motion_cells_init (GstMotioncells * filter, GstMotioncellsClass * gclass)
330 filter->propset_mutex = g_mutex_new ();
331 filter->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
332 gst_pad_set_setcaps_function (filter->sinkpad,
333 GST_DEBUG_FUNCPTR (gst_motion_cells_set_caps));
334 gst_pad_set_getcaps_function (filter->sinkpad,
335 GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
336 gst_pad_set_chain_function (filter->sinkpad,
337 GST_DEBUG_FUNCPTR (gst_motion_cells_chain));
339 filter->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
340 gst_pad_set_getcaps_function (filter->srcpad,
341 GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
343 gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
344 gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
346 filter->display = TRUE;
347 filter->calculate_motion = TRUE;
349 filter->prevgridx = 0;
350 filter->prevgridy = 0;
351 filter->gridx = GRID_DEF;
352 filter->gridy = GRID_DEF;
353 filter->gap = GAP_DEF;
354 filter->postnomotion = POST_NO_MOTION_DEF;
355 filter->minimum_motion_frames = MINIMUM_MOTION_FRAMES_DEF;
357 filter->prev_datafile = g_strdup (NULL);
358 filter->cur_datafile = g_strdup (NULL);
359 filter->basename_datafile = g_strdup (NULL);
360 filter->datafile_extension = g_strdup (DEF_DATAFILEEXT);
361 filter->sensitivity = SENSITIVITY_DEFAULT;
362 filter->threshold = THRESHOLD_DEFAULT;
364 filter->motionmaskcoord_count = 0;
365 filter->motionmaskcoords = NULL;
366 filter->motionmaskcells_count = 0;
367 filter->motionmaskcellsidx = NULL;
368 filter->motioncellscolor = g_new0 (cellscolor, 1);
369 filter->motioncellscolor->R_channel_value = 255;
370 filter->motioncellscolor->G_channel_value = 255;
371 filter->motioncellscolor->B_channel_value = 0;
372 filter->motioncellsidx = NULL;
373 filter->motioncells_count = 0;
374 filter->motion_begin_timestamp = 0;
375 filter->last_motion_timestamp = 0;
376 filter->last_nomotion_notified = 0;
377 filter->consecutive_motion = 0;
378 filter->motion_timestamp = 0;
379 filter->prev_buff_timestamp = 0;
380 filter->cur_buff_timestamp = 0;
381 filter->diff_timestamp = -1;
382 gettimeofday (&filter->tv, NULL);
383 filter->starttime = 1000 * filter->tv.tv_sec;
384 filter->previous_motion = false;
385 filter->changed_datafile = false;
386 filter->postallmotion = false;
387 filter->usealpha = true;
388 filter->firstdatafile = false;
389 filter->firstgridx = true;
390 filter->firstgridy = true;
391 filter->changed_gridx = false;
392 filter->changed_gridy = false;
393 filter->firstframe = true;
394 filter->changed_startime = false;
395 filter->sent_init_error_msg = false;
396 filter->sent_save_error_msg = false;
397 filter->thickness = THICKNESS_DEF;
399 filter->datafileidx = 0;
400 g_mutex_lock (filter->propset_mutex);
401 filter->id = instanceCounter;
402 motion_cells_init ();
403 g_mutex_unlock (filter->propset_mutex);
408 gst_motion_cells_set_property (GObject * object, guint prop_id,
409 const GValue * value, GParamSpec * pspec)
411 GstMotioncells *filter = gst_motion_cells (object);
412 //variables for overlay regions setup
413 gchar **strs, **colorstr, **motioncellsstr, **motionmaskcellsstr;
414 int i, ux, uy, lx, ly;
416 int cellscolorscnt = 0;
417 int linidx, colidx, masklinidx, maskcolidx;
422 GstStateChangeReturn ret;
424 g_mutex_lock (filter->propset_mutex);
427 ret = gst_element_get_state (GST_ELEMENT (filter),
428 &filter->state, NULL, 250 * GST_NSECOND);
429 filter->gridx = g_value_get_int (value);
430 if (filter->prevgridx != filter->gridx
431 && ret == GST_STATE_CHANGE_SUCCESS
432 && filter->state == GST_STATE_PLAYING) {
433 filter->changed_gridx = true;
435 filter->prevgridx = filter->gridx;
438 ret = gst_element_get_state (GST_ELEMENT (filter),
439 &filter->state, NULL, 250 * GST_NSECOND);
440 filter->gridy = g_value_get_int (value);
441 if (filter->prevgridy != filter->gridy
442 && ret == GST_STATE_CHANGE_SUCCESS
443 && filter->state == GST_STATE_PLAYING) {
444 filter->changed_gridy = true;
446 filter->prevgridy = filter->gridy;
449 filter->gap = g_value_get_int (value);
451 case PROP_POSTNOMOTION:
452 filter->postnomotion = g_value_get_int (value);
454 case PROP_MINIMUNMOTIONFRAMES:
455 filter->minimum_motion_frames = g_value_get_int (value);
457 case PROP_SENSITIVITY:
458 filter->sensitivity = g_value_get_double (value);
461 filter->threshold = g_value_get_double (value);
464 filter->display = g_value_get_boolean (value);
466 case PROP_POSTALLMOTION:
467 filter->postallmotion = g_value_get_boolean (value);
470 filter->usealpha = g_value_get_boolean (value);
472 case PROP_CALCULATEMOTION:
473 filter->calculate_motion = g_value_get_boolean (value);
476 ret = gst_element_get_state (GST_ELEMENT (filter),
477 &filter->state, NULL, 250 * GST_NSECOND);
478 if (ret == GST_STATE_CHANGE_SUCCESS && filter->state == GST_STATE_PLAYING) {
479 filter->changed_startime = true;
481 filter->starttime = g_value_get_long (value);
484 GFREE (filter->cur_datafile);
485 GFREE (filter->basename_datafile);
486 filter->basename_datafile = g_value_dup_string (value);
488 if (strlen (filter->basename_datafile) == 0) {
489 filter->cur_datafile = g_strdup (NULL);
492 filter->cur_datafile =
493 g_strdup_printf ("%s-0.%s", filter->basename_datafile,
494 filter->datafile_extension);
495 if (g_strcmp0 (filter->prev_datafile, filter->basename_datafile) != 0) {
496 filter->changed_datafile = TRUE;
497 filter->sent_init_error_msg = FALSE;
498 filter->sent_save_error_msg = FALSE;
499 filter->datafileidx = 0;
500 motion_cells_free_resources (filter->id);
502 filter->changed_datafile = FALSE;
505 GFREE (filter->prev_datafile);
506 filter->prev_datafile = g_strdup (filter->basename_datafile);
508 case PROP_DATAFILE_EXT:
509 GFREE (filter->datafile_extension);
510 filter->datafile_extension = g_value_dup_string (value);
512 case PROP_MOTIONMASKCOORD:
513 strs = g_strsplit (g_value_get_string (value), ",", 255);
514 GFREE (filter->motionmaskcoords);
515 //setting number of regions
516 for (filter->motionmaskcoord_count = 0;
517 strs[filter->motionmaskcoord_count] != NULL;
518 ++filter->motionmaskcoord_count);
519 if (filter->motionmaskcoord_count > 0) {
520 sscanf (strs[0], "%d:%d:%d:%d", &tmpux, &tmpuy, &tmplx, &tmply);
521 if (tmpux > -1 && tmpuy > -1 && tmplx > -1 && tmply > -1) {
522 filter->motionmaskcoords =
523 g_new0 (motionmaskcoordrect, filter->motionmaskcoord_count);
525 for (i = 0; i < filter->motionmaskcoord_count; ++i) {
526 sscanf (strs[i], "%d:%d:%d:%d", &ux, &uy, &lx, &ly);
527 ux = CLAMP (ux, 0, filter->width - 1);
528 uy = CLAMP (uy, 0, filter->height - 1);
529 lx = CLAMP (lx, 0, filter->width - 1);
530 ly = CLAMP (ly, 0, filter->height - 1);
531 filter->motionmaskcoords[i].upper_left_x = ux;
532 filter->motionmaskcoords[i].upper_left_y = uy;
533 filter->motionmaskcoords[i].lower_right_x = lx;
534 filter->motionmaskcoords[i].lower_right_y = ly;
537 filter->motionmaskcoord_count = 0;
547 case PROP_MOTIONMASKCELLSPOS:
548 motionmaskcellsstr = g_strsplit (g_value_get_string (value), ",", 255);
549 GFREE (filter->motionmaskcellsidx);
550 //setting number of regions
551 for (filter->motionmaskcells_count = 0;
552 motionmaskcellsstr[filter->motionmaskcells_count] != NULL;
553 ++filter->motionmaskcells_count);
554 if (filter->motionmaskcells_count > 0) {
555 sscanf (motionmaskcellsstr[0], "%d:%d", &tmpux, &tmpuy);
556 if (tmpux > -1 && tmpuy > -1) {
557 filter->motionmaskcellsidx =
558 g_new0 (motioncellidx, filter->motionmaskcells_count);
559 for (i = 0; i < filter->motionmaskcells_count; ++i) {
560 sscanf (motionmaskcellsstr[i], "%d:%d", &masklinidx, &maskcolidx);
561 filter->motionmaskcellsidx[i].lineidx = masklinidx;
562 filter->motionmaskcellsidx[i].columnidx = maskcolidx;
565 filter->motionmaskcells_count = 0;
568 if (motionmaskcellsstr)
569 g_strfreev (motionmaskcellsstr);
575 case PROP_CELLSCOLOR:
576 colorstr = g_strsplit (g_value_get_string (value), ",", 255);
577 for (cellscolorscnt = 0; colorstr[cellscolorscnt] != NULL;
579 if (cellscolorscnt == 3) {
580 sscanf (colorstr[0], "%d", &r);
581 sscanf (colorstr[1], "%d", &g);
582 sscanf (colorstr[2], "%d", &b);
583 //check right RGB color format
584 r = CLAMP (r, 1, 255);
585 g = CLAMP (g, 1, 255);
586 b = CLAMP (b, 1, 255);
587 filter->motioncellscolor->R_channel_value = r;
588 filter->motioncellscolor->G_channel_value = g;
589 filter->motioncellscolor->B_channel_value = b;
592 g_strfreev (colorstr);
594 case PROP_MOTIONCELLSIDX:
595 motioncellsstr = g_strsplit (g_value_get_string (value), ",", 255);
597 //setting number of regions
598 for (filter->motioncells_count = 0;
599 motioncellsstr[filter->motioncells_count] != NULL;
600 ++filter->motioncells_count);
601 if (filter->motioncells_count > 0) {
602 sscanf (motioncellsstr[0], "%d:%d", &tmpux, &tmpuy);
603 if (tmpux > -1 && tmpuy > -1) {
604 GFREE (filter->motioncellsidx);
606 filter->motioncellsidx =
607 g_new0 (motioncellidx, filter->motioncells_count);
609 for (i = 0; i < filter->motioncells_count; ++i) {
610 sscanf (motioncellsstr[i], "%d:%d", &linidx, &colidx);
611 filter->motioncellsidx[i].lineidx = linidx;
612 filter->motioncellsidx[i].columnidx = colidx;
615 filter->motioncells_count = 0;
619 g_strfreev (motioncellsstr);
625 case PROP_MOTIONCELLTHICKNESS:
626 filter->thickness = g_value_get_int (value);
629 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
632 g_mutex_unlock (filter->propset_mutex);
636 gst_motion_cells_get_property (GObject * object, guint prop_id,
637 GValue * value, GParamSpec * pspec)
639 GstMotioncells *filter = gst_motion_cells (object);
645 g_value_set_int (value, filter->gridx);
648 g_value_set_int (value, filter->gridy);
651 g_value_set_int (value, filter->gap);
653 case PROP_POSTNOMOTION:
654 g_value_set_int (value, filter->postnomotion);
656 case PROP_MINIMUNMOTIONFRAMES:
657 g_value_set_int (value, filter->minimum_motion_frames);
659 case PROP_SENSITIVITY:
660 g_value_set_double (value, filter->sensitivity);
663 g_value_set_double (value, filter->threshold);
666 g_value_set_boolean (value, filter->display);
668 case PROP_POSTALLMOTION:
669 g_value_set_boolean (value, filter->postallmotion);
672 g_value_set_boolean (value, filter->usealpha);
674 case PROP_CALCULATEMOTION:
675 g_value_set_boolean (value, filter->calculate_motion);
678 g_value_set_long (value, filter->starttime);
681 g_value_set_string (value, filter->basename_datafile);
683 case PROP_DATAFILE_EXT:
684 g_value_set_string (value, filter->datafile_extension);
686 case PROP_MOTIONMASKCOORD:
687 str = g_string_new ("");
688 for (i = 0; i < filter->motionmaskcoord_count; ++i) {
689 if (i < filter->motionmaskcoord_count - 1)
690 g_string_append_printf (str, "%d:%d:%d:%d,",
691 filter->motionmaskcoords[i].upper_left_x,
692 filter->motionmaskcoords[i].upper_left_y,
693 filter->motionmaskcoords[i].lower_right_x,
694 filter->motionmaskcoords[i].lower_right_y);
696 g_string_append_printf (str, "%d:%d:%d:%d",
697 filter->motionmaskcoords[i].upper_left_x,
698 filter->motionmaskcoords[i].upper_left_y,
699 filter->motionmaskcoords[i].lower_right_x,
700 filter->motionmaskcoords[i].lower_right_y);
703 g_value_set_string (value, str->str);
704 g_string_free (str, TRUE);
706 case PROP_MOTIONMASKCELLSPOS:
707 str = g_string_new ("");
708 for (i = 0; i < filter->motionmaskcells_count; ++i) {
709 if (i < filter->motionmaskcells_count - 1)
710 g_string_append_printf (str, "%d:%d,",
711 filter->motionmaskcellsidx[i].lineidx,
712 filter->motionmaskcellsidx[i].columnidx);
714 g_string_append_printf (str, "%d:%d",
715 filter->motionmaskcellsidx[i].lineidx,
716 filter->motionmaskcellsidx[i].columnidx);
718 g_value_set_string (value, str->str);
719 g_string_free (str, TRUE);
721 case PROP_CELLSCOLOR:
722 str = g_string_new ("");
724 g_string_printf (str, "%d,%d,%d",
725 filter->motioncellscolor->R_channel_value,
726 filter->motioncellscolor->G_channel_value,
727 filter->motioncellscolor->B_channel_value);
729 g_value_set_string (value, str->str);
730 g_string_free (str, TRUE);
732 case PROP_MOTIONCELLSIDX:
733 str = g_string_new ("");
734 for (i = 0; i < filter->motioncells_count; ++i) {
735 if (i < filter->motioncells_count - 1)
736 g_string_append_printf (str, "%d:%d,",
737 filter->motioncellsidx[i].lineidx,
738 filter->motioncellsidx[i].columnidx);
740 g_string_append_printf (str, "%d:%d",
741 filter->motioncellsidx[i].lineidx,
742 filter->motioncellsidx[i].columnidx);
744 g_value_set_string (value, str->str);
745 g_string_free (str, TRUE);
747 case PROP_MOTIONCELLTHICKNESS:
748 g_value_set_int (value, filter->thickness);
751 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
757 gst_motioncells_update_motion_cells (GstMotioncells * filter)
763 motioncellidx *motioncellsidx;
764 for (i = 0; i < filter->motioncells_count; i++) {
765 if ((filter->gridx <= filter->motioncellsidx[i].columnidx) ||
766 (filter->gridy <= filter->motioncellsidx[i].lineidx)) {
770 newcellscnt = filter->motioncells_count - cellscnt;
771 motioncellsidx = g_new0 (motioncellidx, newcellscnt);
772 for (i = 0; i < filter->motioncells_count; i++) {
773 if ((filter->motioncellsidx[i].lineidx < filter->gridy) &&
774 (filter->motioncellsidx[i].columnidx < filter->gridx)) {
775 motioncellsidx[j].lineidx = filter->motioncellsidx[i].lineidx;
776 motioncellsidx[j].columnidx = filter->motioncellsidx[i].columnidx;
780 GFREE (filter->motioncellsidx);
781 filter->motioncells_count = newcellscnt;
782 filter->motioncellsidx = g_new0 (motioncellidx, filter->motioncells_count);
784 for (i = 0; i < filter->motioncells_count; i++) {
785 filter->motioncellsidx[i].lineidx = motioncellsidx[j].lineidx;
786 filter->motioncellsidx[i].columnidx = motioncellsidx[j].columnidx;
789 GFREE (motioncellsidx);
793 gst_motioncells_update_motion_masks (GstMotioncells * filter)
800 motioncellidx *motionmaskcellsidx;
801 for (i = 0; i < filter->motionmaskcells_count; i++) {
802 if ((filter->gridx <= filter->motionmaskcellsidx[i].columnidx) ||
803 (filter->gridy <= filter->motionmaskcellsidx[i].lineidx)) {
807 newmaskcnt = filter->motionmaskcells_count - maskcnt;
808 motionmaskcellsidx = g_new0 (motioncellidx, newmaskcnt);
809 for (i = 0; i < filter->motionmaskcells_count; i++) {
810 if ((filter->motionmaskcellsidx[i].lineidx < filter->gridy) &&
811 (filter->motionmaskcellsidx[i].columnidx < filter->gridx)) {
812 motionmaskcellsidx[j].lineidx = filter->motionmaskcellsidx[i].lineidx;
813 motionmaskcellsidx[j].columnidx = filter->motionmaskcellsidx[i].columnidx;
817 GFREE (filter->motionmaskcellsidx);
818 filter->motionmaskcells_count = newmaskcnt;
819 filter->motionmaskcellsidx =
820 g_new0 (motioncellidx, filter->motionmaskcells_count);
822 for (i = 0; i < filter->motionmaskcells_count; i++) {
823 filter->motionmaskcellsidx[i].lineidx = motionmaskcellsidx[j].lineidx;
824 filter->motionmaskcellsidx[i].columnidx = motionmaskcellsidx[j].columnidx;
827 GFREE (motionmaskcellsidx);
830 /* GstElement vmethod implementations */
832 /* this function handles the link with other elements */
834 gst_motion_cells_set_caps (GstPad * pad, GstCaps * caps)
836 GstMotioncells *filter;
838 GstStructure *structure;
839 int numerator, denominator;
841 filter = gst_motion_cells (gst_pad_get_parent (pad));
842 structure = gst_caps_get_structure (caps, 0);
843 gst_structure_get_int (structure, "width", &filter->width);
844 gst_structure_get_int (structure, "height", &filter->height);
845 gst_structure_get_fraction (structure, "framerate", &numerator, &denominator);
846 filter->framerate = (double) numerator / (double) denominator;
848 cvReleaseImage (&filter->cvImage);
850 cvCreateImage (cvSize (filter->width, filter->height), IPL_DEPTH_8U, 3);
852 otherpad = (pad == filter->srcpad) ? filter->sinkpad : filter->srcpad;
853 gst_object_unref (filter);
855 return gst_pad_set_caps (otherpad, caps);
859 * this function does the actual processing
862 gst_motion_cells_chain (GstPad * pad, GstBuffer * buf)
865 GstMotioncells *filter;
867 filter = gst_motion_cells (GST_OBJECT_PARENT (pad));
868 if (filter->calculate_motion) {
870 int framerate, gridx, gridy, motionmaskcells_count, motionmaskcoord_count,
871 motioncells_count, i;
872 int thickness, success, motioncellsidxcnt, numberOfCells,
873 motioncellsnumber, cellsOfInterestNumber;
874 int mincellsOfInterestNumber, motiondetect;
876 bool display, changed_datafile, useAlpha;
878 motionmaskcoordrect *motionmaskcoords;
879 motioncellidx *motionmaskcellsidx;
880 cellscolor motioncellscolor;
881 motioncellidx *motioncellsidx;
882 g_mutex_lock (filter->propset_mutex);
883 buf = gst_buffer_make_writable (buf);
884 filter->cvImage->imageData = (char *) GST_BUFFER_DATA (buf);
885 if (filter->firstframe) {
886 setPrevFrame (filter->cvImage, filter->id);
887 filter->firstframe = FALSE;
890 sensitivity = filter->sensitivity;
891 framerate = filter->framerate;
892 gridx = filter->gridx;
893 gridy = filter->gridy;
894 display = filter->display;
895 motionmaskcoord_count = filter->motionmaskcoord_count;
897 g_new0 (motionmaskcoordrect, filter->motionmaskcoord_count);
898 for (i = 0; i < filter->motionmaskcoord_count; i++) { //we need divide 2 because we use gauss pyramid in C++ side
899 motionmaskcoords[i].upper_left_x =
900 filter->motionmaskcoords[i].upper_left_x / 2;
901 motionmaskcoords[i].upper_left_y =
902 filter->motionmaskcoords[i].upper_left_y / 2;
903 motionmaskcoords[i].lower_right_x =
904 filter->motionmaskcoords[i].lower_right_x / 2;
905 motionmaskcoords[i].lower_right_y =
906 filter->motionmaskcoords[i].lower_right_y / 2;
909 motioncellscolor.R_channel_value =
910 filter->motioncellscolor->R_channel_value;
911 motioncellscolor.G_channel_value =
912 filter->motioncellscolor->G_channel_value;
913 motioncellscolor.B_channel_value =
914 filter->motioncellscolor->B_channel_value;
916 if ((filter->changed_gridx || filter->changed_gridy
917 || filter->changed_startime)) {
918 if ((g_strcmp0 (filter->cur_datafile, NULL) != 0)) {
919 GFREE (filter->cur_datafile);
920 filter->datafileidx++;
921 filter->cur_datafile =
922 g_strdup_printf ("%s-%d.%s", filter->basename_datafile,
923 filter->datafileidx, filter->datafile_extension);
924 filter->changed_datafile = TRUE;
925 motion_cells_free_resources (filter->id);
927 if (filter->motioncells_count > 0)
928 gst_motioncells_update_motion_cells (filter);
929 if (filter->motionmaskcells_count > 0)
930 gst_motioncells_update_motion_masks (filter);
931 filter->changed_gridx = FALSE;
932 filter->changed_gridy = FALSE;
933 filter->changed_startime = FALSE;
935 datafile = g_strdup (filter->cur_datafile);
936 filter->cur_buff_timestamp = (GST_BUFFER_TIMESTAMP (buf) / GST_MSECOND);
938 (filter->cur_buff_timestamp - filter->prev_buff_timestamp);
939 starttime = filter->starttime;
940 if (filter->changed_datafile || filter->diff_timestamp < 0)
941 filter->diff_timestamp =
942 (gint64) (GST_BUFFER_TIMESTAMP (buf) / GST_MSECOND);
943 changed_datafile = filter->changed_datafile;
944 motionmaskcells_count = filter->motionmaskcells_count;
945 motionmaskcellsidx = g_new0 (motioncellidx, filter->motionmaskcells_count);
946 for (i = 0; i < filter->motionmaskcells_count; i++) {
947 motionmaskcellsidx[i].lineidx = filter->motionmaskcellsidx[i].lineidx;
948 motionmaskcellsidx[i].columnidx = filter->motionmaskcellsidx[i].columnidx;
950 motioncells_count = filter->motioncells_count;
951 motioncellsidx = g_new0 (motioncellidx, filter->motioncells_count);
952 for (i = 0; i < filter->motioncells_count; i++) {
953 motioncellsidx[i].lineidx = filter->motioncellsidx[i].lineidx;
954 motioncellsidx[i].columnidx = filter->motioncellsidx[i].columnidx;
956 useAlpha = filter->usealpha;
957 thickness = filter->thickness;
959 perform_detection_motion_cells (filter->cvImage, sensitivity, framerate,
961 (gint64) (GST_BUFFER_TIMESTAMP (buf) / GST_MSECOND) -
962 filter->diff_timestamp, display, useAlpha, motionmaskcoord_count,
963 motionmaskcoords, motionmaskcells_count, motionmaskcellsidx,
964 motioncellscolor, motioncells_count, motioncellsidx, starttime,
965 datafile, changed_datafile, thickness, filter->id);
966 if ((success == 1) && (filter->sent_init_error_msg == false)) {
967 char *initfailedreason;
971 initfailedreason = getInitDataFileFailed (filter->id);
972 initerrorcode = getInitErrorCode (filter->id);
973 s = gst_structure_new ("motion", "init_error_code", G_TYPE_INT,
974 initerrorcode, "details", G_TYPE_STRING, initfailedreason, NULL);
975 m = gst_message_new_element (GST_OBJECT (filter), s);
976 gst_element_post_message (GST_ELEMENT (filter), m);
977 filter->sent_init_error_msg = TRUE;
979 if ((success == -1) && (filter->sent_save_error_msg == false)) {
980 char *savefailedreason;
984 savefailedreason = getSaveDataFileFailed (filter->id);
985 saveerrorcode = getSaveErrorCode (filter->id);
986 s = gst_structure_new ("motion", "save_error_code", G_TYPE_INT,
987 saveerrorcode, "details", G_TYPE_STRING, savefailedreason, NULL);
988 m = gst_message_new_element (GST_OBJECT (filter), s);
989 gst_element_post_message (GST_ELEMENT (filter), m);
990 filter->sent_save_error_msg = TRUE;
992 if (success == -2) { //frame dropped
993 filter->prev_buff_timestamp = filter->cur_buff_timestamp;
996 GFREE (motionmaskcoords);
997 GFREE (motionmaskcellsidx);
998 GFREE (motioncellsidx);
999 g_mutex_unlock (filter->propset_mutex);
1000 return gst_pad_push (filter->srcpad, buf);
1002 filter->changed_datafile = getChangedDataFile (filter->id);
1003 motioncellsidxcnt = getMotionCellsIdxCnt (filter->id);
1004 numberOfCells = filter->gridx * filter->gridy;
1005 motioncellsnumber = motioncellsidxcnt / MSGLEN;
1006 cellsOfInterestNumber = (filter->motioncells_count > 0) ? //how many cells interest for us
1007 (filter->motioncells_count) : (numberOfCells);
1008 mincellsOfInterestNumber =
1009 floor ((double) cellsOfInterestNumber * filter->threshold);
1010 motiondetect = (motioncellsnumber >= mincellsOfInterestNumber) ? 1 : 0;
1011 if ((motioncellsidxcnt > 0) && (motiondetect == 1)) {
1012 char *detectedmotioncells;
1013 filter->last_motion_timestamp = GST_BUFFER_TIMESTAMP (buf);
1014 detectedmotioncells = getMotionCellsIdx (filter->id);
1015 if (detectedmotioncells) {
1016 filter->consecutive_motion++;
1017 if ((filter->previous_motion == false)
1018 && (filter->consecutive_motion >= filter->minimum_motion_frames)) {
1021 filter->previous_motion = true;
1022 filter->motion_begin_timestamp = GST_BUFFER_TIMESTAMP (buf);
1023 s = gst_structure_new ("motion", "motion_cells_indices",
1024 G_TYPE_STRING, detectedmotioncells, "motion_begin", G_TYPE_UINT64,
1025 filter->motion_begin_timestamp, NULL);
1026 m = gst_message_new_element (GST_OBJECT (filter), s);
1027 gst_element_post_message (GST_ELEMENT (filter), m);
1028 } else if (filter->postallmotion) {
1031 filter->motion_timestamp = GST_BUFFER_TIMESTAMP (buf);
1032 s = gst_structure_new ("motion", "motion_cells_indices",
1033 G_TYPE_STRING, detectedmotioncells, "motion", G_TYPE_UINT64,
1034 filter->motion_timestamp, NULL);
1035 m = gst_message_new_element (GST_OBJECT (filter), s);
1036 gst_element_post_message (GST_ELEMENT (filter), m);
1041 s = gst_structure_new ("motion", "motion_cells_indices", G_TYPE_STRING,
1043 m = gst_message_new_element (GST_OBJECT (filter), s);
1044 gst_element_post_message (GST_ELEMENT (filter), m);
1047 filter->consecutive_motion = 0;
1048 if ((((GST_BUFFER_TIMESTAMP (buf) -
1049 filter->last_motion_timestamp) / 1000000000l) >=
1051 && (filter->last_motion_timestamp > 0)) {
1052 GST_DEBUG ("POST MOTION FINISHED MSG\n");
1053 if (filter->previous_motion) {
1056 filter->previous_motion = false;
1057 s = gst_structure_new ("motion", "motion_finished", G_TYPE_UINT64,
1058 filter->last_motion_timestamp, NULL);
1059 m = gst_message_new_element (GST_OBJECT (filter), s);
1060 gst_element_post_message (GST_ELEMENT (filter), m);
1064 if (filter->postnomotion > 0) {
1065 guint64 last_buf_timestamp = GST_BUFFER_TIMESTAMP (buf) / 1000000000l;
1066 if ((last_buf_timestamp -
1067 (filter->last_motion_timestamp / 1000000000l)) >=
1068 filter->postnomotion) {
1069 GST_DEBUG ("POST NO MOTION MSG\n");
1070 if ((last_buf_timestamp -
1071 (filter->last_nomotion_notified / 1000000000l)) >=
1072 filter->postnomotion) {
1075 filter->last_nomotion_notified = GST_BUFFER_TIMESTAMP (buf);
1076 s = gst_structure_new ("motion", "no_motion", G_TYPE_UINT64,
1077 filter->last_motion_timestamp, NULL);
1078 m = gst_message_new_element (GST_OBJECT (filter), s);
1079 gst_element_post_message (GST_ELEMENT (filter), m);
1083 filter->prev_buff_timestamp = filter->cur_buff_timestamp;
1086 GFREE (motionmaskcoords);
1087 GFREE (motionmaskcellsidx);
1088 GFREE (motioncellsidx);
1090 g_mutex_unlock (filter->propset_mutex);
1093 return gst_pad_push (filter->srcpad, buf);
1096 /* entry point to initialize the plug-in
1097 * initialize the plug-in itself
1098 * register the element factories and other features
1101 gst_motioncells_plugin_init (GstPlugin * plugin)
1103 /* debug category for fltering log messages */
1104 GST_DEBUG_CATEGORY_INIT (gst_motion_cells_debug,
1107 "Performs motion detection on videos, providing detected positions via bus messages");
1109 return gst_element_register (plugin, "motioncells", GST_RANK_NONE,
1110 GST_TYPE_MOTIONCELLS);