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