| /* GStreamer |
| * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| * Boston, MA 02111-1307, USA. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| #include <string.h> |
| #include "gstmedian.h" |
| #include <gst/video/video.h> |
| |
| /* elementfactory information */ |
| static const GstElementDetails median_details = |
| GST_ELEMENT_DETAILS ("Median effect", |
| "Filter/Effect/Video", |
| "Apply a median filter to an image", |
| "Wim Taymans <wim.taymans@chello.be>"); |
| |
| static GstStaticPadTemplate median_src_factory = GST_STATIC_PAD_TEMPLATE ("src", |
| GST_PAD_SRC, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420")) |
| ); |
| |
| static GstStaticPadTemplate median_sink_factory = |
| GST_STATIC_PAD_TEMPLATE ("sink", |
| GST_PAD_SINK, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("I420")) |
| ); |
| |
| |
| /* Median signals and args */ |
| enum |
| { |
| /* FILL ME */ |
| LAST_SIGNAL |
| }; |
| |
| enum |
| { |
| ARG_0, |
| ARG_ACTIVE, |
| ARG_FILTERSIZE, |
| ARG_LUM_ONLY |
| }; |
| |
| static GType gst_median_get_type (void); |
| static void gst_median_class_init (GstMedianClass * klass); |
| static void gst_median_base_init (GstMedianClass * klass); |
| static void gst_median_init (GstMedian * median); |
| |
| static void median_5 (unsigned char *src, unsigned char *dest, int height, |
| int width); |
| static void median_9 (unsigned char *src, unsigned char *dest, int height, |
| int width); |
| static void gst_median_chain (GstPad * pad, GstData * _data); |
| |
| static void gst_median_set_property (GObject * object, guint prop_id, |
| const GValue * value, GParamSpec * pspec); |
| static void gst_median_get_property (GObject * object, guint prop_id, |
| GValue * value, GParamSpec * pspec); |
| |
| static GstElementClass *parent_class = NULL; |
| |
| /*static guint gst_median_signals[LAST_SIGNAL] = { 0 }; */ |
| |
| GType |
| gst_median_get_type (void) |
| { |
| static GType median_type = 0; |
| |
| if (!median_type) { |
| static const GTypeInfo median_info = { |
| sizeof (GstMedianClass), |
| (GBaseInitFunc) gst_median_base_init, |
| NULL, |
| (GClassInitFunc) gst_median_class_init, |
| NULL, |
| NULL, |
| sizeof (GstMedian), |
| 0, |
| (GInstanceInitFunc) gst_median_init, |
| }; |
| |
| median_type = |
| g_type_register_static (GST_TYPE_ELEMENT, "GstMedian", &median_info, 0); |
| } |
| return median_type; |
| } |
| |
| static void |
| gst_median_base_init (GstMedianClass * klass) |
| { |
| GstElementClass *element_class = GST_ELEMENT_CLASS (klass); |
| |
| gst_element_class_add_pad_template (element_class, |
| gst_static_pad_template_get (&median_sink_factory)); |
| gst_element_class_add_pad_template (element_class, |
| gst_static_pad_template_get (&median_src_factory)); |
| gst_element_class_set_details (element_class, &median_details); |
| } |
| |
| static void |
| gst_median_class_init (GstMedianClass * klass) |
| { |
| GObjectClass *gobject_class; |
| GstElementClass *gstelement_class; |
| |
| gobject_class = (GObjectClass *) klass; |
| gstelement_class = (GstElementClass *) klass; |
| |
| parent_class = g_type_class_peek_parent (klass); |
| |
| g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ACTIVE, g_param_spec_boolean ("active", "active", "active", TRUE, G_PARAM_READWRITE)); /* CHECKME */ |
| g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FILTERSIZE, g_param_spec_int ("filtersize", "filtersize", "filtersize", G_MININT, G_MAXINT, 0, G_PARAM_READWRITE)); /* CHECKME */ |
| g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LUM_ONLY, g_param_spec_boolean ("lum_only", "lum_only", "lum_only", TRUE, G_PARAM_READWRITE)); /* CHECKME */ |
| |
| gobject_class->set_property = gst_median_set_property; |
| gobject_class->get_property = gst_median_get_property; |
| } |
| |
| static gboolean |
| gst_median_link (GstPad * pad, const GstCaps * caps) |
| { |
| GstMedian *filter = GST_MEDIAN (gst_pad_get_parent (pad)); |
| GstPad *otherpad = (pad == filter->srcpad) ? filter->sinkpad : filter->srcpad; |
| GstStructure *structure = gst_caps_get_structure (caps, 0); |
| gint w, h; |
| GstPadLinkReturn ret; |
| |
| gst_structure_get_int (structure, "width", &w); |
| gst_structure_get_int (structure, "height", &h); |
| |
| ret = gst_pad_try_set_caps (otherpad, caps); |
| if (GST_PAD_LINK_SUCCESSFUL (ret)) { |
| filter->width = w; |
| filter->height = h; |
| } |
| |
| gst_object_unref (filter); |
| |
| return ret; |
| } |
| |
| void |
| gst_median_init (GstMedian * median) |
| { |
| median->sinkpad = |
| gst_pad_new_from_static_template (&media_sink_factory, "sink"); |
| gst_pad_set_getcaps_function (median->sinkpad, gst_pad_proxy_getcaps); |
| gst_pad_set_link_function (median->sinkpad, gst_median_link); |
| gst_pad_set_chain_function (median->sinkpad, gst_median_chain); |
| gst_element_add_pad (GST_ELEMENT (median), median->sinkpad); |
| |
| median->srcpad = gst_pad_new_from_static_template (&media_src_factory, "src"); |
| gst_pad_set_getcaps_function (median->srcpad, gst_pad_proxy_getcaps); |
| gst_pad_set_link_function (median->sinkpad, gst_median_link); |
| gst_element_add_pad (GST_ELEMENT (median), median->srcpad); |
| |
| median->filtersize = 5; |
| median->lum_only = TRUE; |
| median->active = TRUE; |
| } |
| |
| #define PIX_SORT(a,b) { if ((a)>(b)) PIX_SWAP((a),(b)); } |
| #define PIX_SWAP(a,b) { unsigned char temp=(a);(a)=(b);(b)=temp; } |
| |
| static void |
| median_5 (unsigned char *src, unsigned char *dest, int width, int height) |
| { |
| int nLastRow; |
| int nLastCol; |
| unsigned char p[9]; |
| int i, j, k; |
| |
| nLastCol = width - 1; |
| nLastRow = height - 1; |
| |
| /*copy the top and bottom rows into the result array */ |
| for (i = 0; i < width; i++) { |
| dest[i] = src[i]; |
| dest[nLastRow * width + i] = src[nLastRow * width + i]; |
| } |
| dest[i] = src[i]; |
| |
| nLastCol--; |
| nLastRow--; |
| |
| /* process the interior pixels */ |
| i = width + 1; |
| for (k = 0; k < nLastRow; k++) { |
| for (j = 0; j < nLastCol; j++, i++) { |
| p[0] = src[i - width]; |
| p[1] = src[i - 1]; |
| p[2] = src[i]; |
| p[3] = src[i + 1]; |
| p[4] = src[i + width]; |
| PIX_SORT (p[0], p[1]); |
| PIX_SORT (p[3], p[4]); |
| PIX_SORT (p[0], p[3]); |
| PIX_SORT (p[1], p[4]); |
| PIX_SORT (p[1], p[2]); |
| PIX_SORT (p[2], p[3]); |
| PIX_SORT (p[1], p[2]); |
| dest[i] = p[2]; |
| } |
| dest[i] = src[i]; |
| i++; |
| dest[i] = src[i]; |
| i++; |
| } |
| dest[i] = src[i]; |
| i++; |
| } |
| |
| static void |
| median_9 (unsigned char *src, unsigned char *dest, int width, int height) |
| { |
| int nLastRow; |
| int nLastCol; |
| unsigned char p[9]; |
| int i, j, k; |
| |
| nLastCol = width - 1; |
| nLastRow = height - 1; |
| |
| /*copy the top and bottom rows into the result array */ |
| for (i = 0; i < width; i++) { |
| dest[i] = src[i]; |
| dest[nLastRow * width + i] = src[nLastRow * width + i]; |
| } |
| dest[i] = src[i]; |
| |
| nLastCol--; |
| nLastRow--; |
| |
| /* process the interior pixels */ |
| i = width + 1; |
| for (k = 0; k < nLastRow; k++) { |
| for (j = 0; j < nLastCol; j++, i++) { |
| p[0] = src[i - width - 1]; |
| p[1] = src[i - width]; |
| p[2] = src[i - width + 1]; |
| p[3] = src[i - 1]; |
| p[4] = src[i]; |
| p[5] = src[i + 1]; |
| p[6] = src[i + width - 1]; |
| p[7] = src[i + width]; |
| p[8] = src[i + width + 1]; |
| PIX_SORT (p[1], p[2]); |
| PIX_SORT (p[4], p[5]); |
| PIX_SORT (p[7], p[8]); |
| PIX_SORT (p[0], p[1]); |
| PIX_SORT (p[3], p[4]); |
| PIX_SORT (p[6], p[7]); |
| PIX_SORT (p[1], p[2]); |
| PIX_SORT (p[4], p[5]); |
| PIX_SORT (p[7], p[8]); |
| PIX_SORT (p[0], p[3]); |
| PIX_SORT (p[5], p[8]); |
| PIX_SORT (p[4], p[7]); |
| PIX_SORT (p[3], p[6]); |
| PIX_SORT (p[1], p[4]); |
| PIX_SORT (p[2], p[5]); |
| PIX_SORT (p[4], p[7]); |
| PIX_SORT (p[4], p[2]); |
| PIX_SORT (p[6], p[4]); |
| PIX_SORT (p[4], p[2]); |
| dest[i] = p[4]; |
| } |
| dest[i] = src[i]; |
| i++; |
| dest[i] = src[i]; |
| i++; |
| } |
| dest[i] = src[i]; |
| i++; |
| } |
| |
| static void |
| gst_median_chain (GstPad * pad, GstData * _data) |
| { |
| GstBuffer *buf = GST_BUFFER (_data); |
| GstMedian *median; |
| guchar *data; |
| gulong size; |
| GstBuffer *outbuf; |
| |
| /* GstMeta *meta; */ |
| int lumsize, chromsize; |
| |
| g_return_if_fail (pad != NULL); |
| g_return_if_fail (GST_IS_PAD (pad)); |
| g_return_if_fail (buf != NULL); |
| |
| median = GST_MEDIAN (GST_OBJECT_PARENT (pad)); |
| |
| if (!median->active) { |
| gst_pad_push (median->srcpad, GST_DATA (buf)); |
| return; |
| } |
| |
| data = GST_BUFFER_DATA (buf); |
| size = GST_BUFFER_SIZE (buf); |
| |
| GST_DEBUG ("median: have buffer of %d", GST_BUFFER_SIZE (buf)); |
| |
| outbuf = gst_buffer_new (); |
| GST_BUFFER_DATA (outbuf) = g_malloc (GST_BUFFER_SIZE (buf)); |
| GST_BUFFER_SIZE (outbuf) = GST_BUFFER_SIZE (buf); |
| |
| lumsize = median->width * median->height; |
| chromsize = lumsize / 4; |
| |
| if (median->filtersize == 5) { |
| median_5 (data, GST_BUFFER_DATA (outbuf), median->width, median->height); |
| if (!median->lum_only) { |
| median_5 (data + lumsize, GST_BUFFER_DATA (outbuf) + lumsize, |
| median->width / 2, median->height / 2); |
| median_5 (data + lumsize + chromsize, |
| GST_BUFFER_DATA (outbuf) + lumsize + chromsize, median->width / 2, |
| median->height / 2); |
| } else { |
| memcpy (GST_BUFFER_DATA (outbuf) + lumsize, data + lumsize, |
| chromsize * 2); |
| } |
| } else { |
| median_9 (data, GST_BUFFER_DATA (outbuf), median->width, median->height); |
| if (!median->lum_only) { |
| median_9 (data + lumsize, GST_BUFFER_DATA (outbuf) + lumsize, |
| median->width / 2, median->height / 2); |
| median_9 (data + lumsize + chromsize, |
| GST_BUFFER_DATA (outbuf) + lumsize + chromsize, median->width / 2, |
| median->height / 2); |
| } else { |
| memcpy (GST_BUFFER_DATA (outbuf) + lumsize, data + lumsize, |
| chromsize * 2); |
| } |
| } |
| GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf); |
| |
| gst_buffer_unref (buf); |
| |
| gst_pad_push (median->srcpad, GST_DATA (outbuf)); |
| } |
| |
| static void |
| gst_median_set_property (GObject * object, guint prop_id, const GValue * value, |
| GParamSpec * pspec) |
| { |
| GstMedian *median; |
| gint argvalue; |
| |
| g_return_if_fail (GST_IS_MEDIAN (object)); |
| median = GST_MEDIAN (object); |
| |
| switch (prop_id) { |
| case ARG_FILTERSIZE: |
| argvalue = g_value_get_int (value); |
| if (argvalue != 5 && argvalue != 9) { |
| g_warning ("median: invalid filtersize (%d), must be 5 or 9\n", |
| argvalue); |
| } else { |
| median->filtersize = argvalue; |
| } |
| break; |
| case ARG_ACTIVE: |
| median->active = g_value_get_boolean (value); |
| break; |
| case ARG_LUM_ONLY: |
| median->lum_only = g_value_get_boolean (value); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void |
| gst_median_get_property (GObject * object, guint prop_id, GValue * value, |
| GParamSpec * pspec) |
| { |
| GstMedian *median; |
| |
| g_return_if_fail (GST_IS_MEDIAN (object)); |
| median = GST_MEDIAN (object); |
| |
| switch (prop_id) { |
| case ARG_FILTERSIZE: |
| g_value_set_int (value, median->filtersize); |
| break; |
| case ARG_ACTIVE: |
| g_value_set_boolean (value, median->active); |
| break; |
| case ARG_LUM_ONLY: |
| g_value_set_boolean (value, median->lum_only); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| |
| static gboolean |
| plugin_init (GstPlugin * plugin) |
| { |
| return gst_element_register (plugin, "median", |
| GST_RANK_NONE, GST_TYPE_MEDIAN); |
| } |
| |
| GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, |
| GST_VERSION_MINOR, |
| "median", |
| "Video median filter", |
| plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) |