1 /* GStreamer
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., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 #include <string.h>
24 #include "gstvideomedian.h"
25 
26 static GstStaticPadTemplate video_median_src_factory =
27 GST_STATIC_PAD_TEMPLATE ("src",
28     GST_PAD_SRC,
29     GST_PAD_ALWAYS,
30     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ I420, YV12 }"))
31     );
32 
33 static GstStaticPadTemplate video_median_sink_factory =
34 GST_STATIC_PAD_TEMPLATE ("sink",
35     GST_PAD_SINK,
36     GST_PAD_ALWAYS,
37     GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ I420, YV12 }"))
38     );
39 
40 
41 /* Median signals and args */
42 enum
43 {
44   /* FILL ME */
45   LAST_SIGNAL
46 };
47 
48 #define DEFAULT_FILTERSIZE   5
49 #define DEFAULT_LUM_ONLY     TRUE
50 enum
51 {
52   PROP_0,
53   PROP_FILTERSIZE,
54   PROP_LUM_ONLY
55 };
56 
57 #define GST_TYPE_VIDEO_MEDIAN_SIZE (gst_video_median_size_get_type())
58 
59 static const GEnumValue video_median_sizes[] = {
60   {GST_VIDEO_MEDIAN_SIZE_5, "Median of 5 neighbour pixels", "5"},
61   {GST_VIDEO_MEDIAN_SIZE_9, "Median of 9 neighbour pixels", "9"},
62   {0, NULL, NULL},
63 };
64 
65 static GType
gst_video_median_size_get_type(void)66 gst_video_median_size_get_type (void)
67 {
68   static GType video_median_size_type = 0;
69 
70   if (!video_median_size_type) {
71     video_median_size_type = g_enum_register_static ("GstVideoMedianSize",
72         video_median_sizes);
73   }
74   return video_median_size_type;
75 }
76 
77 #define gst_video_median_parent_class parent_class
78 G_DEFINE_TYPE (GstVideoMedian, gst_video_median, GST_TYPE_VIDEO_FILTER);
79 
80 static GstFlowReturn gst_video_median_transform_frame (GstVideoFilter * filter,
81     GstVideoFrame * in_frame, GstVideoFrame * out_frame);
82 
83 static void gst_video_median_set_property (GObject * object, guint prop_id,
84     const GValue * value, GParamSpec * pspec);
85 static void gst_video_median_get_property (GObject * object, guint prop_id,
86     GValue * value, GParamSpec * pspec);
87 
88 static void
gst_video_median_class_init(GstVideoMedianClass * klass)89 gst_video_median_class_init (GstVideoMedianClass * klass)
90 {
91   GObjectClass *gobject_class;
92   GstElementClass *gstelement_class;
93   GstVideoFilterClass *vfilter_class;
94 
95   gobject_class = (GObjectClass *) klass;
96   gstelement_class = (GstElementClass *) klass;
97   vfilter_class = (GstVideoFilterClass *) klass;
98 
99   gobject_class->set_property = gst_video_median_set_property;
100   gobject_class->get_property = gst_video_median_get_property;
101 
102   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FILTERSIZE,
103       g_param_spec_enum ("filtersize", "Filtersize", "The size of the filter",
104           GST_TYPE_VIDEO_MEDIAN_SIZE, DEFAULT_FILTERSIZE,
105           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
106 
107   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LUM_ONLY,
108       g_param_spec_boolean ("lum-only", "Lum Only", "Only apply filter on "
109           "luminance", DEFAULT_LUM_ONLY,
110           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
111 
112   gst_element_class_add_static_pad_template (gstelement_class,
113       &video_median_sink_factory);
114   gst_element_class_add_static_pad_template (gstelement_class,
115       &video_median_src_factory);
116   gst_element_class_set_static_metadata (gstelement_class, "Median effect",
117       "Filter/Effect/Video", "Apply a median filter to an image",
118       "Wim Taymans <wim.taymans@gmail.com>");
119 
120   vfilter_class->transform_frame =
121       GST_DEBUG_FUNCPTR (gst_video_median_transform_frame);
122 }
123 
124 void
gst_video_median_init(GstVideoMedian * median)125 gst_video_median_init (GstVideoMedian * median)
126 {
127   median->filtersize = DEFAULT_FILTERSIZE;
128   median->lum_only = DEFAULT_LUM_ONLY;
129 }
130 
131 #define PIX_SORT(a,b) { if ((a)>(b)) PIX_SWAP((a),(b)); }
132 #define PIX_SWAP(a,b) { unsigned char temp=(a);(a)=(b);(b)=temp; }
133 
134 static void
median_5(guint8 * dest,gint dstride,const guint8 * src,gint sstride,gint width,gint height)135 median_5 (guint8 * dest, gint dstride, const guint8 * src, gint sstride,
136     gint width, gint height)
137 {
138   unsigned char p[5];
139   int i, j, k;
140 
141   /* copy the top and bottom rows into the result array */
142   for (i = 0; i < width; i++) {
143     dest[i] = src[i];
144     dest[(height - 1) * dstride + i] = src[(height - 1) * sstride + i];
145   }
146 
147   /* process the interior pixels */
148   for (k = 2; k < height; k++) {
149     dest += dstride;
150     src += sstride;
151 
152     dest[0] = src[0];
153     for (j = 2, i = 1; j < width; j++, i++) {
154       p[0] = src[i - sstride];
155       p[1] = src[i - 1];
156       p[2] = src[i];
157       p[3] = src[i + 1];
158       p[4] = src[i + sstride];
159       PIX_SORT (p[0], p[1]);
160       PIX_SORT (p[3], p[4]);
161       PIX_SORT (p[0], p[3]);
162       PIX_SORT (p[1], p[4]);
163       PIX_SORT (p[1], p[2]);
164       PIX_SORT (p[2], p[3]);
165       PIX_SORT (p[1], p[2]);
166       dest[i] = p[2];
167     }
168     dest[i] = src[i];
169   }
170 }
171 
172 static void
median_9(guint8 * dest,gint dstride,const guint8 * src,gint sstride,gint width,gint height)173 median_9 (guint8 * dest, gint dstride, const guint8 * src, gint sstride,
174     gint width, gint height)
175 {
176   unsigned char p[9];
177   int i, j, k;
178 
179   /*copy the top and bottom rows into the result array */
180   for (i = 0; i < width; i++) {
181     dest[i] = src[i];
182     dest[(height - 1) * dstride + i] = src[(height - 1) * sstride + i];
183   }
184   /* process the interior pixels */
185   for (k = 2; k < height; k++) {
186     dest += dstride;
187     src += sstride;
188 
189     dest[0] = src[0];
190     for (j = 2, i = 1; j < width; j++, i++) {
191       p[0] = src[i - sstride - 1];
192       p[1] = src[i - sstride];
193       p[2] = src[i - sstride + 1];
194       p[3] = src[i - 1];
195       p[4] = src[i];
196       p[5] = src[i + 1];
197       p[6] = src[i + sstride - 1];
198       p[7] = src[i + sstride];
199       p[8] = src[i + sstride + 1];
200       PIX_SORT (p[1], p[2]);
201       PIX_SORT (p[4], p[5]);
202       PIX_SORT (p[7], p[8]);
203       PIX_SORT (p[0], p[1]);
204       PIX_SORT (p[3], p[4]);
205       PIX_SORT (p[6], p[7]);
206       PIX_SORT (p[1], p[2]);
207       PIX_SORT (p[4], p[5]);
208       PIX_SORT (p[7], p[8]);
209       PIX_SORT (p[0], p[3]);
210       PIX_SORT (p[5], p[8]);
211       PIX_SORT (p[4], p[7]);
212       PIX_SORT (p[3], p[6]);
213       PIX_SORT (p[1], p[4]);
214       PIX_SORT (p[2], p[5]);
215       PIX_SORT (p[4], p[7]);
216       PIX_SORT (p[4], p[2]);
217       PIX_SORT (p[6], p[4]);
218       PIX_SORT (p[4], p[2]);
219       dest[i] = p[4];
220     }
221     dest[i] = src[i];
222   }
223 }
224 
225 static GstFlowReturn
gst_video_median_transform_frame(GstVideoFilter * filter,GstVideoFrame * in_frame,GstVideoFrame * out_frame)226 gst_video_median_transform_frame (GstVideoFilter * filter,
227     GstVideoFrame * in_frame, GstVideoFrame * out_frame)
228 {
229   GstVideoMedian *median = GST_VIDEO_MEDIAN (filter);
230 
231   if (median->filtersize == 5) {
232     median_5 (GST_VIDEO_FRAME_PLANE_DATA (out_frame, 0),
233         GST_VIDEO_FRAME_PLANE_STRIDE (out_frame, 0),
234         GST_VIDEO_FRAME_PLANE_DATA (in_frame, 0),
235         GST_VIDEO_FRAME_PLANE_STRIDE (in_frame, 0),
236         GST_VIDEO_FRAME_WIDTH (in_frame), GST_VIDEO_FRAME_HEIGHT (in_frame));
237 
238     if (median->lum_only) {
239       gst_video_frame_copy_plane (out_frame, in_frame, 1);
240       gst_video_frame_copy_plane (out_frame, in_frame, 2);
241     } else {
242       median_5 (GST_VIDEO_FRAME_PLANE_DATA (out_frame, 1),
243           GST_VIDEO_FRAME_PLANE_STRIDE (out_frame, 1),
244           GST_VIDEO_FRAME_PLANE_DATA (in_frame, 1),
245           GST_VIDEO_FRAME_PLANE_STRIDE (in_frame, 1),
246           GST_VIDEO_FRAME_WIDTH (in_frame) / 2,
247           GST_VIDEO_FRAME_HEIGHT (in_frame) / 2);
248       median_5 (GST_VIDEO_FRAME_PLANE_DATA (out_frame, 2),
249           GST_VIDEO_FRAME_PLANE_STRIDE (out_frame, 2),
250           GST_VIDEO_FRAME_PLANE_DATA (in_frame, 2),
251           GST_VIDEO_FRAME_PLANE_STRIDE (in_frame, 2),
252           GST_VIDEO_FRAME_WIDTH (in_frame) / 2,
253           GST_VIDEO_FRAME_HEIGHT (in_frame) / 2);
254     }
255   } else {
256     median_9 (GST_VIDEO_FRAME_PLANE_DATA (out_frame, 0),
257         GST_VIDEO_FRAME_PLANE_STRIDE (out_frame, 0),
258         GST_VIDEO_FRAME_PLANE_DATA (in_frame, 0),
259         GST_VIDEO_FRAME_PLANE_STRIDE (in_frame, 0),
260         GST_VIDEO_FRAME_WIDTH (in_frame), GST_VIDEO_FRAME_HEIGHT (in_frame));
261 
262     if (median->lum_only) {
263       gst_video_frame_copy_plane (out_frame, in_frame, 1);
264       gst_video_frame_copy_plane (out_frame, in_frame, 2);
265     } else {
266       median_9 (GST_VIDEO_FRAME_PLANE_DATA (out_frame, 1),
267           GST_VIDEO_FRAME_PLANE_STRIDE (out_frame, 1),
268           GST_VIDEO_FRAME_PLANE_DATA (in_frame, 1),
269           GST_VIDEO_FRAME_PLANE_STRIDE (in_frame, 1),
270           GST_VIDEO_FRAME_WIDTH (in_frame) / 2,
271           GST_VIDEO_FRAME_HEIGHT (in_frame) / 2);
272       median_9 (GST_VIDEO_FRAME_PLANE_DATA (out_frame, 2),
273           GST_VIDEO_FRAME_PLANE_STRIDE (out_frame, 2),
274           GST_VIDEO_FRAME_PLANE_DATA (in_frame, 2),
275           GST_VIDEO_FRAME_PLANE_STRIDE (in_frame, 2),
276           GST_VIDEO_FRAME_WIDTH (in_frame) / 2,
277           GST_VIDEO_FRAME_HEIGHT (in_frame) / 2);
278     }
279   }
280 
281   return GST_FLOW_OK;
282 }
283 
284 static void
gst_video_median_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)285 gst_video_median_set_property (GObject * object, guint prop_id,
286     const GValue * value, GParamSpec * pspec)
287 {
288   GstVideoMedian *median;
289 
290   median = GST_VIDEO_MEDIAN (object);
291 
292   switch (prop_id) {
293     case PROP_FILTERSIZE:
294       median->filtersize = g_value_get_enum (value);
295       break;
296     case PROP_LUM_ONLY:
297       median->lum_only = g_value_get_boolean (value);
298       break;
299     default:
300       break;
301   }
302 }
303 
304 static void
gst_video_median_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)305 gst_video_median_get_property (GObject * object, guint prop_id, GValue * value,
306     GParamSpec * pspec)
307 {
308   GstVideoMedian *median;
309 
310   median = GST_VIDEO_MEDIAN (object);
311 
312   switch (prop_id) {
313     case PROP_FILTERSIZE:
314       g_value_set_enum (value, median->filtersize);
315       break;
316     case PROP_LUM_ONLY:
317       g_value_set_boolean (value, median->lum_only);
318       break;
319     default:
320       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
321       break;
322   }
323 }
324