• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  * ## Example launch line
50  *
51  * |[
52  * gst-launch-1.0 videotestsrc ! cvsmooth ! videoconvert ! autovideosink
53  * ]|
54  */
55 
56 #ifdef HAVE_CONFIG_H
57 #  include <config.h>
58 #endif
59 
60 #include "gst/opencv/gstopencvutils.h"
61 #include "gstcvsmooth.h"
62 #include <opencv2/imgproc.hpp>
63 
64 
65 GST_DEBUG_CATEGORY_STATIC (gst_cv_smooth_debug);
66 #define GST_CAT_DEFAULT gst_cv_smooth_debug
67 
68 using namespace cv;
69 /* Filter signals and args */
70 enum
71 {
72   /* FILL ME */
73   LAST_SIGNAL
74 };
75 enum
76 {
77   PROP_0,
78   PROP_SMOOTH_TYPE,
79   PROP_KERNELWIDTH,
80   PROP_KERNELHEIGHT,
81   PROP_COLORSIGMA,
82   PROP_SPATIALSIGMA,
83   PROP_POSITION_X,
84   PROP_POSITION_Y,
85   PROP_WIDTH,
86   PROP_HEIGHT
87 };
88 
89 /* blur-no-scale only handle: gray 8bits -> gray 16bits
90  * FIXME there is no way in base transform to override pad's getcaps
91  * to be property-sensitive, instead of using the template caps as
92  * the base caps, this might lead us to negotiating rgb in this
93  * smooth type.
94  *
95  * Keep it deactivated for now.
96  */
97 
98 enum GstCvSmoothMethod
99 {
100   GST_SMOOTH_BLUR = 1,
101   GST_SMOOTH_GAUSSIAN = 2,
102   GST_SMOOTH_MEDIAN = 3,
103   GST_SMOOTH_BILATERAL = 4
104 };
105 
106 
107 #define GST_TYPE_CV_SMOOTH_TYPE (gst_cv_smooth_type_get_type ())
108 static GType
gst_cv_smooth_type_get_type(void)109 gst_cv_smooth_type_get_type (void)
110 {
111   static GType cv_smooth_type_type = 0;
112 
113   static const GEnumValue smooth_types[] = {
114     {GST_SMOOTH_BLUR, "CV Blur", "blur"},
115     {GST_SMOOTH_GAUSSIAN, "CV Gaussian", "gaussian"},
116     {GST_SMOOTH_MEDIAN, "CV Median", "median"},
117     {GST_SMOOTH_BILATERAL, "CV Bilateral", "bilateral"},
118     {0, NULL, NULL},
119   };
120 
121   if (!cv_smooth_type_type) {
122     cv_smooth_type_type =
123         g_enum_register_static ("GstCvSmoothTypeType", smooth_types);
124   }
125   return cv_smooth_type_type;
126 }
127 
128 #define DEFAULT_CV_SMOOTH_TYPE GST_SMOOTH_GAUSSIAN
129 #define DEFAULT_KERNELWIDTH 3
130 #define DEFAULT_KERNELHEIGHT 3
131 #define DEFAULT_COLORSIGMA 0.0
132 #define DEFAULT_SPATIALSIGMA 0.0
133 #define DEFAULT_POSITION_X 0
134 #define DEFAULT_POSITION_Y 0
135 #define DEFAULT_WIDTH G_MAXINT
136 #define DEFAULT_HEIGHT G_MAXINT
137 
138 G_DEFINE_TYPE_WITH_CODE (GstCvSmooth, gst_cv_smooth,
139     GST_TYPE_OPENCV_VIDEO_FILTER, GST_DEBUG_CATEGORY_INIT (gst_cv_smooth_debug,
140         "cvsmooth", 0, "cvsmooth");
141     );
142 GST_ELEMENT_REGISTER_DEFINE (cvsmooth, "cvsmooth", GST_RANK_NONE,
143     GST_TYPE_CV_SMOOTH);
144 
145 static void gst_cv_smooth_set_property (GObject * object, guint prop_id,
146     const GValue * value, GParamSpec * pspec);
147 static void gst_cv_smooth_get_property (GObject * object, guint prop_id,
148     GValue * value, GParamSpec * pspec);
149 
150 static GstFlowReturn gst_cv_smooth_transform_ip (GstOpencvVideoFilter *
151     filter, GstBuffer * buf, Mat img);
152 
153 /* initialize the cvsmooth's class */
154 static void
gst_cv_smooth_class_init(GstCvSmoothClass * klass)155 gst_cv_smooth_class_init (GstCvSmoothClass * klass)
156 {
157   GObjectClass *gobject_class;
158   GstOpencvVideoFilterClass *gstopencvbasefilter_class;
159   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
160   GstCaps *caps;
161   GstPadTemplate *templ;
162 
163   gobject_class = (GObjectClass *) klass;
164   gstopencvbasefilter_class = (GstOpencvVideoFilterClass *) klass;
165 
166   gobject_class->set_property = gst_cv_smooth_set_property;
167   gobject_class->get_property = gst_cv_smooth_get_property;
168 
169   gstopencvbasefilter_class->cv_trans_ip_func = gst_cv_smooth_transform_ip;
170 
171   g_object_class_install_property (gobject_class, PROP_SMOOTH_TYPE,
172       g_param_spec_enum ("type",
173           "type",
174           "Smooth Type",
175           GST_TYPE_CV_SMOOTH_TYPE,
176           DEFAULT_CV_SMOOTH_TYPE,
177           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))
178       );
179   g_object_class_install_property (gobject_class, PROP_KERNELWIDTH,
180       g_param_spec_int ("kernel-width", "kernel width",
181           "The gaussian kernel width (must be positive and odd)."
182           "If type is median, this means the aperture linear size."
183           "Check OpenCV docs: http://docs.opencv.org"
184           "/2.4/modules/imgproc/doc/filtering.htm",
185           1, G_MAXINT, DEFAULT_KERNELWIDTH,
186           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
187   g_object_class_install_property (gobject_class, PROP_KERNELHEIGHT,
188       g_param_spec_int ("kernel-height", "kernel height",
189           "The gaussian kernel height (must be positive and odd).",
190           0, G_MAXINT, DEFAULT_KERNELHEIGHT,
191           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
192   g_object_class_install_property (gobject_class, PROP_COLORSIGMA,
193       g_param_spec_double ("color", "color (gaussian standard deviation or "
194           "color sigma",
195           "If type is gaussian, this means the standard deviation."
196           "If type is bilateral, this means the color-sigma. If zero, "
197           "Default values are used.",
198           0, G_MAXDOUBLE, DEFAULT_COLORSIGMA,
199           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
200   g_object_class_install_property (gobject_class, PROP_SPATIALSIGMA,
201       g_param_spec_double ("spatial", "spatial (spatial sigma, bilateral only)",
202           "Only used in bilateral type, means the spatial-sigma.",
203           0, G_MAXDOUBLE, DEFAULT_SPATIALSIGMA,
204           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
205   g_object_class_install_property (gobject_class, PROP_POSITION_X,
206       g_param_spec_int ("position-x", "starting x position for blur",
207           "Starting x position for blur (in pixels).",
208           0, G_MAXINT, DEFAULT_POSITION_X,
209           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
210   g_object_class_install_property (gobject_class, PROP_POSITION_Y,
211       g_param_spec_int ("position-y", "starting y position for blur",
212           "Starting y position for blur (in pixels).",
213           0, G_MAXINT, DEFAULT_POSITION_Y,
214           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
215   g_object_class_install_property (gobject_class, PROP_WIDTH,
216       g_param_spec_int ("width", "width of area to blur",
217           "Width of the area to blur (in pixels).",
218           0, G_MAXINT, DEFAULT_WIDTH,
219           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
220   g_object_class_install_property (gobject_class, PROP_HEIGHT,
221       g_param_spec_int ("height", "height of area to blur",
222           "Height of the area to blur (in pixels).",
223           0, G_MAXINT, DEFAULT_HEIGHT,
224           (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
225 
226   gst_element_class_set_static_metadata (element_class,
227       "cvsmooth",
228       "Transform/Effect/Video",
229       "Applies cvSmooth OpenCV function to the image",
230       "Thiago Santos<thiago.sousa.santos@collabora.co.uk>");
231 
232   /* add sink and source pad templates */
233   caps = gst_opencv_caps_from_cv_image_type (CV_8UC3);
234   gst_caps_append (caps, gst_opencv_caps_from_cv_image_type (CV_8UC1));
235   templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
236       gst_caps_ref (caps));
237   gst_element_class_add_pad_template (element_class, templ);
238   templ = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, caps);
239   gst_element_class_add_pad_template (element_class, templ);
240   gst_caps_unref (caps);
241 
242   gst_type_mark_as_plugin_api (GST_TYPE_CV_SMOOTH_TYPE, (GstPluginAPIFlags) 0);
243 }
244 
245 /* initialize the new element
246  * instantiate pads and add them to element
247  * set pad callback functions
248  * initialize instance structure
249  */
250 static void
gst_cv_smooth_init(GstCvSmooth * filter)251 gst_cv_smooth_init (GstCvSmooth * filter)
252 {
253   filter->type = DEFAULT_CV_SMOOTH_TYPE;
254   filter->kernelwidth = DEFAULT_KERNELWIDTH;
255   filter->kernelheight = DEFAULT_KERNELHEIGHT;
256   filter->colorsigma = DEFAULT_COLORSIGMA;
257   filter->spatialsigma = DEFAULT_SPATIALSIGMA;
258   filter->positionx = DEFAULT_POSITION_X;
259   filter->positiony = DEFAULT_POSITION_Y;
260   filter->width = DEFAULT_WIDTH;
261   filter->height = DEFAULT_HEIGHT;
262 
263   gst_opencv_video_filter_set_in_place (GST_OPENCV_VIDEO_FILTER_CAST (filter),
264       TRUE);
265 }
266 
267 static void
gst_cv_smooth_change_type(GstCvSmooth * filter,gint value)268 gst_cv_smooth_change_type (GstCvSmooth * filter, gint value)
269 {
270   GST_DEBUG_OBJECT (filter, "Changing type from %d to %d", filter->type, value);
271   if (filter->type == value)
272     return;
273 
274   filter->type = value;
275   switch (value) {
276     case GST_SMOOTH_GAUSSIAN:
277     case GST_SMOOTH_BLUR:
278       gst_opencv_video_filter_set_in_place (GST_OPENCV_VIDEO_FILTER_CAST
279           (filter), TRUE);
280       break;
281     default:
282       gst_opencv_video_filter_set_in_place (GST_OPENCV_VIDEO_FILTER_CAST
283           (filter), FALSE);
284       break;
285   }
286 }
287 
288 static void
gst_cv_smooth_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)289 gst_cv_smooth_set_property (GObject * object, guint prop_id,
290     const GValue * value, GParamSpec * pspec)
291 {
292   GstCvSmooth *filter = GST_CV_SMOOTH (object);
293 
294   switch (prop_id) {
295     case PROP_SMOOTH_TYPE:
296       gst_cv_smooth_change_type (filter, g_value_get_enum (value));
297       break;
298     case PROP_KERNELWIDTH:{
299       gint prop = g_value_get_int (value);
300 
301       if (prop % 2 == 1) {
302         filter->kernelwidth = prop;
303       } else {
304         GST_WARNING_OBJECT (filter, "Ignoring value for kernel-width, not odd"
305             "(%d)", prop);
306       }
307     }
308       break;
309     case PROP_KERNELHEIGHT:{
310       gint prop = g_value_get_int (value);
311 
312       if (prop % 2 == 1) {
313         filter->kernelheight = prop;
314       } else {
315         GST_WARNING_OBJECT (filter, "Ignoring value for kernel-height, not odd"
316             " nor zero (%d)", prop);
317       }
318     }
319       break;
320     case PROP_COLORSIGMA:
321       filter->colorsigma = g_value_get_double (value);
322       break;
323     case PROP_SPATIALSIGMA:
324       filter->spatialsigma = g_value_get_double (value);
325       break;
326     case PROP_POSITION_X:
327       filter->positionx = g_value_get_int (value);
328       break;
329     case PROP_POSITION_Y:
330       filter->positiony = g_value_get_int (value);
331       break;
332     case PROP_WIDTH:
333       filter->width = g_value_get_int (value);
334       break;
335     case PROP_HEIGHT:
336       filter->height = g_value_get_int (value);
337       break;
338     default:
339       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
340       break;
341   }
342 }
343 
344 static void
gst_cv_smooth_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)345 gst_cv_smooth_get_property (GObject * object, guint prop_id,
346     GValue * value, GParamSpec * pspec)
347 {
348   GstCvSmooth *filter = GST_CV_SMOOTH (object);
349 
350   switch (prop_id) {
351     case PROP_SMOOTH_TYPE:
352       g_value_set_enum (value, filter->type);
353       break;
354     case PROP_KERNELWIDTH:
355       g_value_set_int (value, filter->kernelwidth);
356       break;
357     case PROP_KERNELHEIGHT:
358       g_value_set_int (value, filter->kernelheight);
359       break;
360     case PROP_COLORSIGMA:
361       g_value_set_double (value, filter->colorsigma);
362       break;
363     case PROP_SPATIALSIGMA:
364       g_value_set_double (value, filter->spatialsigma);
365       break;
366     case PROP_POSITION_X:
367       g_value_set_int (value, filter->positionx);
368       break;
369     case PROP_POSITION_Y:
370       g_value_set_int (value, filter->positiony);
371       break;
372     case PROP_WIDTH:
373       g_value_set_int (value, filter->width);
374       break;
375     case PROP_HEIGHT:
376       g_value_set_int (value, filter->height);
377       break;
378     default:
379       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
380       break;
381   }
382 }
383 
384 static GstFlowReturn
gst_cv_smooth_transform_ip(GstOpencvVideoFilter * base,GstBuffer * buf,Mat img)385 gst_cv_smooth_transform_ip (GstOpencvVideoFilter * base, GstBuffer * buf,
386     Mat img)
387 {
388   GstCvSmooth *filter = GST_CV_SMOOTH (base);
389 
390   if (filter->positionx != 0 || filter->positiony != 0 ||
391       filter->width != G_MAXINT || filter->height != G_MAXINT) {
392     Size mat_size = img.size ();
393 
394     /* if the effect would start outside the image, just skip it */
395     if (filter->positionx >= mat_size.width
396         || filter->positiony >= mat_size.height)
397       return GST_FLOW_OK;
398     /* explicitly account for empty area */
399     if (filter->width <= 0 || filter->height <= 0)
400       return GST_FLOW_OK;
401 
402     Rect mat_rect (filter->positionx,
403         filter->positiony,
404         MIN (filter->width, mat_size.width - filter->positionx),
405         MIN (filter->height, mat_size.height - filter->positiony));
406 
407     img = img (mat_rect);
408   }
409 
410   switch (filter->type) {
411     case GST_SMOOTH_BLUR:
412       blur (img, img, Size (filter->kernelwidth, filter->kernelheight),
413           Point (-1, -1));
414       break;
415     case GST_SMOOTH_GAUSSIAN:
416       GaussianBlur (img, img, Size (filter->kernelwidth, filter->kernelheight),
417           filter->colorsigma, filter->colorsigma);
418       break;
419     case GST_SMOOTH_MEDIAN:
420       medianBlur (img, img, filter->kernelwidth);
421       break;
422     case GST_SMOOTH_BILATERAL:
423       bilateralFilter (img, img, -1, filter->colorsigma, 0.0);
424       break;
425     default:
426       break;
427   }
428 
429   return GST_FLOW_OK;
430 }
431