1 /*
2 * GStreamer hand gesture detection plugins
3 * Copyright (C) 2012 Andol Li <<andol@andol.info>>
4 * Copyright (C) 2013 Sreerenj Balachandran <sreerenj.balachandran@intel.com>
5 *
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:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
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.
23 *
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
27 * mentioned above:
28 *
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.
33 *
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.
38 *
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.
43 */
44 /**
45 * SECTION:video-filter-handdetect
46 *
47 * FIXME:operates hand gesture detection in video streams and images,
48 * and enable media operation e.g. play/stop/fast forward/back rewind.
49 *
50 * <refsect2>
51 * <title>Example launch line</title>
52 * |[
53 * gst-launch-1.0 autovideosrc ! videoconvert ! "video/x-raw, format=RGB, width=320, height=240" ! \
54 * videoscale ! handdetect ! videoconvert ! xvimagesink
55 * ]|
56 * </refsect2>
57 */
58
59 #ifdef HAVE_CONFIG_H
60 #include <config.h>
61 #endif
62
63 /* element header */
64 #include "gsthanddetect.h"
65 #include <opencv2/imgproc.hpp>
66
67 GST_DEBUG_CATEGORY_STATIC (gst_handdetect_debug);
68 #define GST_CAT_DEFAULT gst_handdetect_debug
69 #if (CV_MAJOR_VERSION < 4)
70 #define CASCADE_DO_CANNY_PRUNING CV_HAAR_DO_CANNY_PRUNING
71 #endif
72
73 /* define HAAR files */
74 #define HAAR_FILE_FIST GST_HAAR_CASCADES_DIR G_DIR_SEPARATOR_S "fist.xml"
75 #define HAAR_FILE_PALM GST_HAAR_CASCADES_DIR G_DIR_SEPARATOR_S "palm.xml"
76
77 using namespace cv;
78 using namespace std;
79 /* Filter signals and args */
80 enum
81 {
82 /* FILL ME */
83 LAST_SIGNAL
84 };
85
86 enum
87 {
88 PROP_0,
89 PROP_DISPLAY,
90 PROP_PROFILE_FIST,
91 PROP_PROFILE_PALM,
92 PROP_ROI_X,
93 PROP_ROI_Y,
94 PROP_ROI_WIDTH,
95 PROP_ROI_HEIGHT
96 };
97
98 /* the capabilities of the inputs and outputs */
99 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
100 GST_PAD_SINK,
101 GST_PAD_ALWAYS,
102 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGB"))
103 );
104 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
105 GST_PAD_SRC,
106 GST_PAD_ALWAYS,
107 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGB"))
108 );
109
110 static void gst_handdetect_set_property (GObject * object, guint prop_id,
111 const GValue * value, GParamSpec * pspec);
112 static void gst_handdetect_get_property (GObject * object, guint prop_id,
113 GValue * value, GParamSpec * pspec);
114 static gboolean gst_handdetect_set_caps (GstOpencvVideoFilter * transform,
115 gint in_width, gint in_height, int in_cv_type,
116 gint out_width, gint out_height, int out_cv_type);
117 static GstFlowReturn gst_handdetect_transform_ip (GstOpencvVideoFilter *
118 transform, GstBuffer * buffer, Mat img);
119
120 static CascadeClassifier *gst_handdetect_load_profile (GstHanddetect * filter,
121 gchar * profile);
122
123 static void gst_handdetect_navigation_interface_init (GstNavigationInterface *
124 iface);
125 static void gst_handdetect_navigation_send_event (GstNavigation * navigation,
126 GstStructure * structure);
127
128 G_DEFINE_TYPE_WITH_CODE (GstHanddetect, gst_handdetect,
129 GST_TYPE_OPENCV_VIDEO_FILTER,
130 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
131 gst_handdetect_navigation_interface_init););
132
133 static void
gst_handdetect_navigation_interface_init(GstNavigationInterface * iface)134 gst_handdetect_navigation_interface_init (GstNavigationInterface * iface)
135 {
136 iface->send_event = gst_handdetect_navigation_send_event;
137 }
138
139 /* FIXME: this function used to parse the region of interests coordinates
140 * sending from applications when the hand gestures reach the defined regions of interests,
141 * at this moment this function is not doing anything significantly
142 * but will be CHANGED when the gstreamer is patched with new hand gesture events
143 */
144 static void
gst_handdetect_navigation_send_event(GstNavigation * navigation,GstStructure * structure)145 gst_handdetect_navigation_send_event (GstNavigation * navigation,
146 GstStructure * structure)
147 {
148 GstHanddetect *filter = GST_HANDDETECT (navigation);
149 GstPad *peer;
150
151 if ((peer = gst_pad_get_peer (GST_BASE_TRANSFORM_CAST (filter)->sinkpad))) {
152 GstEvent *event;
153 event = gst_event_new_navigation (structure);
154 gst_pad_send_event (peer, event);
155 gst_object_unref (peer);
156 }
157 }
158
159 /* clean opencv images and parameters */
160 static void
gst_handdetect_finalize(GObject * obj)161 gst_handdetect_finalize (GObject * obj)
162 {
163 GstHanddetect *filter = GST_HANDDETECT (obj);
164
165 filter->cvGray.release ();
166 g_free (filter->profile_fist);
167 g_free (filter->profile_palm);
168 delete (filter->best_r);
169 if (filter->cvCascade_fist)
170 delete filter->cvCascade_fist;
171 if (filter->cvCascade_palm)
172 delete filter->cvCascade_palm;
173
174 G_OBJECT_CLASS (gst_handdetect_parent_class)->finalize (obj);
175 }
176
177 /* initialise the HANDDETECT class */
178 static void
gst_handdetect_class_init(GstHanddetectClass * klass)179 gst_handdetect_class_init (GstHanddetectClass * klass)
180 {
181 GObjectClass *gobject_class;
182 GstOpencvVideoFilterClass *gstopencvbasefilter_class;
183
184 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
185 gobject_class = (GObjectClass *) klass;
186 gstopencvbasefilter_class = (GstOpencvVideoFilterClass *) klass;
187
188 gstopencvbasefilter_class->cv_trans_ip_func = gst_handdetect_transform_ip;
189 gstopencvbasefilter_class->cv_set_caps = gst_handdetect_set_caps;
190
191 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_handdetect_finalize);
192 gobject_class->set_property = gst_handdetect_set_property;
193 gobject_class->get_property = gst_handdetect_get_property;
194
195 g_object_class_install_property (gobject_class,
196 PROP_DISPLAY,
197 g_param_spec_boolean ("display",
198 "Display",
199 "Whether the detected hands are highlighted in output frame",
200 TRUE, (GParamFlags) G_PARAM_READWRITE)
201 );
202 g_object_class_install_property (gobject_class,
203 PROP_PROFILE_FIST,
204 g_param_spec_string ("profile_fist",
205 "Profile_fist",
206 "Location of HAAR cascade file (fist gesture)",
207 HAAR_FILE_FIST, (GParamFlags) G_PARAM_READWRITE)
208 );
209 g_object_class_install_property (gobject_class,
210 PROP_PROFILE_PALM,
211 g_param_spec_string ("profile_palm",
212 "Profile_palm",
213 "Location of HAAR cascade file (palm gesture)",
214 HAAR_FILE_PALM, (GParamFlags) G_PARAM_READWRITE)
215 );
216 /* FIXME: property name needs fixing */
217 g_object_class_install_property (gobject_class,
218 PROP_ROI_X,
219 g_param_spec_int ("ROI_X",
220 "ROI_X",
221 "X of left-top pointer in region of interest \nGestures in the defined region of interest will emit messages",
222 0, INT_MAX, 0, (GParamFlags) G_PARAM_READWRITE)
223 );
224 /* FIXME: property name needs fixing */
225 g_object_class_install_property (gobject_class,
226 PROP_ROI_Y,
227 g_param_spec_int ("ROI_Y",
228 "ROI_Y",
229 "Y of left-top pointer in region of interest \nGestures in the defined region of interest will emit messages",
230 0, INT_MAX, 0, (GParamFlags) G_PARAM_READWRITE)
231 );
232 /* FIXME: property name needs fixing */
233 g_object_class_install_property (gobject_class,
234 PROP_ROI_WIDTH,
235 g_param_spec_int ("ROI_WIDTH",
236 "ROI_WIDTH",
237 "WIDTH of left-top pointer in region of interest \nGestures in the defined region of interest will emit messages",
238 0, INT_MAX, 0, (GParamFlags) G_PARAM_READWRITE)
239 );
240 /* FIXME: property name needs fixing */
241 g_object_class_install_property (gobject_class,
242 PROP_ROI_HEIGHT,
243 g_param_spec_int ("ROI_HEIGHT",
244 "ROI_HEIGHT",
245 "HEIGHT of left-top pointer in region of interest \nGestures in the defined region of interest will emit messages",
246 0, INT_MAX, 0, (GParamFlags) G_PARAM_READWRITE)
247 );
248
249 gst_element_class_set_static_metadata (element_class,
250 "handdetect",
251 "Filter/Effect/Video",
252 "Performs hand gesture detection on videos, providing detected hand positions via bus message and navigation event, and deals with hand gesture events",
253 "Andol Li <andol@andol.info>");
254
255 gst_element_class_add_static_pad_template (element_class, &src_factory);
256 gst_element_class_add_static_pad_template (element_class, &sink_factory);
257
258 }
259
260 /* initialise the new element
261 * instantiate pads and add them to element
262 * set pad call-back functions
263 * initialise instance structure
264 */
265 static void
gst_handdetect_init(GstHanddetect * filter)266 gst_handdetect_init (GstHanddetect * filter)
267 {
268 const gchar *haar_path;
269
270 haar_path = g_getenv ("GST_HAAR_CASCADES_PATH");
271 if (haar_path) {
272 filter->profile_fist = g_build_filename (haar_path, "fist.xml", NULL);
273 filter->profile_palm = g_build_filename (haar_path, "palm.xml", NULL);
274 } else {
275 filter->profile_fist = g_strdup (HAAR_FILE_FIST);
276 filter->profile_palm = g_strdup (HAAR_FILE_PALM);
277 }
278
279 filter->roi_x = 0;
280 filter->roi_y = 0;
281 filter->roi_width = 0;
282 filter->roi_height = 0;
283 filter->display = TRUE;
284
285 filter->cvCascade_fist =
286 gst_handdetect_load_profile (filter, filter->profile_fist);
287 filter->cvCascade_palm =
288 gst_handdetect_load_profile (filter, filter->profile_palm);
289
290 gst_opencv_video_filter_set_in_place (GST_OPENCV_VIDEO_FILTER_CAST (filter),
291 TRUE);
292 }
293
294 static void
gst_handdetect_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)295 gst_handdetect_set_property (GObject * object, guint prop_id,
296 const GValue * value, GParamSpec * pspec)
297 {
298 GstHanddetect *filter = GST_HANDDETECT (object);
299
300 switch (prop_id) {
301 case PROP_PROFILE_FIST:
302 g_free (filter->profile_fist);
303 if (filter->cvCascade_fist)
304 delete filter->cvCascade_fist;
305 filter->profile_fist = g_value_dup_string (value);
306 filter->cvCascade_fist =
307 gst_handdetect_load_profile (filter, filter->profile_fist);
308 break;
309 case PROP_PROFILE_PALM:
310 g_free (filter->profile_palm);
311 if (filter->cvCascade_palm)
312 delete filter->cvCascade_palm;
313 filter->profile_palm = g_value_dup_string (value);
314 filter->cvCascade_palm =
315 gst_handdetect_load_profile (filter, filter->profile_palm);
316 break;
317 case PROP_DISPLAY:
318 filter->display = g_value_get_boolean (value);
319 break;
320 case PROP_ROI_X:
321 filter->roi_x = g_value_get_int (value);
322 break;
323 case PROP_ROI_Y:
324 filter->roi_y = g_value_get_int (value);
325 break;
326 case PROP_ROI_WIDTH:
327 filter->roi_width = g_value_get_int (value);
328 break;
329 case PROP_ROI_HEIGHT:
330 filter->roi_height = g_value_get_int (value);
331 break;
332 default:
333 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
334 break;
335 }
336 }
337
338 static void
gst_handdetect_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)339 gst_handdetect_get_property (GObject * object, guint prop_id, GValue * value,
340 GParamSpec * pspec)
341 {
342 GstHanddetect *filter = GST_HANDDETECT (object);
343
344 switch (prop_id) {
345 case PROP_DISPLAY:
346 g_value_set_boolean (value, filter->display);
347 break;
348 case PROP_PROFILE_FIST:
349 g_value_set_string (value, filter->profile_fist);
350 break;
351 case PROP_PROFILE_PALM:
352 g_value_set_string (value, filter->profile_palm);
353 break;
354 case PROP_ROI_X:
355 g_value_set_int (value, filter->roi_x);
356 break;
357 case PROP_ROI_Y:
358 g_value_set_int (value, filter->roi_y);
359 break;
360 case PROP_ROI_WIDTH:
361 g_value_set_int (value, filter->roi_width);
362 break;
363 case PROP_ROI_HEIGHT:
364 g_value_set_int (value, filter->roi_height);
365 break;
366 default:
367 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
368 break;
369 }
370 }
371
372 /* GstElement vmethod implementations */
373 /* this function handles the link with other elements */
374 static gboolean
gst_handdetect_set_caps(GstOpencvVideoFilter * transform,gint in_width,gint in_height,int in_cv_type,gint out_width,gint out_height,int out_cv_type)375 gst_handdetect_set_caps (GstOpencvVideoFilter * transform,
376 gint in_width, gint in_height, int in_cv_type,
377 gint out_width, gint out_height, int out_cv_type)
378 {
379 GstHanddetect *filter;
380 filter = GST_HANDDETECT (transform);
381
382 /* 320 x 240 is with the best detect accuracy, if not, give info */
383 if (in_width != 320 || in_height != 240)
384 GST_WARNING_OBJECT (filter,
385 "resize to 320 x 240 to have best detect accuracy.\n");
386
387 filter->cvGray.create (Size (in_width, in_height), CV_8UC1);
388
389 return TRUE;
390 }
391
392 /* Hand detection function
393 * This function does the actual processing 'of hand detect and display'
394 */
395 static GstFlowReturn
gst_handdetect_transform_ip(GstOpencvVideoFilter * transform,GstBuffer * buffer,Mat img)396 gst_handdetect_transform_ip (GstOpencvVideoFilter * transform,
397 GstBuffer * buffer, Mat img)
398 {
399 GstHanddetect *filter = GST_HANDDETECT (transform);
400 Rect *r;
401 GstStructure *s;
402 GstMessage *m;
403 unsigned int i;
404 vector < Rect > hands;
405
406 /* check detection cascades */
407 if (filter->cvCascade_fist && filter->cvCascade_palm) {
408 /* cvt to gray colour space for hand detect */
409 cvtColor (img, filter->cvGray, COLOR_RGB2GRAY);
410
411 /* detect FIST gesture fist */
412 Mat roi (filter->cvGray, Rect (0,
413 0, filter->cvGray.size ().width, filter->cvGray.size ().height));
414 filter->cvCascade_fist->detectMultiScale (roi, hands, 1.1, 2,
415 CASCADE_DO_CANNY_PRUNING, Size (24, 24), Size (0, 0));
416
417 /* if FIST gesture detected */
418 if (!hands.empty ()) {
419
420 int min_distance, distance;
421 Rect temp_r;
422 Point c;
423
424 /* Go through all detected FIST gestures to get the best one
425 * prev_r => previous hand
426 * best_r => best hand in this frame
427 */
428 /* set min_distance for init comparison */
429 min_distance = img.size ().width + img.size ().height;
430 /* Init filter->prev_r */
431 temp_r = Rect (0, 0, 0, 0);
432 if (filter->prev_r == NULL)
433 filter->prev_r = &temp_r;
434 /* Get the best FIST gesture */
435 for (i = 0; i < hands.size (); i++) {
436 r = &hands[i];
437 distance = (int) sqrt (pow ((r->x - filter->prev_r->x),
438 2) + pow ((r->y - filter->prev_r->y), 2));
439 if (distance <= min_distance) {
440 min_distance = distance;
441 delete (filter->best_r);
442 filter->best_r = new Rect (*r);
443 }
444 }
445 /* Save best_r as prev_r for next frame comparison */
446 filter->prev_r = filter->best_r;
447
448 /* send msg to app/bus if the detected gesture falls in the region of interest */
449 /* get center point of gesture */
450 c = Point (filter->best_r->x + filter->best_r->width / 2,
451 filter->best_r->y + filter->best_r->height / 2);
452 /* send message:
453 * if the center point is in the region of interest, OR,
454 * if the region of interest remains default as (0,0,0,0)*/
455 if ((c.x >= filter->roi_x && c.x <= (filter->roi_x + filter->roi_width)
456 && c.y >= filter->roi_y
457 && c.y <= (filter->roi_y + filter->roi_height))
458 || (filter->roi_x == 0
459 && filter->roi_y == 0
460 && filter->roi_width == 0 && filter->roi_height == 0)) {
461 /* Define structure for message post */
462 s = gst_structure_new ("hand-gesture",
463 "gesture", G_TYPE_STRING, "fist",
464 "x", G_TYPE_INT,
465 (gint) (filter->best_r->x + filter->best_r->width * 0.5), "y",
466 G_TYPE_INT,
467 (gint) (filter->best_r->y + filter->best_r->height * 0.5), "width",
468 G_TYPE_INT, (gint) filter->best_r->width, "height", G_TYPE_INT,
469 (gint) filter->best_r->height, NULL);
470 /* Init message element */
471 m = gst_message_new_element (GST_OBJECT (filter), s);
472 /* Send message */
473 gst_element_post_message (GST_ELEMENT (filter), m);
474
475 #if 0
476 /* send event
477 * here we use mouse-move event instead of fist-move or palm-move event
478 * !!! this will CHANGE in the future !!!
479 * !!! by adding gst_navigation_send_hand_detect_event() in navigation.c !!!
480 */
481 gst_navigation_send_mouse_event (GST_NAVIGATION (filter),
482 "mouse-move",
483 0,
484 (double) (filter->best_r->x + filter->best_r->width * 0.5),
485 (double) (filter->best_r->y + filter->best_r->height * 0.5));
486
487 #endif
488 }
489 /* Check filter->display,
490 * If TRUE, displaying red circle marker in the out frame */
491 if (filter->display) {
492 Point center;
493 int radius;
494 center.x = cvRound ((filter->best_r->x + filter->best_r->width * 0.5));
495 center.y = cvRound ((filter->best_r->y + filter->best_r->height * 0.5));
496 radius =
497 cvRound ((filter->best_r->width + filter->best_r->height) * 0.25);
498 circle (img, center, radius, CV_RGB (0, 0, 200), 1, 8, 0);
499 }
500 } else {
501 /* if NO FIST gesture, detecting PALM gesture */
502 filter->cvCascade_palm->detectMultiScale (roi, hands, 1.1, 2,
503 CASCADE_DO_CANNY_PRUNING, Size (24, 24), Size (0, 0));
504 /* if PALM detected */
505 if (!hands.empty ()) {
506 int min_distance, distance;
507 Rect temp_r;
508 Point c;
509
510 if (filter->display) {
511 GST_DEBUG_OBJECT (filter, "%d PALM gestures detected\n",
512 (int) hands.size ());
513 }
514 /* Go through all detected PALM gestures to get the best one
515 * prev_r => previous hand
516 * best_r => best hand in this frame
517 */
518 /* suppose a min_distance for init comparison */
519 min_distance = img.size ().width + img.size ().height;
520 /* Init filter->prev_r */
521 temp_r = Rect (0, 0, 0, 0);
522 if (filter->prev_r == NULL)
523 filter->prev_r = &temp_r;
524 /* Get the best PALM gesture */
525 for (i = 0; i < hands.size (); ++i) {
526 r = &hands[i];
527 distance = (int) sqrt (pow ((r->x - filter->prev_r->x),
528 2) + pow ((r->y - filter->prev_r->y), 2));
529 if (distance <= min_distance) {
530 min_distance = distance;
531 delete (filter->best_r);
532 filter->best_r = new Rect (*r);
533 }
534 }
535 /* Save best_r as prev_r for next frame comparison */
536 filter->prev_r = filter->best_r;
537
538 /* send msg to app/bus if the detected gesture falls in the region of interest */
539 /* get center point of gesture */
540 c = Point (filter->best_r->x + filter->best_r->width / 2,
541 filter->best_r->y + filter->best_r->height / 2);
542 /* send message:
543 * if the center point is in the region of interest, OR,
544 * if the region of interest remains default as (0,0,0,0)*/
545 if (((gint) c.x >= filter->roi_x
546 && (gint) c.x <= (filter->roi_x + filter->roi_width)
547 && (gint) c.y >= filter->roi_y
548 && (gint) c.y <= (filter->roi_y + filter->roi_height))
549 || (filter->roi_x == 0 && filter->roi_y == 0
550 && filter->roi_width == 0 && filter->roi_height == 0)) {
551 /* Define structure for message post */
552 s = gst_structure_new ("hand-gesture",
553 "gesture", G_TYPE_STRING, "palm",
554 "x", G_TYPE_INT,
555 (gint) (filter->best_r->x + filter->best_r->width * 0.5), "y",
556 G_TYPE_INT,
557 (gint) (filter->best_r->y + filter->best_r->height * 0.5),
558 "width", G_TYPE_INT, (gint) filter->best_r->width, "height",
559 G_TYPE_INT, (gint) filter->best_r->height, NULL);
560 /* Init message element */
561 m = gst_message_new_element (GST_OBJECT (filter), s);
562 /* Send message */
563 gst_element_post_message (GST_ELEMENT (filter), m);
564
565 #if 0
566 /* send event
567 * here we use mouse-move event instead of fist-move or palm-move event
568 * !!! this will CHANGE in the future !!!
569 * !!! by adding gst_navigation_send_hand_detect_event() in navigation.c !!!
570 */
571 gst_navigation_send_mouse_event (GST_NAVIGATION (filter),
572 "mouse-move",
573 0,
574 (double) (filter->best_r->x + filter->best_r->width * 0.5),
575 (double) (filter->best_r->y + filter->best_r->height * 0.5));
576
577 /* or use another way to send upstream navigation event for debug
578 *
579 * GstEvent *event =
580 * gst_event_new_navigation (gst_structure_new
581 * ("application/x-gst-navigation", "event", G_TYPE_STRING,
582 * "mouse-move",
583 * "button", G_TYPE_INT, 0,
584 * "pointer_x", G_TYPE_DOUBLE,
585 * (double) (filter->best_r->x + filter->best_r->width * 0.5),
586 * "pointer_y", G_TYPE_DOUBLE,
587 * (double) (filter->best_r->y + filter->best_r->height * 0.5),
588 * NULL));
589 * gst_pad_send_event (GST_BASE_TRANSFORM_CAST (filter)->srcpad, event);
590 */
591 #endif
592 }
593 /* Check filter->display,
594 * If TRUE, displaying red circle marker in the out frame */
595 if (filter->display) {
596 Point center;
597 int radius;
598 center.x =
599 cvRound ((filter->best_r->x + filter->best_r->width * 0.5));
600 center.y =
601 cvRound ((filter->best_r->y + filter->best_r->height * 0.5));
602 radius =
603 cvRound ((filter->best_r->width + filter->best_r->height) * 0.25);
604 circle (img, center, radius, CV_RGB (0, 0, 200), 1, 8, 0);
605 }
606 }
607 }
608 }
609
610 /* Push out the incoming buffer */
611 return GST_FLOW_OK;
612 }
613
614 static CascadeClassifier *
gst_handdetect_load_profile(GstHanddetect * filter,gchar * profile)615 gst_handdetect_load_profile (GstHanddetect * filter, gchar * profile)
616 {
617 CascadeClassifier *cascade;
618
619 cascade = new CascadeClassifier (profile);
620 if (cascade->empty ()) {
621 GST_ERROR_OBJECT (filter, "Invalid profile file: %s", profile);
622 delete cascade;
623 return NULL;
624 }
625
626 return cascade;
627 }
628
629 /* Entry point to initialize the plug-in
630 * Initialize the plug-in itself
631 * Register the element factories and other features
632 */
633 gboolean
gst_handdetect_plugin_init(GstPlugin * plugin)634 gst_handdetect_plugin_init (GstPlugin * plugin)
635 {
636 GST_DEBUG_CATEGORY_INIT (gst_handdetect_debug,
637 "handdetect", 0, "opencv hand gesture detection");
638 return gst_element_register (plugin, "handdetect", GST_RANK_NONE,
639 GST_TYPE_HANDDETECT);
640 }
641