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