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