Merging gst-rtsp-server
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / ext / opencv / gsttemplatematch.cpp
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., 51 Franklin St, Fifth Floor,
44  * Boston, MA 02110-1301, 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  * ## Example launch line
53  *
54  * |[
55  * gst-launch-1.0 videotestsrc ! videoconvert ! templatematch template=/path/to/file.jpg ! videoconvert ! xvimagesink
56  * ]|
57  */
58
59 #ifdef HAVE_CONFIG_H
60 #  include <config.h>
61 #endif
62
63 #include <gst/gst-i18n-plugin.h>
64 #include "gsttemplatematch.h"
65 #include <opencv2/imgproc.hpp>
66 #include <opencv2/imgcodecs.hpp>
67
68 GST_DEBUG_CATEGORY_STATIC (gst_template_match_debug);
69 #define GST_CAT_DEFAULT gst_template_match_debug
70
71 #define DEFAULT_METHOD (3)
72
73 /* Filter signals and args */
74 enum
75 {
76   /* FILL ME */
77   LAST_SIGNAL
78 };
79
80 enum
81 {
82   PROP_0,
83   PROP_METHOD,
84   PROP_TEMPLATE,
85   PROP_DISPLAY,
86 };
87
88 /* the capabilities of the inputs and outputs.
89  */
90 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
91     GST_PAD_SINK,
92     GST_PAD_ALWAYS,
93     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("BGR"))
94     );
95
96 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
97     GST_PAD_SRC,
98     GST_PAD_ALWAYS,
99     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("BGR"))
100     );
101
102 G_DEFINE_TYPE_WITH_CODE (GstTemplateMatch, gst_template_match,
103     GST_TYPE_OPENCV_VIDEO_FILTER,
104     GST_DEBUG_CATEGORY_INIT (gst_template_match_debug, "templatematch", 0,
105         "Performs template matching on videos and images, providing detected positions via bus messages");
106     );
107 GST_ELEMENT_REGISTER_DEFINE (templatematch, "templatematch",
108     GST_RANK_NONE, GST_TYPE_TEMPLATE_MATCH);
109
110 static void gst_template_match_finalize (GObject * object);
111 static void gst_template_match_set_property (GObject * object, guint prop_id,
112     const GValue * value, GParamSpec * pspec);
113 static void gst_template_match_get_property (GObject * object, guint prop_id,
114     GValue * value, GParamSpec * pspec);
115
116 static GstFlowReturn gst_template_match_transform_ip (GstOpencvVideoFilter *
117     filter, GstBuffer * buf, cv::Mat img);
118
119 /* initialize the templatematch's class */
120 static void
121 gst_template_match_class_init (GstTemplateMatchClass * klass)
122 {
123   GObjectClass *gobject_class;
124   GstOpencvVideoFilterClass *gstopencvbasefilter_class;
125   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
126
127   gobject_class = (GObjectClass *) klass;
128   gstopencvbasefilter_class = (GstOpencvVideoFilterClass *) klass;
129
130   gobject_class->finalize = gst_template_match_finalize;
131   gobject_class->set_property = gst_template_match_set_property;
132   gobject_class->get_property = gst_template_match_get_property;
133
134   gstopencvbasefilter_class->cv_trans_ip_func = gst_template_match_transform_ip;
135
136   g_object_class_install_property (gobject_class, PROP_METHOD,
137       g_param_spec_int ("method", "Method",
138           "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.",
139           0, 5, DEFAULT_METHOD,
140           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
141   g_object_class_install_property (gobject_class, PROP_TEMPLATE,
142       g_param_spec_string ("template", "Template", "Filename of template image",
143           NULL, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
144   g_object_class_install_property (gobject_class, PROP_DISPLAY,
145       g_param_spec_boolean ("display", "Display",
146           "Sets whether the detected template should be highlighted in the output",
147           TRUE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
148
149   gst_element_class_set_static_metadata (element_class,
150       "templatematch",
151       "Filter/Effect/Video",
152       "Performs template matching on videos and images, providing detected positions via bus messages.",
153       "Noam Lewis <jones.noamle@gmail.com>");
154
155   gst_element_class_add_static_pad_template (element_class, &src_factory);
156   gst_element_class_add_static_pad_template (element_class, &sink_factory);
157 }
158
159 /* initialize the new element
160  * instantiate pads and add them to element
161  * set pad callback functions
162  * initialize instance structure
163  */
164 static void
165 gst_template_match_init (GstTemplateMatch * filter)
166 {
167   filter->templ = NULL;
168   filter->display = TRUE;
169   filter->reload_dist_image = TRUE;
170   filter->method = DEFAULT_METHOD;
171
172   gst_opencv_video_filter_set_in_place (GST_OPENCV_VIDEO_FILTER_CAST (filter),
173       TRUE);
174 }
175
176 /* We take ownership of template here */
177 static void
178 gst_template_match_load_template (GstTemplateMatch * filter, gchar * templ)
179 {
180   cv::Mat newTemplateImage;
181
182   if (templ) {
183     newTemplateImage = cv::imread (templ);
184
185     if (newTemplateImage.empty ()) {
186       /* Unfortunately OpenCV doesn't seem to provide any way of finding out
187          why the image load failed, so we can't be more specific than FAILED: */
188       GST_ELEMENT_WARNING (filter, RESOURCE, FAILED,
189           (_("OpenCV failed to load template image")),
190           ("While attempting to load template '%s'", templ));
191       g_free (templ);
192       templ = NULL;
193     }
194   }
195
196   GST_OBJECT_LOCK (filter);
197   g_free (filter->templ);
198   filter->templ = templ;
199   filter->cvTemplateImage = cv::Mat (newTemplateImage);
200   filter->reload_dist_image = TRUE;
201   GST_OBJECT_UNLOCK (filter);
202
203 }
204
205 static void
206 gst_template_match_set_property (GObject * object, guint prop_id,
207     const GValue * value, GParamSpec * pspec)
208 {
209   GstTemplateMatch *filter = GST_TEMPLATE_MATCH (object);
210
211   switch (prop_id) {
212     case PROP_METHOD:
213       GST_OBJECT_LOCK (filter);
214       switch (g_value_get_int (value)) {
215         case 0:
216           filter->method = cv::TM_SQDIFF;
217           break;
218         case 1:
219           filter->method = cv::TM_SQDIFF_NORMED;
220           break;
221         case 2:
222           filter->method = cv::TM_CCORR;
223           break;
224         case 3:
225           filter->method = cv::TM_CCORR_NORMED;
226           break;
227         case 4:
228           filter->method = cv::TM_CCOEFF;
229           break;
230         case 5:
231           filter->method = cv::TM_CCOEFF_NORMED;
232           break;
233       }
234       GST_OBJECT_UNLOCK (filter);
235       break;
236     case PROP_TEMPLATE:
237       gst_template_match_load_template (filter, g_value_dup_string (value));
238       break;
239     case PROP_DISPLAY:
240       GST_OBJECT_LOCK (filter);
241       filter->display = g_value_get_boolean (value);
242       GST_OBJECT_UNLOCK (filter);
243       break;
244     default:
245       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
246       break;
247   }
248 }
249
250 static void
251 gst_template_match_get_property (GObject * object, guint prop_id,
252     GValue * value, GParamSpec * pspec)
253 {
254   GstTemplateMatch *filter = GST_TEMPLATE_MATCH (object);
255
256   switch (prop_id) {
257     case PROP_METHOD:
258       g_value_set_int (value, filter->method);
259       break;
260     case PROP_TEMPLATE:
261       g_value_set_string (value, filter->templ);
262       break;
263     case PROP_DISPLAY:
264       g_value_set_boolean (value, filter->display);
265       break;
266     default:
267       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
268       break;
269   }
270 }
271
272 /* GstElement vmethod implementations */
273
274 static void
275 gst_template_match_finalize (GObject * object)
276 {
277   GstTemplateMatch *filter;
278   filter = GST_TEMPLATE_MATCH (object);
279
280   g_free (filter->templ);
281
282   filter->cvDistImage.release ();
283   filter->cvTemplateImage.release ();
284
285   G_OBJECT_CLASS (gst_template_match_parent_class)->finalize (object);
286 }
287
288 static void
289 gst_template_match_match (cv::Mat input, cv::Mat templ,
290     cv::Mat dist_image, double *best_res, cv::Point * best_pos, int method)
291 {
292   double dist_min = 0, dist_max = 0;
293   cv::Point min_pos, max_pos;
294   matchTemplate (input, templ, dist_image, method);
295   minMaxLoc (dist_image, &dist_min, &dist_max, &min_pos, &max_pos);
296   if ((cv::TM_SQDIFF_NORMED == method) || (cv::TM_SQDIFF == method)) {
297     *best_res = dist_min;
298     *best_pos = min_pos;
299     if (cv::TM_SQDIFF_NORMED == method) {
300       *best_res = 1 - *best_res;
301     }
302   } else {
303     *best_res = dist_max;
304     *best_pos = max_pos;
305   }
306 }
307
308 /* chain function
309  * this function does the actual processing
310  */
311 static GstFlowReturn
312 gst_template_match_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf,
313     cv::Mat img)
314 {
315   GstTemplateMatch *filter;
316   cv::Point best_pos;
317   double best_res;
318   GstMessage *m = NULL;
319
320   filter = GST_TEMPLATE_MATCH (base);
321
322   GST_LOG_OBJECT (filter, "Buffer size %u", (guint) gst_buffer_get_size (buf));
323
324   GST_OBJECT_LOCK (filter);
325   if (!filter->cvTemplateImage.empty () && filter->reload_dist_image) {
326     if (filter->cvTemplateImage.size ().width > img.size ().width) {
327       GST_WARNING ("Template Image is wider than input image");
328     } else if (filter->cvTemplateImage.size ().height > img.size ().height) {
329       GST_WARNING ("Template Image is taller than input image");
330     } else {
331
332       GST_DEBUG_OBJECT (filter, "cv create (Size(%d-%d+1,%d) %d)",
333           img.size ().width, filter->cvTemplateImage.size ().width,
334           img.size ().height - filter->cvTemplateImage.size ().height + 1,
335           CV_32FC1);
336       filter->cvDistImage.create (cv::Size (img.size ().width -
337               filter->cvTemplateImage.size ().width + 1,
338               img.size ().height - filter->cvTemplateImage.size ().height + 1),
339           CV_32FC1);
340       filter->reload_dist_image = FALSE;
341     }
342   }
343   if (!filter->cvTemplateImage.empty () && !filter->reload_dist_image) {
344     GstStructure *s;
345
346     gst_template_match_match (img, filter->cvTemplateImage,
347         filter->cvDistImage, &best_res, &best_pos, filter->method);
348
349     s = gst_structure_new ("template_match",
350         "x", G_TYPE_UINT, best_pos.x,
351         "y", G_TYPE_UINT, best_pos.y,
352         "width", G_TYPE_UINT, filter->cvTemplateImage.size ().width,
353         "height", G_TYPE_UINT, filter->cvTemplateImage.size ().height,
354         "result", G_TYPE_DOUBLE, best_res, NULL);
355
356     m = gst_message_new_element (GST_OBJECT (filter), s);
357
358     if (filter->display) {
359       cv::Point corner = best_pos;
360       cv::Scalar color;
361       if (filter->method == cv::TM_SQDIFF_NORMED
362           || filter->method == cv::TM_CCORR_NORMED
363           || filter->method == cv::TM_CCOEFF_NORMED) {
364         /* Yellow growing redder as match certainty approaches 1.0.  This can
365            only be applied with method == *_NORMED as the other match methods
366            aren't normalized to be in range 0.0 - 1.0 */
367         color = CV_RGB (255, 255 - pow (255, best_res), 32);
368       } else {
369         color = CV_RGB (255, 32, 32);
370       }
371
372       buf = gst_buffer_make_writable (buf);
373
374       corner.x += filter->cvTemplateImage.size ().width;
375       corner.y += filter->cvTemplateImage.size ().height;
376       cv::rectangle (img, best_pos, corner, color, 3, 8, 0);
377     }
378
379   }
380   GST_OBJECT_UNLOCK (filter);
381
382   if (m) {
383     gst_element_post_message (GST_ELEMENT (filter), m);
384   }
385   return GST_FLOW_OK;
386 }