Initial revision
[platform/upstream/gst-plugins-good.git] / gst / median / gstmedian.c
1 /* Gnome-Streamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <string.h>
21 #include <gstmedian.h>
22
23
24 static GstElementDetails median_details = {
25   "Median effect",
26   "Filter/Effect",
27   "apply a median filter to an image",
28   VERSION,
29   "Wim Taymans <wim.taymans@chello.be>",
30   "(C) 2000",
31 };
32
33 GST_PADTEMPLATE_FACTORY (median_src_factory,
34   "src",
35   GST_PAD_SRC,
36   GST_PAD_ALWAYS,
37   GST_CAPS_NEW (
38    "median_src",
39    "video/raw",
40      "format",   GST_PROPS_FOURCC (GST_STR_FOURCC ("I420"))
41   )
42 )
43
44 GST_PADTEMPLATE_FACTORY (median_sink_factory,
45   "sink",
46   GST_PAD_SINK,
47   GST_PAD_ALWAYS,
48   GST_CAPS_NEW (
49    "median_src",
50    "video/raw",
51      "format",   GST_PROPS_FOURCC (GST_STR_FOURCC ("I420"))
52   )
53 )
54
55
56 /* Median signals and args */
57 enum {
58   /* FILL ME */
59   LAST_SIGNAL
60 };
61
62 enum {
63   ARG_0,
64   ARG_ACTIVE,
65   ARG_FILTERSIZE,
66   ARG_LUM_ONLY
67 };
68
69 static GType    gst_median_get_type     (void);
70 static void     gst_median_class_init   (GstMedianClass *klass);
71 static void     gst_median_init         (GstMedian *median);
72
73 static void     median_5                (unsigned char *src, unsigned char *dest, int height, int width);
74 static void     median_9                (unsigned char *src, unsigned char *dest, int height, int width);
75 static void     gst_median_chain        (GstPad *pad, GstBuffer *buf);
76
77 static void     gst_median_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
78 static void     gst_median_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
79
80 static GstElementClass *parent_class = NULL;
81 //static guint gst_median_signals[LAST_SIGNAL] = { 0 };
82
83 static GstPadNegotiateReturn
84 median_negotiate_src (GstPad *pad, GstCaps **caps, gpointer *data)
85 {
86   GstMedian* filter = GST_MEDIAN (gst_pad_get_parent (pad));
87
88   if (*caps==NULL)
89     return GST_PAD_NEGOTIATE_FAIL;
90
91   return gst_pad_negotiate_proxy (pad, filter->sinkpad, caps);
92 }
93
94 static GstPadNegotiateReturn
95 median_negotiate_sink (GstPad *pad, GstCaps **caps, gpointer *data)
96 {
97   GstMedian* filter = GST_MEDIAN (gst_pad_get_parent (pad));
98
99   if (*caps==NULL)
100     return GST_PAD_NEGOTIATE_FAIL;
101
102   return gst_pad_negotiate_proxy (pad, filter->srcpad, caps);
103 }
104
105 GType
106 gst_median_get_type (void)
107 {
108   static GType median_type = 0;
109
110   if (!median_type) {
111     static const GTypeInfo median_info = {
112       sizeof(GstMedianClass),      NULL,      NULL,      (GClassInitFunc)gst_median_class_init,
113       NULL,
114       NULL,
115       sizeof(GstMedian),
116       0,
117       (GInstanceInitFunc)gst_median_init,
118     };
119     median_type = g_type_register_static(GST_TYPE_ELEMENT, "GstMedian", &median_info, 0);
120   }
121   return median_type;
122 }
123
124 static void
125 gst_median_class_init (GstMedianClass *klass)
126 {
127   GObjectClass *gobject_class;
128   GstElementClass *gstelement_class;
129
130   gobject_class = (GObjectClass*)klass;
131   gstelement_class = (GstElementClass*)klass;
132
133   parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
134
135   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_ACTIVE,
136     g_param_spec_boolean("active","active","active",
137                          TRUE,G_PARAM_READWRITE)); // CHECKME
138   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FILTERSIZE,
139     g_param_spec_int("filtersize","filtersize","filtersize",
140                      G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); // CHECKME
141   g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_LUM_ONLY,
142     g_param_spec_boolean("lum_only","lum_only","lum_only",
143                          TRUE,G_PARAM_READWRITE)); // CHECKME
144
145   gobject_class->set_property = gst_median_set_property;
146   gobject_class->get_property = gst_median_get_property;
147 }
148
149 static void
150 gst_median_newcaps (GstPad *pad, GstCaps *caps)
151 {
152   GstMedian *filter;
153
154   filter = GST_MEDIAN (gst_pad_get_parent (pad));
155
156   filter->width = gst_caps_get_int (caps, "width");
157   filter->height = gst_caps_get_int (caps, "height");
158 }
159
160 void gst_median_init (GstMedian *median)
161 {
162   median->sinkpad = gst_pad_new_from_template (
163                   GST_PADTEMPLATE_GET (median_sink_factory), "sink");
164   gst_pad_set_negotiate_function (median->sinkpad, median_negotiate_sink);
165   gst_pad_set_newcaps_function (median->sinkpad, gst_median_newcaps);
166   gst_pad_set_chain_function (median->sinkpad, gst_median_chain);
167   gst_element_add_pad (GST_ELEMENT (median), median->sinkpad);
168
169   median->srcpad = gst_pad_new_from_template (
170                   GST_PADTEMPLATE_GET (median_src_factory), "src");
171   gst_pad_set_negotiate_function (median->srcpad, median_negotiate_src);
172   gst_element_add_pad (GST_ELEMENT (median), median->srcpad);
173
174   median->filtersize = 5;
175   median->lum_only = TRUE;
176   median->active = TRUE;
177 }
178
179 #define PIX_SORT(a,b) { if ((a)>(b)) PIX_SWAP((a),(b)); }
180 #define PIX_SWAP(a,b) { unsigned char temp=(a);(a)=(b);(b)=temp; }
181
182 static void
183 median_5 (unsigned char *src, unsigned char *dest, int width, int height)
184 {
185   int nLastRow;
186   int nLastCol;
187   unsigned char p[9];
188   int i, j, k;
189
190   nLastCol = width - 1;
191   nLastRow = height - 1;
192
193   //copy the top and bottom rows into the result array
194   for (i=0; i<width; i++) {
195     dest[i] = src[i];
196     dest[nLastRow * width + i] = src[nLastRow * width + i];
197   }
198   dest[i] = src[i];
199
200   nLastCol--;
201   nLastRow--;
202
203   /* process the interior pixels */
204   i = width + 1;
205   for (k=0; k < nLastRow; k++) {
206     for (j=0; j < nLastCol; j++, i++) {
207       p[0] = src[i-width];
208       p[1] = src[i-1];
209       p[2] = src[i];
210       p[3] = src[i+1];
211       p[4] = src[i+width];
212       PIX_SORT(p[0],p[1]) ; PIX_SORT(p[3],p[4]) ; PIX_SORT(p[0],p[3]) ;
213       PIX_SORT(p[1],p[4]) ; PIX_SORT(p[1],p[2]) ; PIX_SORT(p[2],p[3]) ;
214       PIX_SORT(p[1],p[2]) ;
215       dest[i] = p[2];
216     }
217     dest[i] = src[i++];
218     dest[i] = src[i++];
219   }
220   dest[i] = src[i++];
221 }
222
223 static void
224 median_9 (unsigned char *src, unsigned char *dest, int width, int height)
225 {
226   int nLastRow;
227   int nLastCol;
228   unsigned char p[9];
229   int i, j, k;
230
231   nLastCol = width - 1;
232   nLastRow = height - 1;
233
234   //copy the top and bottom rows into the result array
235   for (i=0; i<width; i++) {
236     dest[i] = src[i];
237     dest[nLastRow * width + i] = src[nLastRow * width + i];
238   }
239   dest[i] = src[i];
240
241   nLastCol--;
242   nLastRow--;
243
244   /* process the interior pixels */
245   i = width + 1;
246   for (k=0; k < nLastRow; k++) {
247     for (j=0; j < nLastCol; j++, i++) {
248       p[0] = src[i-width-1];
249       p[1] = src[i-width];
250       p[2] = src[i-width+1];
251       p[3] = src[i-1];
252       p[4] = src[i];
253       p[5] = src[i+1];
254       p[6] = src[i+width-1];
255       p[7] = src[i+width];
256       p[8] = src[i+width+1];
257       PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ; 
258       PIX_SORT(p[0], p[1]) ; PIX_SORT(p[3], p[4]) ; PIX_SORT(p[6], p[7]) ; 
259       PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ; 
260       PIX_SORT(p[0], p[3]) ; PIX_SORT(p[5], p[8]) ; PIX_SORT(p[4], p[7]) ; 
261       PIX_SORT(p[3], p[6]) ; PIX_SORT(p[1], p[4]) ; PIX_SORT(p[2], p[5]) ; 
262       PIX_SORT(p[4], p[7]) ; PIX_SORT(p[4], p[2]) ; PIX_SORT(p[6], p[4]) ; 
263       PIX_SORT(p[4], p[2]) ;
264       dest[i] = p[4];
265     }
266     dest[i] = src[i++];
267     dest[i] = src[i++];
268   }
269   dest[i] = src[i++];
270 }
271
272 static void
273 gst_median_chain (GstPad *pad, GstBuffer *buf)
274 {
275   GstMedian *median;
276   guchar *data;
277   gulong size;
278   GstBuffer *outbuf;
279 //  GstMeta *meta;
280   int lumsize, chromsize;
281
282   g_return_if_fail(pad != NULL);
283   g_return_if_fail(GST_IS_PAD(pad));
284   g_return_if_fail(buf != NULL);
285
286   median = GST_MEDIAN (GST_OBJECT_PARENT (pad));
287
288   if (!median->active) {
289     gst_pad_push(median->srcpad,buf);
290     return;
291   }
292
293   data = GST_BUFFER_DATA(buf);
294   size = GST_BUFFER_SIZE(buf);
295
296   GST_DEBUG (0,"median: have buffer of %d\n", GST_BUFFER_SIZE(buf));
297
298   outbuf = gst_buffer_new();
299   GST_BUFFER_DATA(outbuf) = g_malloc(GST_BUFFER_SIZE(buf));
300   GST_BUFFER_SIZE(outbuf) = GST_BUFFER_SIZE(buf);
301
302   lumsize = median->width * median->height;
303   chromsize = lumsize/4;
304
305   if (median->filtersize == 5) {
306     median_5(data, GST_BUFFER_DATA(outbuf), median->width, median->height);
307     if (!median->lum_only) {
308       median_5(data+lumsize, GST_BUFFER_DATA(outbuf)+lumsize, median->width/2, median->height/2);
309       median_5(data+lumsize+chromsize, GST_BUFFER_DATA(outbuf)+lumsize+chromsize, median->width/2, median->height/2);
310     }
311     else {
312       memcpy (GST_BUFFER_DATA (outbuf)+lumsize, data+lumsize, chromsize*2);
313     }
314   }
315   else {
316     median_9(data, GST_BUFFER_DATA(outbuf), median->width, median->height);
317     if (!median->lum_only) {
318       median_9(data+lumsize, GST_BUFFER_DATA(outbuf)+lumsize, median->width/2, median->height/2);
319       median_9(data+lumsize+chromsize, GST_BUFFER_DATA(outbuf)+lumsize+chromsize, median->width/2, median->height/2);
320     }
321     else {
322       memcpy (GST_BUFFER_DATA (outbuf)+lumsize, data+lumsize, chromsize*2);
323     }
324   }
325   GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf);
326
327   gst_buffer_unref(buf);
328
329   gst_pad_push(median->srcpad,outbuf);
330 }
331
332 static void
333 gst_median_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
334 {
335   GstMedian *median;
336   gint argvalue;
337
338   /* it's not null if we got it, but it might not be ours */
339   g_return_if_fail(GST_IS_MEDIAN(object));
340   median = GST_MEDIAN(object);
341
342   switch (prop_id) {
343     case ARG_FILTERSIZE:
344       argvalue = g_value_get_int (value);
345       if (argvalue != 5 && argvalue != 9) {
346         g_warning ("median: invalid filtersize (%d), must be 5 or 9\n", argvalue);
347       }
348       else {
349         median->filtersize = argvalue;
350       }
351       break;
352     case ARG_ACTIVE:
353       median->active = g_value_get_boolean (value);
354       break;
355     case ARG_LUM_ONLY:
356       median->lum_only = g_value_get_boolean (value);
357       break;
358     default:
359       break;
360   }
361 }
362
363 static void
364 gst_median_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
365 {
366   GstMedian *median;
367
368   /* it's not null if we got it, but it might not be ours */
369   g_return_if_fail(GST_IS_MEDIAN(object));
370   median = GST_MEDIAN(object);
371
372   switch (prop_id) {
373     case ARG_FILTERSIZE:
374       g_value_set_int (value, median->filtersize);
375       break;
376     case ARG_ACTIVE:
377       g_value_set_boolean (value, median->active);
378       break;
379     case ARG_LUM_ONLY:
380       g_value_set_boolean (value, median->lum_only);
381       break;
382     default:
383       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
384       break;
385   }
386 }
387
388
389 static gboolean
390 plugin_init (GModule *module, GstPlugin *plugin)
391 {
392   GstElementFactory *factory;
393
394   factory = gst_elementfactory_new("median",GST_TYPE_MEDIAN,
395                                    &median_details);
396   g_return_val_if_fail(factory != NULL, FALSE);
397
398   gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (median_sink_factory));
399   gst_elementfactory_add_padtemplate (factory, GST_PADTEMPLATE_GET (median_src_factory));
400
401   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
402
403   return TRUE;
404 }
405
406 GstPluginDesc plugin_desc = {
407   GST_VERSION_MAJOR,
408   GST_VERSION_MINOR,
409   "median",
410   plugin_init
411 };
412