summaryrefslogtreecommitdiff
path: root/gst/gaudieffects
diff options
context:
space:
mode:
authorJan Schmidt <thaytan@noraisin.net>2010-08-02 20:41:12 +1000
committerJan Schmidt <thaytan@noraisin.net>2010-08-02 20:46:48 +1000
commit2f1899cd31e5aa666ff31d99f1c4b2b09d927fc6 (patch)
tree5c9135c87831bd0a8f3f5ddb081034a9f087ce59 /gst/gaudieffects
parentfc9cfb0c0036d4ff8432d6b5880eb377dabf465b (diff)
gaudieffects: Add Gaussian Blur effect, and brief example.
Diffstat (limited to 'gst/gaudieffects')
-rw-r--r--gst/gaudieffects/Makefile.am8
-rw-r--r--gst/gaudieffects/blur-example.py23
-rw-r--r--gst/gaudieffects/gstgaussblur.c411
-rw-r--r--gst/gaudieffects/gstgaussblur.h40
-rw-r--r--gst/gaudieffects/gstplugin.c1
-rw-r--r--gst/gaudieffects/gstplugin.h13
6 files changed, 486 insertions, 10 deletions
diff --git a/gst/gaudieffects/Makefile.am b/gst/gaudieffects/Makefile.am
index 41b005ccd..a050fbdaf 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 000000000..63920ec84
--- /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 000000000..6691c4c36
--- /dev/null
+++ b/gst/gaudieffects/gstgaussblur.c
@@ -0,0 +1,411 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <math.h>
+#include <gst/gst.h>
+#include <gst/controller/gstcontroller.h>
+
+#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 <thaytan@noraisin.net>");
+
+ 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 000000000..e582d181c
--- /dev/null
+++ b/gst/gaudieffects/gstgaussblur.h
@@ -0,0 +1,40 @@
+#ifndef __GST_GAUSS_BLUR_H__
+#define __GST_GAUSS_BLUR_H__
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <gst/video/gstvideofilter.h>
+
+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 d8cb57137..536bcb7e0 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 4794a3975..dcfacbd7d 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