From 2f1899cd31e5aa666ff31d99f1c4b2b09d927fc6 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Mon, 2 Aug 2010 20:41:12 +1000 Subject: [PATCH] gaudieffects: Add Gaussian Blur effect, and brief example. --- docs/plugins/inspect/plugin-gaudieffects.xml | 21 + gst/gaudieffects/Makefile.am | 8 +- gst/gaudieffects/blur-example.py | 23 ++ gst/gaudieffects/gstgaussblur.c | 411 +++++++++++++++++++ gst/gaudieffects/gstgaussblur.h | 40 ++ gst/gaudieffects/gstplugin.c | 1 + gst/gaudieffects/gstplugin.h | 13 +- 7 files changed, 507 insertions(+), 10 deletions(-) create mode 100644 gst/gaudieffects/blur-example.py create mode 100644 gst/gaudieffects/gstgaussblur.c create mode 100644 gst/gaudieffects/gstgaussblur.h diff --git a/docs/plugins/inspect/plugin-gaudieffects.xml b/docs/plugins/inspect/plugin-gaudieffects.xml index 9ec6ad3f49..c22cf6b77d 100644 --- a/docs/plugins/inspect/plugin-gaudieffects.xml +++ b/docs/plugins/inspect/plugin-gaudieffects.xml @@ -114,6 +114,27 @@ + + gaussianblur + GaussBlur + Filter/Effect/Video + Perform Gaussian blur/sharpen on a video + Jan Schmidt <thaytan@noraisin.net> + + + sink + sink + always +
video/x-raw-yuv, format=(fourcc)AYUV, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]
+
+ + src + source + always +
video/x-raw-yuv, format=(fourcc)AYUV, width=(int)[ 1, 2147483647 ], height=(int)[ 1, 2147483647 ], framerate=(fraction)[ 0/1, 2147483647/1 ]
+
+
+
solarize Solarize diff --git a/gst/gaudieffects/Makefile.am b/gst/gaudieffects/Makefile.am index 41b005ccdd..a050fbdaf3 100644 --- a/gst/gaudieffects/Makefile.am +++ b/gst/gaudieffects/Makefile.am @@ -1,13 +1,13 @@ plugin_LTLIBRARIES = libgstgaudieffects.la libgstgaudieffects_la_SOURCES = gstburn.c gstchromium.c gstdilate.c \ - gstdodge.c gstexclusion.c gstsolarize.c gstplugin.c -libgstgaudieffects_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) -libgstgaudieffects_la_LIBADD = $(GST_LIBS) -lgstvideo-@GST_MAJORMINOR@ + gstdodge.c gstexclusion.c gstgaussblur.c gstsolarize.c gstplugin.c +libgstgaudieffects_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) +libgstgaudieffects_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstvideo-@GST_MAJORMINOR@ $(GST_CONTROLLER_LIBS) $(GST_LIBS) $(LIBM) libgstgaudieffects_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstgaudieffects_la_LIBTOOLFLAGS = --tag=disable-static # headers we need but don't want installed noinst_HEADERS = \ gstburn.h gstchromium.h gstdilate.h gstdodge.h \ - gstexclusion.h gstplugin.h gstsolarize.h + gstexclusion.h gstgaussblur.h gstplugin.h gstsolarize.h diff --git a/gst/gaudieffects/blur-example.py b/gst/gaudieffects/blur-example.py new file mode 100644 index 0000000000..63920ec849 --- /dev/null +++ b/gst/gaudieffects/blur-example.py @@ -0,0 +1,23 @@ +#!/usr/bin/python +import gobject; gobject.threads_init() +import pygst; pygst.require("0.10") +import gst + +p = gst.parse_launch (""" + v4l2src ! + ffmpegcolorspace ! queue ! video/x-raw-yuv,width=320,height=240,framerate=30/1 ! gaussianblur qos=true name=vf ! ffmpegcolorspace ! + timeoverlay ! xvimagesink + """) + +m = p.get_by_name ("vf") +m.set_property ("sigma", 0.5) + +control = gst.Controller(m, "sigma") +control.set_interpolation_mode("sigma", gst.INTERPOLATE_LINEAR) +control.set("sigma", 0 * gst.SECOND, 0.5) +control.set("sigma", 5 * gst.SECOND, 10.0) +control.set("sigma", 25 * gst.SECOND, -5.0) + +p.set_state (gst.STATE_PLAYING) + +gobject.MainLoop().run() diff --git a/gst/gaudieffects/gstgaussblur.c b/gst/gaudieffects/gstgaussblur.c new file mode 100644 index 0000000000..6691c4c368 --- /dev/null +++ b/gst/gaudieffects/gstgaussblur.c @@ -0,0 +1,411 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include + +#include "gstplugin.h" +#include "gstgaussblur.h" + +static gboolean gauss_blur_stop (GstBaseTransform * btrans); +static gboolean gauss_blur_set_caps (GstBaseTransform * btrans, + GstCaps * incaps, GstCaps * outcaps); +static GstFlowReturn gauss_blur_process_frame (GstBaseTransform * btrans, + GstBuffer * in_buf, GstBuffer * out_buf); + +static void gauss_blur_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gauss_blur_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +GST_DEBUG_CATEGORY_STATIC (gst_gauss_blur_debug); +#define GST_CAT_DEFAULT gst_gauss_blur_debug + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN +#define CAPS_STR_RGB GST_VIDEO_CAPS_BGRx ";" GST_VIDEO_CAPS_RGBx +#else +#define CAPS_STR_RGB GST_VIDEO_CAPS_xRGB ";" GST_VIDEO_CAPS_xBGR +#endif + +#define CAPS_STR GST_VIDEO_CAPS_YUV("AYUV") + +/* The capabilities of the inputs and outputs. */ +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (CAPS_STR) + ); + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (CAPS_STR) + ); + +enum +{ + PROP_0, + PROP_SIGMA, + PROP_LAST +}; + +static void cleanup (GaussBlur * gb); +static gboolean make_gaussian_kernel (GaussBlur * gb, float sigma); +static void gaussian_smooth (GaussBlur * gb, guint8 * image, + guint8 * out_image); + +GST_BOILERPLATE (GaussBlur, gauss_blur, GstVideoFilter, GST_TYPE_VIDEO_FILTER); + +#define DEFAULT_SIGMA 1.2 + +static void +gauss_blur_base_init (gpointer gclass) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); + + gst_element_class_set_details_simple (element_class, + "GaussBlur", + "Filter/Effect/Video", + "Perform Gaussian blur/sharpen on a video", + "Jan Schmidt "); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_factory)); +} + +static void +gauss_blur_class_init (GaussBlurClass * klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass; + + object_class->set_property = gauss_blur_set_property; + object_class->get_property = gauss_blur_get_property; + + trans_class->stop = gauss_blur_stop; + trans_class->set_caps = gauss_blur_set_caps; + trans_class->transform = gauss_blur_process_frame; + + g_object_class_install_property (object_class, PROP_SIGMA, + g_param_spec_double ("sigma", "Sigma", + "Sigma value for gaussian blur (negative for sharpen)", + -20.0, 20.0, DEFAULT_SIGMA, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); +} + +static void +gauss_blur_init (GaussBlur * gb, GaussBlurClass * gclass) +{ + gb->sigma = DEFAULT_SIGMA; + gb->cur_sigma = -1.0; +} + +static void +cleanup (GaussBlur * gb) +{ + g_free (gb->tempim); + gb->tempim = NULL; + + g_free (gb->smoothedim); + gb->smoothedim = NULL; + + g_free (gb->kernel); + gb->kernel = NULL; + g_free (gb->kernel_sum); + gb->kernel_sum = NULL; +} + +static gboolean +gauss_blur_stop (GstBaseTransform * btrans) +{ + GaussBlur *gb = GAUSS_BLUR (btrans); + + cleanup (gb); + + return TRUE; +} + +static gboolean +gauss_blur_set_caps (GstBaseTransform * btrans, + GstCaps * incaps, GstCaps * outcaps) +{ + GaussBlur *gb = GAUSS_BLUR (btrans); + GstStructure *structure; + GstVideoFormat format; + guint32 n_elems; + + structure = gst_caps_get_structure (incaps, 0); + g_return_val_if_fail (structure != NULL, FALSE); + + if (!gst_video_format_parse_caps (incaps, &format, &gb->width, &gb->height)) + return FALSE; + + /* get stride */ + gb->stride = gst_video_format_get_row_stride (format, 0, gb->width); + + n_elems = gb->stride * gb->height; + + gb->tempim = g_malloc (sizeof (gfloat) * n_elems); + //gb->smoothedim = g_malloc (sizeof (guint16) * n_elems); + + return TRUE; +} + +static GstFlowReturn +gauss_blur_process_frame (GstBaseTransform * btrans, + GstBuffer * in_buf, GstBuffer * out_buf) +{ + GaussBlur *gb = GAUSS_BLUR (btrans); + GstClockTime timestamp; + gint64 stream_time; + gfloat sigma; + + /* GstController: update the properties */ + timestamp = GST_BUFFER_TIMESTAMP (in_buf); + stream_time = + gst_segment_to_stream_time (&btrans->segment, GST_FORMAT_TIME, timestamp); + if (GST_CLOCK_TIME_IS_VALID (stream_time)) + gst_object_sync_values (G_OBJECT (gb), stream_time); + + GST_OBJECT_LOCK (gb); + sigma = gb->sigma; + GST_OBJECT_UNLOCK (gb); + + if (gb->cur_sigma != sigma) { + g_free (gb->kernel); + gb->kernel = NULL; + g_free (gb->kernel_sum); + gb->kernel_sum = NULL; + gb->cur_sigma = sigma; + } + if (gb->kernel == NULL && !make_gaussian_kernel (gb, gb->cur_sigma)) { + GST_ELEMENT_ERROR (btrans, RESOURCE, NO_SPACE_LEFT, ("Out of memory"), + ("Failed to allocation gaussian kernel")); + return GST_FLOW_ERROR; + } + + /* + * Perform gaussian smoothing on the image using the input standard + * deviation. + */ + memcpy (GST_BUFFER_DATA (out_buf), GST_BUFFER_DATA (in_buf), + gb->height * gb->stride); + gaussian_smooth (gb, GST_BUFFER_DATA (in_buf), GST_BUFFER_DATA (out_buf)); + + return GST_FLOW_OK; +} + +static void +blur_row_x (GaussBlur * gb, guint8 * in_row, gfloat * out_row) +{ + int c, cc, center; + float dot[4], sum; + int k, kmin, kmax; + + center = gb->windowsize / 2; + + for (c = 0; c < gb->width; c++) { + /* Calculate min */ + cc = center - c; + kmin = MAX (0, cc); + cc = kmin - cc; + /* Calc max */ + kmax = MIN (gb->windowsize, gb->width - cc); + cc *= 4; + + dot[0] = dot[1] = dot[2] = dot[3] = 0.0; + /* Calculate sum for range */ + sum = gb->kernel_sum[kmax - 1]; + sum -= kmin ? gb->kernel_sum[kmin - 1] : 0.0; + + for (k = kmin; k < kmax; k++) { + float coeff = gb->kernel[k]; + dot[0] += (float) in_row[cc++] * coeff; + dot[1] += (float) in_row[cc++] * coeff; + dot[2] += (float) in_row[cc++] * coeff; + dot[3] += (float) in_row[cc++] * coeff; + } + + out_row[c * 4] = dot[0] / sum; + out_row[c * 4 + 1] = dot[1] / sum; + out_row[c * 4 + 2] = dot[2] / sum; + out_row[c * 4 + 3] = dot[3] / sum; + } +} + +static void +gaussian_smooth (GaussBlur * gb, guint8 * image, guint8 * out_image) +{ + int r, c, rr, center; + float dot[4], sum; + int k, kmin, kmax; + guint8 *in_row = image; + float *tmp_out_row = gb->tempim; + float *tmp_in_pos; + gint y_avail = 0; + guint8 *out_row; + + /* Apply the gaussian kernel */ + center = gb->windowsize / 2; + + /* Blur in the y - direction. */ + for (r = 0; r < gb->height; r++) { + /* Calculate input row range */ + rr = center - r; + kmin = MAX (0, rr); + rr = kmin - rr; + /* Calc max */ + kmax = MIN (gb->windowsize, gb->height - rr); + + /* Precalculate sum for range */ + sum = gb->kernel_sum[kmax - 1]; + sum -= kmin ? gb->kernel_sum[kmin - 1] : 0.0; + + /* Blur more input rows (x direction blur) */ + while (y_avail <= (r + center) && y_avail < gb->height) { + blur_row_x (gb, in_row, tmp_out_row); + in_row += gb->stride; + tmp_out_row += gb->stride; + y_avail++; + } + + tmp_in_pos = gb->tempim + (rr * gb->stride); + out_row = out_image + r * gb->stride; + + for (c = 0; c < gb->width; c++) { + float *tmp = tmp_in_pos; + + dot[0] = dot[1] = dot[2] = dot[3] = 0.0; + for (k = kmin; k < kmax; k++, tmp += gb->stride) { + float kern = gb->kernel[k]; + dot[0] += tmp[0] * kern; + dot[1] += tmp[1] * kern; + dot[2] += tmp[2] * kern; + dot[3] += tmp[3] * kern; + } + + *out_row++ = (guint8) CLAMP ((dot[0] / sum + 0.5), 0, 255); + *out_row++ = (guint8) CLAMP ((dot[1] / sum + 0.5), 0, 255); + *out_row++ = (guint8) CLAMP ((dot[2] / sum + 0.5), 0, 255); + *out_row++ = (guint8) CLAMP ((dot[3] / sum + 0.5), 0, 255); + + tmp_in_pos += 4; + } + } +} + +/* + * Create a one dimensional gaussian kernel. + */ +static gboolean +make_gaussian_kernel (GaussBlur * gb, float sigma) +{ + int i, center, left, right; + float sum, sum2; + const float fe = -0.5 / (sigma * sigma); + const float dx = 1.0 / (sigma * sqrt (2 * M_PI)); + + center = ceil (2.5 * fabs (sigma)); + gb->windowsize = (int) (1 + 2 * center); + + gb->kernel = g_new (float, gb->windowsize); + gb->kernel_sum = g_new (float, gb->windowsize); + if (gb->kernel == NULL || gb->kernel_sum == NULL) + return FALSE; + + if (gb->windowsize == 1) { + gb->kernel[0] = 1.0; + gb->kernel_sum[0] = 1.0; + return TRUE; + } + + /* Center co-efficient */ + sum = gb->kernel[center] = dx; + + /* Other coefficients */ + left = center - 1; + right = center + 1; + for (i = 1; i <= center; i++, left--, right++) { + float fx = dx * pow (M_E, fe * i * i); + gb->kernel[right] = gb->kernel[left] = fx; + sum += 2 * fx; + } + + if (sigma < 0) { + sum = -sum; + gb->kernel[center] += 2.0 * sum; + } + + for (i = 0; i < gb->windowsize; i++) + gb->kernel[i] /= sum; + + sum2 = 0.0; + for (i = 0; i < gb->windowsize; i++) { + sum2 += gb->kernel[i]; + gb->kernel_sum[i] = sum2; + } + +#if 0 + g_print ("Sigma %f: ", sigma); + for (i = 0; i < gb->windowsize; i++) + g_print ("%f ", gb->kernel[i]); + g_print ("\n"); + g_print ("sums: "); + for (i = 0; i < gb->windowsize; i++) + g_print ("%f ", gb->kernel_sum[i]); + g_print ("\n"); + g_print ("sum %f sum2 %f\n", sum, sum2); +#endif + + return TRUE; +} + +static void +gauss_blur_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GaussBlur *gb = GAUSS_BLUR (object); + switch (prop_id) { + case PROP_SIGMA: + GST_OBJECT_LOCK (object); + gb->sigma = g_value_get_double (value); + GST_OBJECT_UNLOCK (object); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gauss_blur_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GaussBlur *gb = GAUSS_BLUR (object); + switch (prop_id) { + case PROP_SIGMA: + GST_OBJECT_LOCK (gb); + g_value_set_double (value, gb->sigma); + GST_OBJECT_UNLOCK (gb); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/* Register the element factories and other features. */ +gboolean +gst_gauss_blur_plugin_init (GstPlugin * plugin) +{ + /* debug category for fltering log messages */ + GST_DEBUG_CATEGORY_INIT (gst_gauss_blur_debug, "gaussianblur", + 0, "Gaussian Blur video effect"); + + return gst_element_register (plugin, "gaussianblur", GST_RANK_NONE, + GST_TYPE_GAUSS_BLUR); +} diff --git a/gst/gaudieffects/gstgaussblur.h b/gst/gaudieffects/gstgaussblur.h new file mode 100644 index 0000000000..e582d181c1 --- /dev/null +++ b/gst/gaudieffects/gstgaussblur.h @@ -0,0 +1,40 @@ +#ifndef __GST_GAUSS_BLUR_H__ +#define __GST_GAUSS_BLUR_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_GAUSS_BLUR (gauss_blur_get_type()) +#define GAUSS_BLUR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GAUSS_BLUR, GaussBlur)) + +typedef struct GaussBlur GaussBlur; +typedef struct GaussBlurClass GaussBlurClass; + +struct GaussBlur +{ + GstVideoFilter videofilter; + gint width, height, stride; + + float cur_sigma, sigma; + int windowsize; + + float *kernel; + float *kernel_sum; + float *tempim; + gint16 *smoothedim; +}; + +struct GaussBlurClass +{ + GstVideoFilterClass parent_class; +}; + +GType gauss_blur_get_type(void); + +G_END_DECLS + +#endif diff --git a/gst/gaudieffects/gstplugin.c b/gst/gaudieffects/gstplugin.c index d8cb57137c..536bcb7e08 100644 --- a/gst/gaudieffects/gstplugin.c +++ b/gst/gaudieffects/gstplugin.c @@ -71,6 +71,7 @@ plugin_init (GstPlugin * plugin) ret &= gst_dodge_plugin_init (plugin); ret &= gst_exclusion_plugin_init (plugin); ret &= gst_solarize_plugin_init (plugin); + ret &= gst_gauss_blur_plugin_init (plugin); return ret; } diff --git a/gst/gaudieffects/gstplugin.h b/gst/gaudieffects/gstplugin.h index 4794a39757..dcfacbd7d9 100644 --- a/gst/gaudieffects/gstplugin.h +++ b/gst/gaudieffects/gstplugin.h @@ -50,12 +50,13 @@ G_BEGIN_DECLS -gboolean gst_burn_plugin_init (GstPlugin * chromium); -gboolean gst_chromium_plugin_init (GstPlugin * chromium); -gboolean gst_dilate_plugin_init (GstPlugin * chromium); -gboolean gst_dodge_plugin_init (GstPlugin * chromium); -gboolean gst_exclusion_plugin_init (GstPlugin * chromium); -gboolean gst_solarize_plugin_init (GstPlugin * chromium); +gboolean gst_burn_plugin_init (GstPlugin *plugin); +gboolean gst_chromium_plugin_init (GstPlugin *plugin); +gboolean gst_dilate_plugin_init (GstPlugin *plugin); +gboolean gst_dodge_plugin_init (GstPlugin *plugin); +gboolean gst_exclusion_plugin_init (GstPlugin *plugin); +gboolean gst_gauss_blur_plugin_init (GstPlugin *plugin); +gboolean gst_solarize_plugin_init (GstPlugin *plugin); G_END_DECLS -- 2.34.1