Tizen 2.0 Release
[framework/multimedia/gst-plugins-bad0.10.git] / ext / opencv / gsttemplatematch.c
1 /*
2  * GStreamer
3  * Copyright (C) 2005 Thomas Vander Stichele <thomas@apestaart.org>
4  * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
5  * Copyright (C) 2008 Michael Sheldon <mike@mikeasoft.com>
6  * Copyright (C) 2009 Noam Lewis <jones.noamle@gmail.com>
7  * 
8  * Permission is hereby granted, free of charge, to any person obtaining a
9  * copy of this software and associated documentation files (the "Software"),
10  * to deal in the Software without restriction, including without limitation
11  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12  * and/or sell copies of the Software, and to permit persons to whom the
13  * Software is furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24  * DEALINGS IN THE SOFTWARE.
25  *
26  * Alternatively, the contents of this file may be used under the
27  * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
28  * which case the following provisions apply instead of the ones
29  * mentioned above:
30  *
31  * This library is free software; you can redistribute it and/or
32  * modify it under the terms of the GNU Library General Public
33  * License as published by the Free Software Foundation; either
34  * version 2 of the License, or (at your option) any later version.
35  *
36  * This library is distributed in the hope that it will be useful,
37  * but WITHOUT ANY WARRANTY; without even the implied warranty of
38  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
39  * Library General Public License for more details.
40  *
41  * You should have received a copy of the GNU Library General Public
42  * License along with this library; if not, write to the
43  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
44  * Boston, MA 02111-1307, USA.
45  */
46
47 /**
48  * SECTION:element-templatematch
49  *
50  * Performs template matching on videos and images, providing detected positions via bus messages.
51  *
52  * <refsect2>
53  * <title>Example launch line</title>
54  * |[
55  * gst-launch-0.10 videotestsrc ! decodebin ! ffmpegcolorspace ! templatematch template=/path/to/file.jpg ! ffmpegcolorspace ! xvimagesink
56  * ]|
57  * </refsect2>
58  */
59
60 #ifdef HAVE_CONFIG_H
61 #  include <config.h>
62 #endif
63
64 #include <gst/gst.h>
65
66 #include "gstopencvutils.h"
67 #include "gsttemplatematch.h"
68
69 GST_DEBUG_CATEGORY_STATIC (gst_template_match_debug);
70 #define GST_CAT_DEFAULT gst_template_match_debug
71
72 #define DEFAULT_METHOD (3)
73
74 /* Filter signals and args */
75 enum
76 {
77   /* FILL ME */
78   LAST_SIGNAL
79 };
80
81 enum
82 {
83   PROP_0,
84   PROP_METHOD,
85   PROP_TEMPLATE,
86   PROP_DISPLAY,
87 };
88
89 /* the capabilities of the inputs and outputs.
90  */
91 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
92     GST_PAD_SINK,
93     GST_PAD_ALWAYS,
94     GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB)
95     );
96
97 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
98     GST_PAD_SRC,
99     GST_PAD_ALWAYS,
100     GST_STATIC_CAPS (GST_VIDEO_CAPS_RGB)
101     );
102
103 GST_BOILERPLATE (GstTemplateMatch, gst_template_match, GstElement,
104     GST_TYPE_ELEMENT);
105
106 static void gst_template_match_finalize (GObject * object);
107 static void gst_template_match_set_property (GObject * object, guint prop_id,
108     const GValue * value, GParamSpec * pspec);
109 static void gst_template_match_get_property (GObject * object, guint prop_id,
110     GValue * value, GParamSpec * pspec);
111
112 static gboolean gst_template_match_set_caps (GstPad * pad, GstCaps * caps);
113 static GstFlowReturn gst_template_match_chain (GstPad * pad, GstBuffer * buf);
114
115 static void gst_template_match_load_template (GstTemplateMatch * filter);
116 static void gst_template_match_match (IplImage * input, IplImage * template,
117     IplImage * dist_image, double *best_res, CvPoint * best_pos, int method);
118
119 /* GObject vmethod implementations */
120
121 static void
122 gst_template_match_base_init (gpointer gclass)
123 {
124   GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
125
126   gst_element_class_set_details_simple (element_class,
127       "templatematch",
128       "Filter/Effect/Video",
129       "Performs template matching on videos and images, providing detected positions via bus messages.",
130       "Noam Lewis <jones.noamle@gmail.com>");
131
132   gst_element_class_add_static_pad_template (element_class, &src_factory);
133   gst_element_class_add_static_pad_template (element_class, &sink_factory);
134 }
135
136 /* initialize the templatematch's class */
137 static void
138 gst_template_match_class_init (GstTemplateMatchClass * klass)
139 {
140   GObjectClass *gobject_class;
141
142   gobject_class = (GObjectClass *) klass;
143
144   gobject_class->finalize = gst_template_match_finalize;
145   gobject_class->set_property = gst_template_match_set_property;
146   gobject_class->get_property = gst_template_match_get_property;
147
148   g_object_class_install_property (gobject_class, PROP_METHOD,
149       g_param_spec_int ("method", "Method",
150           "Specifies the way the template must be compared with image regions. 0=SQDIFF, 1=SQDIFF_NORMED, 2=CCOR, 3=CCOR_NORMED, 4=CCOEFF, 5=CCOEFF_NORMED.",
151           0, 5, DEFAULT_METHOD, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
152   g_object_class_install_property (gobject_class, PROP_TEMPLATE,
153       g_param_spec_string ("template", "Template", "Filename of template image",
154           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
155   g_object_class_install_property (gobject_class, PROP_DISPLAY,
156       g_param_spec_boolean ("display", "Display",
157           "Sets whether the detected template should be highlighted in the output",
158           TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
159 }
160
161 /* initialize the new element
162  * instantiate pads and add them to element
163  * set pad callback functions
164  * initialize instance structure
165  */
166 static void
167 gst_template_match_init (GstTemplateMatch * filter,
168     GstTemplateMatchClass * gclass)
169 {
170   filter->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
171   gst_pad_set_setcaps_function (filter->sinkpad,
172       GST_DEBUG_FUNCPTR (gst_template_match_set_caps));
173   gst_pad_set_getcaps_function (filter->sinkpad,
174       GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
175   gst_pad_set_chain_function (filter->sinkpad,
176       GST_DEBUG_FUNCPTR (gst_template_match_chain));
177
178   filter->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
179   gst_pad_set_getcaps_function (filter->srcpad,
180       GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps));
181
182   gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
183   gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
184   filter->template = NULL;
185   filter->display = TRUE;
186   filter->cvTemplateImage = NULL;
187   filter->cvDistImage = NULL;
188   filter->cvImage = NULL;
189   filter->method = DEFAULT_METHOD;
190   gst_template_match_load_template (filter);
191 }
192
193 static void
194 gst_template_match_set_property (GObject * object, guint prop_id,
195     const GValue * value, GParamSpec * pspec)
196 {
197   GstTemplateMatch *filter = GST_TEMPLATE_MATCH (object);
198
199   switch (prop_id) {
200     case PROP_METHOD:
201       switch (g_value_get_int (value)) {
202         case 0:
203           filter->method = CV_TM_SQDIFF;
204           break;
205         case 1:
206           filter->method = CV_TM_SQDIFF_NORMED;
207           break;
208         case 2:
209           filter->method = CV_TM_CCORR;
210           break;
211         case 3:
212           filter->method = CV_TM_CCORR_NORMED;
213           break;
214         case 4:
215           filter->method = CV_TM_CCOEFF;
216           break;
217         case 5:
218           filter->method = CV_TM_CCOEFF_NORMED;
219           break;
220       }
221       break;
222     case PROP_TEMPLATE:
223       filter->template = (char *) g_value_get_string (value);
224       gst_template_match_load_template (filter);
225       break;
226     case PROP_DISPLAY:
227       filter->display = g_value_get_boolean (value);
228       break;
229     default:
230       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
231       break;
232   }
233 }
234
235 static void
236 gst_template_match_get_property (GObject * object, guint prop_id,
237     GValue * value, GParamSpec * pspec)
238 {
239   GstTemplateMatch *filter = GST_TEMPLATE_MATCH (object);
240
241   switch (prop_id) {
242     case PROP_METHOD:
243       g_value_set_int (value, filter->method);
244       break;
245     case PROP_TEMPLATE:
246       g_value_set_string (value, filter->template);
247       break;
248     case PROP_DISPLAY:
249       g_value_set_boolean (value, filter->display);
250       break;
251     default:
252       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
253       break;
254   }
255 }
256
257 /* GstElement vmethod implementations */
258
259 /* this function handles the link with other elements */
260 static gboolean
261 gst_template_match_set_caps (GstPad * pad, GstCaps * caps)
262 {
263   GstTemplateMatch *filter;
264   GstPad *otherpad;
265   gint width, height;
266   GstStructure *structure;
267
268   filter = GST_TEMPLATE_MATCH (gst_pad_get_parent (pad));
269   structure = gst_caps_get_structure (caps, 0);
270   gst_structure_get_int (structure, "width", &width);
271   gst_structure_get_int (structure, "height", &height);
272
273   filter->cvImage =
274       cvCreateImageHeader (cvSize (width, height), IPL_DEPTH_8U, 3);
275
276   otherpad = (pad == filter->srcpad) ? filter->sinkpad : filter->srcpad;
277   gst_object_unref (filter);
278
279   return gst_pad_set_caps (otherpad, caps);
280 }
281
282 static void
283 gst_template_match_finalize (GObject * object)
284 {
285   GstTemplateMatch *filter;
286   filter = GST_TEMPLATE_MATCH (object);
287
288   if (filter->cvImage) {
289     cvReleaseImageHeader (&filter->cvImage);
290   }
291   if (filter->cvDistImage) {
292     cvReleaseImage (&filter->cvDistImage);
293   }
294   if (filter->cvTemplateImage) {
295     cvReleaseImage (&filter->cvTemplateImage);
296   }
297
298   GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
299 }
300
301 /* chain function
302  * this function does the actual processing
303  */
304 static GstFlowReturn
305 gst_template_match_chain (GstPad * pad, GstBuffer * buf)
306 {
307   GstTemplateMatch *filter;
308   CvPoint best_pos;
309   double best_res;
310
311   filter = GST_TEMPLATE_MATCH (GST_OBJECT_PARENT (pad));
312
313   /* FIXME Why template == NULL returns OK?
314    * shouldn't it be a passthrough instead? */
315   if ((!filter) || (!buf) || filter->template == NULL) {
316     return GST_FLOW_OK;
317   }
318   GST_DEBUG_OBJECT (filter, "Buffer size %u ", GST_BUFFER_SIZE (buf));
319
320   filter->cvImage->imageData = (char *) GST_BUFFER_DATA (buf);
321
322   if (!filter->cvDistImage) {
323     if (filter->cvTemplateImage->width > filter->cvImage->width) {
324       GST_WARNING ("Template Image is wider than input image");
325     } else if (filter->cvTemplateImage->height > filter->cvImage->height) {
326       GST_WARNING ("Template Image is taller than input image");
327     } else {
328
329       GST_DEBUG_OBJECT (filter, "cvCreateImage (Size(%d-%d+1,%d) %d, %d)",
330           filter->cvImage->width, filter->cvTemplateImage->width,
331           filter->cvImage->height - filter->cvTemplateImage->height + 1,
332           IPL_DEPTH_32F, 1);
333       filter->cvDistImage =
334           cvCreateImage (cvSize (filter->cvImage->width -
335               filter->cvTemplateImage->width + 1,
336               filter->cvImage->height - filter->cvTemplateImage->height + 1),
337           IPL_DEPTH_32F, 1);
338       if (!filter->cvDistImage) {
339         GST_WARNING ("Couldn't create dist image.");
340       }
341     }
342   }
343   if (filter->cvTemplateImage && filter->cvImage && filter->cvDistImage) {
344     GstStructure *s;
345     GstMessage *m;
346
347     gst_template_match_match (filter->cvImage, filter->cvTemplateImage,
348         filter->cvDistImage, &best_res, &best_pos, filter->method);
349
350     s = gst_structure_new ("template_match",
351         "x", G_TYPE_UINT, best_pos.x,
352         "y", G_TYPE_UINT, best_pos.y,
353         "width", G_TYPE_UINT, filter->cvTemplateImage->width,
354         "height", G_TYPE_UINT, filter->cvTemplateImage->height,
355         "result", G_TYPE_DOUBLE, best_res, NULL);
356
357     m = gst_message_new_element (GST_OBJECT (filter), s);
358     gst_element_post_message (GST_ELEMENT (filter), m);
359
360     if (filter->display) {
361       CvPoint corner = best_pos;
362
363       buf = gst_buffer_make_writable (buf);
364
365       corner.x += filter->cvTemplateImage->width;
366       corner.y += filter->cvTemplateImage->height;
367       cvRectangle (filter->cvImage, best_pos, corner, CV_RGB (255, 32, 32), 3,
368           8, 0);
369     }
370
371   }
372
373   return gst_pad_push (filter->srcpad, buf);
374 }
375
376
377
378 static void
379 gst_template_match_match (IplImage * input, IplImage * template,
380     IplImage * dist_image, double *best_res, CvPoint * best_pos, int method)
381 {
382   double dist_min = 0, dist_max = 0;
383   CvPoint min_pos, max_pos;
384   cvMatchTemplate (input, template, dist_image, method);
385   cvMinMaxLoc (dist_image, &dist_min, &dist_max, &min_pos, &max_pos, NULL);
386   if ((CV_TM_SQDIFF_NORMED == method) || (CV_TM_SQDIFF == method)) {
387     *best_res = dist_min;
388     *best_pos = min_pos;
389     if (CV_TM_SQDIFF_NORMED == method) {
390       *best_res = 1 - *best_res;
391     }
392   } else {
393     *best_res = dist_max;
394     *best_pos = max_pos;
395   }
396 }
397
398
399 static void
400 gst_template_match_load_template (GstTemplateMatch * filter)
401 {
402   if (filter->template) {
403     filter->cvTemplateImage =
404         cvLoadImage (filter->template, CV_LOAD_IMAGE_COLOR);
405
406     if (!filter->cvTemplateImage) {
407       GST_WARNING ("Couldn't load template image: %s. error: %s",
408           filter->template, g_strerror (errno));
409     }
410   }
411 }
412
413
414 /* entry point to initialize the plug-in
415  * initialize the plug-in itself
416  * register the element factories and other features
417  */
418 gboolean
419 gst_template_match_plugin_init (GstPlugin * templatematch)
420 {
421   /* debug category for fltering log messages */
422   GST_DEBUG_CATEGORY_INIT (gst_template_match_debug, "templatematch",
423       0,
424       "Performs template matching on videos and images, providing detected positions via bus messages");
425
426   return gst_element_register (templatematch, "templatematch", GST_RANK_NONE,
427       GST_TYPE_TEMPLATE_MATCH);
428 }