7 #include <gst/controller/gstcontroller.h>
10 #include "gstgaussblur.h"
12 static gboolean gauss_blur_stop (GstBaseTransform * btrans);
13 static gboolean gauss_blur_set_caps (GstBaseTransform * btrans,
14 GstCaps * incaps, GstCaps * outcaps);
15 static GstFlowReturn gauss_blur_process_frame (GstBaseTransform * btrans,
16 GstBuffer * in_buf, GstBuffer * out_buf);
18 static void gauss_blur_set_property (GObject * object,
19 guint prop_id, const GValue * value, GParamSpec * pspec);
20 static void gauss_blur_get_property (GObject * object,
21 guint prop_id, GValue * value, GParamSpec * pspec);
23 GST_DEBUG_CATEGORY_STATIC (gst_gauss_blur_debug);
24 #define GST_CAT_DEFAULT gst_gauss_blur_debug
26 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
27 #define CAPS_STR_RGB GST_VIDEO_CAPS_BGRx ";" GST_VIDEO_CAPS_RGBx
29 #define CAPS_STR_RGB GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR
32 #define CAPS_STR GST_VIDEO_CAPS_YUV("AYUV")
34 /* The capabilities of the inputs and outputs. */
35 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
38 GST_STATIC_CAPS (CAPS_STR)
41 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
44 GST_STATIC_CAPS (CAPS_STR)
54 static void cleanup (GaussBlur * gb);
55 static gboolean make_gaussian_kernel (GaussBlur * gb, float sigma);
56 static void gaussian_smooth (GaussBlur * gb, guint8 * image,
59 GST_BOILERPLATE (GaussBlur, gauss_blur, GstVideoFilter, GST_TYPE_VIDEO_FILTER);
61 #define DEFAULT_SIGMA 1.2
64 gauss_blur_base_init (gpointer gclass)
66 GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
68 gst_element_class_set_details_simple (element_class,
70 "Filter/Effect/Video",
71 "Perform Gaussian blur/sharpen on a video",
72 "Jan Schmidt <thaytan@noraisin.net>");
74 gst_element_class_add_static_pad_template (element_class, &src_factory);
75 gst_element_class_add_static_pad_template (element_class, &sink_factory);
79 gauss_blur_class_init (GaussBlurClass * klass)
81 GObjectClass *object_class = (GObjectClass *) klass;
82 GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
84 object_class->set_property = gauss_blur_set_property;
85 object_class->get_property = gauss_blur_get_property;
87 trans_class->stop = gauss_blur_stop;
88 trans_class->set_caps = gauss_blur_set_caps;
89 trans_class->transform = gauss_blur_process_frame;
91 g_object_class_install_property (object_class, PROP_SIGMA,
92 g_param_spec_double ("sigma", "Sigma",
93 "Sigma value for gaussian blur (negative for sharpen)",
94 -20.0, 20.0, DEFAULT_SIGMA,
95 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
99 gauss_blur_init (GaussBlur * gb, GaussBlurClass * gclass)
101 gb->sigma = DEFAULT_SIGMA;
102 gb->cur_sigma = -1.0;
106 cleanup (GaussBlur * gb)
111 g_free (gb->smoothedim);
112 gb->smoothedim = NULL;
116 g_free (gb->kernel_sum);
117 gb->kernel_sum = NULL;
121 gauss_blur_stop (GstBaseTransform * btrans)
123 GaussBlur *gb = GAUSS_BLUR (btrans);
131 gauss_blur_set_caps (GstBaseTransform * btrans,
132 GstCaps * incaps, GstCaps * outcaps)
134 GaussBlur *gb = GAUSS_BLUR (btrans);
135 GstStructure *structure;
136 GstVideoFormat format;
139 structure = gst_caps_get_structure (incaps, 0);
140 g_return_val_if_fail (structure != NULL, FALSE);
142 if (!gst_video_format_parse_caps (incaps, &format, &gb->width, &gb->height))
146 gb->stride = gst_video_format_get_row_stride (format, 0, gb->width);
148 n_elems = gb->stride * gb->height;
150 gb->tempim = g_malloc (sizeof (gfloat) * n_elems);
151 //gb->smoothedim = g_malloc (sizeof (guint16) * n_elems);
157 gauss_blur_process_frame (GstBaseTransform * btrans,
158 GstBuffer * in_buf, GstBuffer * out_buf)
160 GaussBlur *gb = GAUSS_BLUR (btrans);
161 GstClockTime timestamp;
165 /* GstController: update the properties */
166 timestamp = GST_BUFFER_TIMESTAMP (in_buf);
168 gst_segment_to_stream_time (&btrans->segment, GST_FORMAT_TIME, timestamp);
169 if (GST_CLOCK_TIME_IS_VALID (stream_time))
170 gst_object_sync_values (G_OBJECT (gb), stream_time);
172 GST_OBJECT_LOCK (gb);
174 GST_OBJECT_UNLOCK (gb);
176 if (gb->cur_sigma != sigma) {
179 g_free (gb->kernel_sum);
180 gb->kernel_sum = NULL;
181 gb->cur_sigma = sigma;
183 if (gb->kernel == NULL && !make_gaussian_kernel (gb, gb->cur_sigma)) {
184 GST_ELEMENT_ERROR (btrans, RESOURCE, NO_SPACE_LEFT, ("Out of memory"),
185 ("Failed to allocation gaussian kernel"));
186 return GST_FLOW_ERROR;
190 * Perform gaussian smoothing on the image using the input standard
193 memcpy (GST_BUFFER_DATA (out_buf), GST_BUFFER_DATA (in_buf),
194 gb->height * gb->stride);
195 gaussian_smooth (gb, GST_BUFFER_DATA (in_buf), GST_BUFFER_DATA (out_buf));
201 blur_row_x (GaussBlur * gb, guint8 * in_row, gfloat * out_row)
207 center = gb->windowsize / 2;
209 for (c = 0; c < gb->width; c++) {
215 kmax = MIN (gb->windowsize, gb->width - cc);
218 dot[0] = dot[1] = dot[2] = dot[3] = 0.0;
219 /* Calculate sum for range */
220 sum = gb->kernel_sum[kmax - 1];
221 sum -= kmin ? gb->kernel_sum[kmin - 1] : 0.0;
223 for (k = kmin; k < kmax; k++) {
224 float coeff = gb->kernel[k];
225 dot[0] += (float) in_row[cc++] * coeff;
226 dot[1] += (float) in_row[cc++] * coeff;
227 dot[2] += (float) in_row[cc++] * coeff;
228 dot[3] += (float) in_row[cc++] * coeff;
231 out_row[c * 4] = dot[0] / sum;
232 out_row[c * 4 + 1] = dot[1] / sum;
233 out_row[c * 4 + 2] = dot[2] / sum;
234 out_row[c * 4 + 3] = dot[3] / sum;
239 gaussian_smooth (GaussBlur * gb, guint8 * image, guint8 * out_image)
241 int r, c, rr, center;
244 guint8 *in_row = image;
245 float *tmp_out_row = gb->tempim;
250 /* Apply the gaussian kernel */
251 center = gb->windowsize / 2;
253 /* Blur in the y - direction. */
254 for (r = 0; r < gb->height; r++) {
255 /* Calculate input row range */
260 kmax = MIN (gb->windowsize, gb->height - rr);
262 /* Precalculate sum for range */
263 sum = gb->kernel_sum[kmax - 1];
264 sum -= kmin ? gb->kernel_sum[kmin - 1] : 0.0;
266 /* Blur more input rows (x direction blur) */
267 while (y_avail <= (r + center) && y_avail < gb->height) {
268 blur_row_x (gb, in_row, tmp_out_row);
269 in_row += gb->stride;
270 tmp_out_row += gb->stride;
274 tmp_in_pos = gb->tempim + (rr * gb->stride);
275 out_row = out_image + r * gb->stride;
277 for (c = 0; c < gb->width; c++) {
278 float *tmp = tmp_in_pos;
280 dot[0] = dot[1] = dot[2] = dot[3] = 0.0;
281 for (k = kmin; k < kmax; k++, tmp += gb->stride) {
282 float kern = gb->kernel[k];
283 dot[0] += tmp[0] * kern;
284 dot[1] += tmp[1] * kern;
285 dot[2] += tmp[2] * kern;
286 dot[3] += tmp[3] * kern;
289 *out_row++ = (guint8) CLAMP ((dot[0] / sum + 0.5), 0, 255);
290 *out_row++ = (guint8) CLAMP ((dot[1] / sum + 0.5), 0, 255);
291 *out_row++ = (guint8) CLAMP ((dot[2] / sum + 0.5), 0, 255);
292 *out_row++ = (guint8) CLAMP ((dot[3] / sum + 0.5), 0, 255);
300 * Create a one dimensional gaussian kernel.
303 make_gaussian_kernel (GaussBlur * gb, float sigma)
305 int i, center, left, right;
307 const float fe = -0.5 / (sigma * sigma);
308 const float dx = 1.0 / (sigma * sqrt (2 * G_PI));
310 center = ceil (2.5 * fabs (sigma));
311 gb->windowsize = (int) (1 + 2 * center);
313 gb->kernel = g_new (float, gb->windowsize);
314 gb->kernel_sum = g_new (float, gb->windowsize);
315 if (gb->kernel == NULL || gb->kernel_sum == NULL)
318 if (gb->windowsize == 1) {
320 gb->kernel_sum[0] = 1.0;
324 /* Center co-efficient */
325 sum = gb->kernel[center] = dx;
327 /* Other coefficients */
330 for (i = 1; i <= center; i++, left--, right++) {
331 float fx = dx * pow (G_E, fe * i * i);
332 gb->kernel[right] = gb->kernel[left] = fx;
338 gb->kernel[center] += 2.0 * sum;
341 for (i = 0; i < gb->windowsize; i++)
342 gb->kernel[i] /= sum;
345 for (i = 0; i < gb->windowsize; i++) {
346 sum2 += gb->kernel[i];
347 gb->kernel_sum[i] = sum2;
351 g_print ("Sigma %f: ", sigma);
352 for (i = 0; i < gb->windowsize; i++)
353 g_print ("%f ", gb->kernel[i]);
356 for (i = 0; i < gb->windowsize; i++)
357 g_print ("%f ", gb->kernel_sum[i]);
359 g_print ("sum %f sum2 %f\n", sum, sum2);
366 gauss_blur_set_property (GObject * object,
367 guint prop_id, const GValue * value, GParamSpec * pspec)
369 GaussBlur *gb = GAUSS_BLUR (object);
372 GST_OBJECT_LOCK (object);
373 gb->sigma = g_value_get_double (value);
374 GST_OBJECT_UNLOCK (object);
377 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
383 gauss_blur_get_property (GObject * object,
384 guint prop_id, GValue * value, GParamSpec * pspec)
386 GaussBlur *gb = GAUSS_BLUR (object);
389 GST_OBJECT_LOCK (gb);
390 g_value_set_double (value, gb->sigma);
391 GST_OBJECT_UNLOCK (gb);
394 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
399 /* Register the element factories and other features. */
401 gst_gauss_blur_plugin_init (GstPlugin * plugin)
403 /* debug category for fltering log messages */
404 GST_DEBUG_CATEGORY_INIT (gst_gauss_blur_debug, "gaussianblur",
405 0, "Gaussian Blur video effect");
407 return gst_element_register (plugin, "gaussianblur", GST_RANK_NONE,
408 GST_TYPE_GAUSS_BLUR);