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