1 /*
2  * GStreamer
3  * Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Alternatively, the contents of this file may be used under the
24  * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
25  * which case the following provisions apply instead of the ones
26  * mentioned above:
27  *
28  * This library is free software; you can redistribute it and/or
29  * modify it under the terms of the GNU Library General Public
30  * License as published by the Free Software Foundation; either
31  * version 2 of the License, or (at your option) any later version.
32  *
33  * This library is distributed in the hope that it will be useful,
34  * but WITHOUT ANY WARRANTY; without even the implied warranty of
35  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
36  * Library General Public License for more details.
37  *
38  * You should have received a copy of the GNU Library General Public
39  * License along with this library; if not, write to the
40  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
41  * Boston, MA 02110-1301, USA.
42  */
43 
44 /**
45  * SECTION:element-cvsmooth
46  *
47  * Smooths the image using thes cvSmooth OpenCV function.
48  *
49  * <refsect2>
50  * <title>Example launch line</title>
51  * |[
52  * gst-launch-1.0 videotestsrc ! cvsmooth ! videoconvert ! autovideosink
53  * ]|
54  * </refsect2>
55  */
56 
57 #ifdef HAVE_CONFIG_H
58 #  include <config.h>
59 #endif
60 
61 #include "gst/opencv/gstopencvutils.h"
62 #include "gstcvsmooth.h"
63 #include <opencv2/imgproc.hpp>
64 
65 
66 GST_DEBUG_CATEGORY_STATIC (gst_cv_smooth_debug);
67 #define GST_CAT_DEFAULT gst_cv_smooth_debug
68 
69 using namespace cv;
70 /* Filter signals and args */
71 enum
72 {
73   /* FILL ME */
74   LAST_SIGNAL
75 };
76 enum
77 {
78   PROP_0,
79   PROP_SMOOTH_TYPE,
80   PROP_KERNELWIDTH,
81   PROP_KERNELHEIGHT,
82   PROP_COLORSIGMA,
83   PROP_SPATIALSIGMA,
84   PROP_POSITION_X,
85   PROP_POSITION_Y,
86   PROP_WIDTH,
87   PROP_HEIGHT
88 };
89 
90 /* blur-no-scale only handle: gray 8bits -> gray 16bits
91  * FIXME there is no way in base transform to override pad's getcaps
92  * to be property-sensitive, instead of using the template caps as
93  * the base caps, this might lead us to negotiating rgb in this
94  * smooth type.
95  *
96  * Keep it deactivated for now.
97  */
98 
99 enum GstCvSmoothMethod
100 {
101   GST_SMOOTH_BLUR = 1,
102   GST_SMOOTH_GAUSSIAN = 2,
103   GST_SMOOTH_MEDIAN = 3,
104   GST_SMOOTH_BILATERAL = 4
105 };
106 
107 
108 #define GST_TYPE_CV_SMOOTH_TYPE (gst_cv_smooth_type_get_type ())
109 static GType
gst_cv_smooth_type_get_type(void)110 gst_cv_smooth_type_get_type (void)
111 {
112   static GType cv_smooth_type_type = 0;
113 
114   static const GEnumValue smooth_types[] = {
115     {GST_SMOOTH_BLUR, "CV Blur", "blur"},
116     {GST_SMOOTH_GAUSSIAN, "CV Gaussian", "gaussian"},
117     {GST_SMOOTH_MEDIAN, "CV Median", "median"},
118     {GST_SMOOTH_BILATERAL, "CV Bilateral", "bilateral"},
119     {0, NULL, NULL},
120   };
121 
122   if (!cv_smooth_type_type) {
123     cv_smooth_type_type =
124         g_enum_register_static ("GstCvSmoothTypeType", smooth_types);
125   }
126   return cv_smooth_type_type;
127 }
128 
129 #define DEFAULT_CV_SMOOTH_TYPE GST_SMOOTH_GAUSSIAN
130 #define DEFAULT_KERNELWIDTH 3
131 #define DEFAULT_KERNELHEIGHT 3
132 #define DEFAULT_COLORSIGMA 0.0
133 #define DEFAULT_SPATIALSIGMA 0.0
134 #define DEFAULT_POSITION_X 0
135 #define DEFAULT_POSITION_Y 0
136 #define DEFAULT_WIDTH G_MAXINT
137 #define DEFAULT_HEIGHT G_MAXINT
138 
139 G_DEFINE_TYPE (GstCvSmooth, gst_cv_smooth, GST_TYPE_OPENCV_VIDEO_FILTER);
140 
141 static void gst_cv_smooth_set_property (GObject * object, guint prop_id,
142     const GValue * value, GParamSpec * pspec);
143 static void gst_cv_smooth_get_property (GObject * object, guint prop_id,
144     GValue * value, GParamSpec * pspec);
145 
146 static GstFlowReturn gst_cv_smooth_transform_ip (GstOpencvVideoFilter *
147     filter, GstBuffer * buf, Mat img);
148 
149 /* initialize the cvsmooth's class */
150 static void
gst_cv_smooth_class_init(GstCvSmoothClass * klass)151 gst_cv_smooth_class_init (GstCvSmoothClass * klass)
152 {
153   GObjectClass *gobject_class;
154   GstOpencvVideoFilterClass *gstopencvbasefilter_class;
155   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
156   GstCaps *caps;
157   GstPadTemplate *templ;
158 
159   gobject_class = (GObjectClass *) klass;
160   gstopencvbasefilter_class = (GstOpencvVideoFilterClass *) klass;
161 
162   gobject_class->set_property = gst_cv_smooth_set_property;
163   gobject_class->get_property = gst_cv_smooth_get_property;
164 
165   gstopencvbasefilter_class->cv_trans_ip_func = gst_cv_smooth_transform_ip;
166 
167   g_object_class_install_property (gobject_class, PROP_SMOOTH_TYPE,
168       g_param_spec_enum ("type",
169           "type",
170           "Smooth Type",
171           GST_TYPE_CV_SMOOTH_TYPE,
172           DEFAULT_CV_SMOOTH_TYPE,
173           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))
174       );
175   g_object_class_install_property (gobject_class, PROP_KERNELWIDTH,
176       g_param_spec_int ("kernel-width", "kernel width",
177           "The gaussian kernel width (must be positive and odd)."
178           "If type is median, this means the aperture linear size."
179           "Check OpenCV docs: http://docs.opencv.org"
180           "/2.4/modules/imgproc/doc/filtering.htm",
181           1, G_MAXINT, DEFAULT_KERNELWIDTH,
182           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
183   g_object_class_install_property (gobject_class, PROP_KERNELHEIGHT,
184       g_param_spec_int ("kernel-height", "kernel height",
185           "The gaussian kernel height (must be positive and odd).",
186           0, G_MAXINT, DEFAULT_KERNELHEIGHT,
187           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
188   g_object_class_install_property (gobject_class, PROP_COLORSIGMA,
189       g_param_spec_double ("color", "color (gaussian standard deviation or "
190           "color sigma",
191           "If type is gaussian, this means the standard deviation."
192           "If type is bilateral, this means the color-sigma. If zero, "
193           "Default values are used.",
194           0, G_MAXDOUBLE, DEFAULT_COLORSIGMA,
195           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
196   g_object_class_install_property (gobject_class, PROP_SPATIALSIGMA,
197       g_param_spec_double ("spatial", "spatial (spatial sigma, bilateral only)",
198           "Only used in bilateral type, means the spatial-sigma.",
199           0, G_MAXDOUBLE, DEFAULT_SPATIALSIGMA,
200           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
201   g_object_class_install_property (gobject_class, PROP_POSITION_X,
202       g_param_spec_int ("position-x", "starting x position for blur",
203           "Starting x position for blur (in pixels).",
204           0, G_MAXINT, DEFAULT_POSITION_X,
205           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
206   g_object_class_install_property (gobject_class, PROP_POSITION_Y,
207       g_param_spec_int ("position-y", "starting y position for blur",
208           "Starting y position for blur (in pixels).",
209           0, G_MAXINT, DEFAULT_POSITION_Y,
210           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
211   g_object_class_install_property (gobject_class, PROP_WIDTH,
212       g_param_spec_int ("width", "width of area to blur",
213           "Width of the area to blur (in pixels).",
214           0, G_MAXINT, DEFAULT_WIDTH,
215           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
216   g_object_class_install_property (gobject_class, PROP_HEIGHT,
217       g_param_spec_int ("height", "height of area to blur",
218           "Height of the area to blur (in pixels).",
219           0, G_MAXINT, DEFAULT_HEIGHT,
220           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
221 
222   gst_element_class_set_static_metadata (element_class,
223       "cvsmooth",
224       "Transform/Effect/Video",
225       "Applies cvSmooth OpenCV function to the image",
226       "Thiago Santos<thiago.sousa.santos@collabora.co.uk>");
227 
228   /* add sink and source pad templates */
229   caps = gst_opencv_caps_from_cv_image_type (CV_8UC3);
230   gst_caps_append (caps, gst_opencv_caps_from_cv_image_type (CV_8UC1));
231   templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
232       gst_caps_ref (caps));
233   gst_element_class_add_pad_template (element_class, templ);
234   templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, caps);
235   gst_element_class_add_pad_template (element_class, templ);
236   gst_caps_unref (caps);
237 }
238 
239 /* initialize the new element
240  * instantiate pads and add them to element
241  * set pad calback functions
242  * initialize instance structure
243  */
244 static void
gst_cv_smooth_init(GstCvSmooth * filter)245 gst_cv_smooth_init (GstCvSmooth * filter)
246 {
247   filter->type = DEFAULT_CV_SMOOTH_TYPE;
248   filter->kernelwidth = DEFAULT_KERNELWIDTH;
249   filter->kernelheight = DEFAULT_KERNELHEIGHT;
250   filter->colorsigma = DEFAULT_COLORSIGMA;
251   filter->spatialsigma = DEFAULT_SPATIALSIGMA;
252   filter->positionx = DEFAULT_POSITION_X;
253   filter->positiony = DEFAULT_POSITION_Y;
254   filter->width = DEFAULT_WIDTH;
255   filter->height = DEFAULT_HEIGHT;
256 
257   gst_opencv_video_filter_set_in_place (GST_OPENCV_VIDEO_FILTER_CAST (filter),
258       TRUE);
259 }
260 
261 static void
gst_cv_smooth_change_type(GstCvSmooth * filter,gint value)262 gst_cv_smooth_change_type (GstCvSmooth * filter, gint value)
263 {
264   GST_DEBUG_OBJECT (filter, "Changing type from %d to %d", filter->type, value);
265   if (filter->type == value)
266     return;
267 
268   filter->type = value;
269   switch (value) {
270     case GST_SMOOTH_GAUSSIAN:
271     case GST_SMOOTH_BLUR:
272       gst_opencv_video_filter_set_in_place (GST_OPENCV_VIDEO_FILTER_CAST
273           (filter), TRUE);
274       break;
275     default:
276       gst_opencv_video_filter_set_in_place (GST_OPENCV_VIDEO_FILTER_CAST
277           (filter), FALSE);
278       break;
279   }
280 }
281 
282 static void
gst_cv_smooth_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)283 gst_cv_smooth_set_property (GObject * object, guint prop_id,
284     const GValue * value, GParamSpec * pspec)
285 {
286   GstCvSmooth *filter = GST_CV_SMOOTH (object);
287 
288   switch (prop_id) {
289     case PROP_SMOOTH_TYPE:
290       gst_cv_smooth_change_type (filter, g_value_get_enum (value));
291       break;
292     case PROP_KERNELWIDTH:{
293       gint prop = g_value_get_int (value);
294 
295       if (prop % 2 == 1) {
296         filter->kernelwidth = prop;
297       } else {
298         GST_WARNING_OBJECT (filter, "Ignoring value for kernel-width, not odd"
299             "(%d)", prop);
300       }
301     }
302       break;
303     case PROP_KERNELHEIGHT:{
304       gint prop = g_value_get_int (value);
305 
306       if (prop % 2 == 1) {
307         filter->kernelheight = prop;
308       } else {
309         GST_WARNING_OBJECT (filter, "Ignoring value for kernel-height, not odd"
310             " nor zero (%d)", prop);
311       }
312     }
313       break;
314     case PROP_COLORSIGMA:
315       filter->colorsigma = g_value_get_double (value);
316       break;
317     case PROP_SPATIALSIGMA:
318       filter->spatialsigma = g_value_get_double (value);
319       break;
320     case PROP_POSITION_X:
321       filter->positionx = g_value_get_int (value);
322       break;
323     case PROP_POSITION_Y:
324       filter->positiony = g_value_get_int (value);
325       break;
326     case PROP_WIDTH:
327       filter->width = g_value_get_int (value);
328       break;
329     case PROP_HEIGHT:
330       filter->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_cv_smooth_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)339 gst_cv_smooth_get_property (GObject * object, guint prop_id,
340     GValue * value, GParamSpec * pspec)
341 {
342   GstCvSmooth *filter = GST_CV_SMOOTH (object);
343 
344   switch (prop_id) {
345     case PROP_SMOOTH_TYPE:
346       g_value_set_enum (value, filter->type);
347       break;
348     case PROP_KERNELWIDTH:
349       g_value_set_int (value, filter->kernelwidth);
350       break;
351     case PROP_KERNELHEIGHT:
352       g_value_set_int (value, filter->kernelheight);
353       break;
354     case PROP_COLORSIGMA:
355       g_value_set_double (value, filter->colorsigma);
356       break;
357     case PROP_SPATIALSIGMA:
358       g_value_set_double (value, filter->spatialsigma);
359       break;
360     case PROP_POSITION_X:
361       g_value_set_int (value, filter->positionx);
362       break;
363     case PROP_POSITION_Y:
364       g_value_set_int (value, filter->positiony);
365       break;
366     case PROP_WIDTH:
367       g_value_set_int (value, filter->width);
368       break;
369     case PROP_HEIGHT:
370       g_value_set_int (value, filter->height);
371       break;
372     default:
373       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
374       break;
375   }
376 }
377 
378 static GstFlowReturn
gst_cv_smooth_transform_ip(GstOpencvVideoFilter * base,GstBuffer * buf,Mat img)379 gst_cv_smooth_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf,
380     Mat img)
381 {
382   GstCvSmooth *filter = GST_CV_SMOOTH (base);
383 
384   if (filter->positionx != 0 || filter->positiony != 0 ||
385       filter->width != G_MAXINT || filter->height != G_MAXINT) {
386     Size mat_size = img.size ();
387 
388     /* if the effect would start outside the image, just skip it */
389     if (filter->positionx >= mat_size.width
390         || filter->positiony >= mat_size.height)
391       return GST_FLOW_OK;
392     /* explicitly account for empty area */
393     if (filter->width <= 0 || filter->height <= 0)
394       return GST_FLOW_OK;
395 
396     Rect mat_rect (filter->positionx,
397         filter->positiony,
398         MIN (filter->width, mat_size.width - filter->positionx),
399         MIN (filter->height, mat_size.height - filter->positiony));
400 
401     img = img (mat_rect);
402   }
403 
404   switch (filter->type) {
405     case GST_SMOOTH_BLUR:
406       blur (img, img, Size (filter->kernelwidth, filter->kernelheight),
407           Point (-1, -1));
408       break;
409     case GST_SMOOTH_GAUSSIAN:
410       GaussianBlur (img, img, Size (filter->kernelwidth, filter->kernelheight),
411           filter->colorsigma, filter->colorsigma);
412       break;
413     case GST_SMOOTH_MEDIAN:
414       medianBlur (img, img, filter->kernelwidth);
415       break;
416     case GST_SMOOTH_BILATERAL:
417       bilateralFilter (img, img, -1, filter->colorsigma, 0.0);
418       break;
419     default:
420       break;
421   }
422 
423   return GST_FLOW_OK;
424 }
425 
426 gboolean
gst_cv_smooth_plugin_init(GstPlugin * plugin)427 gst_cv_smooth_plugin_init (GstPlugin * plugin)
428 {
429   GST_DEBUG_CATEGORY_INIT (gst_cv_smooth_debug, "cvsmooth", 0, "cvsmooth");
430 
431   return gst_element_register (plugin, "cvsmooth", GST_RANK_NONE,
432       GST_TYPE_CV_SMOOTH);
433 }
434