2
* Copyright (C) 2006 Mark Nauwelaerts <mnauw@users.sourceforge.net>
4
* Avisynth filter plugin:
5
* Copyright (C) 2003 Donald A. Graft
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02110-1307 USA
23
* SECTION:element-kerneldeint
27
* [based on Avisynth filter documentation]
30
* This filter deinterlaces using a kernel approach.
31
* It gives greatly improved vertical resolution in deinterlaced areas
32
* compared to simple field discarding.
35
* If you set the <link linkend="GstKernelDeint--threshold">threshold</link>
36
* to 0, you can get totally artifact free results (due to lack of thresholding)
37
* but with much less loss of vertical resolution than simple field discarding.
38
* For optimal results, however, set a motion threshold that allows static
39
* areas of the picture to be passed through. In this mode, the kernel-based
40
* deinterlacing of the moving areas preserves their vertical resolution compared
41
* to simple interpolation.
44
* This filter assumes that top field is first. If not, use a filter (e.g. fields)
45
* to swap them. Note that whether the top field is indeed first can be verified
46
* by separating fields (using e.g. fields) and verifying that motion is progressive
47
* (and not jumping back and forth).
50
* The <link linkend="GstKernelDeint--threshold">threshold</link> parameter
51
* defines the "motion" thresold. Moving areas are kernel-deinterlaced while
52
* non-moving areas are passed through.
53
* Use the <link linkend="GstKernelDeint--map">map</link> parameter to tweak
54
* the threshold parameter so that just the combed areas of the frame are
58
* <link linkend="GstKernelDeint--sharp">sharp</link>, when set to true,
59
* selects a kernel that provides better vertical resolution and performs
60
* some sharpening of the video. For less sharpening but also less vertical
61
* resolution, set this parameter to false.
64
* <link linkend="GstKernelDeint--two-way">two-way</link>, when set to true,
65
* selects a kernel that includes both the previous and the following fields
66
* for deinterlacing. When set to false, the kernel includes only the previous
67
* field. The latter one-way kernel is faster, crisper, and gives less blending
68
* (this last advantage makes the filter perform better on anime).
71
* <link linkend="GstKernelDeint--map">map</link>, when set to true, shows
72
* the areas that are "moving" as determined by the
73
* <link linkend="GstKernelDeint--threshold">threshold</link> parameter
74
* and which will be kernel-deinterlaced.
76
* <title>History</title>
80
* Avisynth kerndeint filter [Donald A. Graft]
83
* Also available in mplayer (kerndeint filter)
86
* Also available in avidemux (kernel deinterlacer)
98
#include "plugin-vd.h"
104
#define GST_TYPE_KERNEL_DEINT \
105
(gst_kernel_deint_get_type())
106
#define GST_KERNEL_DEINT(obj) \
107
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_KERNEL_DEINT,GstKernelDeint))
108
#define GST_KERNEL_DEINT_CLASS(klass) \
109
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_KERNEL_DEINT,GstKernelDeintClass))
110
#define GST_IS_KERNEL_DEINT(obj) \
111
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_KERNEL_DEINT))
112
#define GST_IS_KERNEL_DEINT_CLASS(klass) \
113
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_KERNEL_DEINT))
116
typedef struct _GstKernelDeint GstKernelDeint;
117
typedef struct _GstKernelDeintClass GstKernelDeintClass;
119
struct _GstKernelDeint
121
GstVideoFilter videofilter;
127
gboolean sharp, two_way, map;
132
/* previous buffer */
137
struct _GstKernelDeintClass
139
GstVideoFilterClass parent_class;
142
GST_DEBUG_CATEGORY_STATIC (kernel_deint_debug);
143
#define GST_CAT_DEFAULT kernel_deint_debug
145
/* signals and args */
162
#define MIN_THRESHOLD 0
163
#define MAX_THRESHOLD 100
164
#define DEFAULT_THRESHOLD 10
165
#define DEFAULT_SHARP FALSE
166
#define DEFAULT_TWO_WAY FALSE
167
#define DEFAULT_MAP FALSE
176
static GstElementDetails kernel_deint_details =
177
GST_ELEMENT_DETAILS ("KernelDeint",
178
"Filter/Effect/Video",
179
"Adaptive kernel deinterlacer",
180
"Mark Nauwelaerts <mnauw@users.sourceforge.net>,\n"
183
static GstStaticPadTemplate gst_kernel_deint_src_template =
184
GST_STATIC_PAD_TEMPLATE (GST_BASE_TRANSFORM_SRC_NAME,
187
GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ IYUV, I420, YV12, YUY2, YUYV, YVYU }") " ; "
188
GST_VIDEO_CAPS_RGBx " ; " GST_VIDEO_CAPS_BGRx " ; "
189
GST_VIDEO_CAPS_xRGB " ; " GST_VIDEO_CAPS_xBGR)
192
static GstStaticPadTemplate gst_kernel_deint_sink_template =
193
GST_STATIC_PAD_TEMPLATE (GST_BASE_TRANSFORM_SINK_NAME,
196
GST_STATIC_CAPS (GST_VIDEO_CAPS_YUV ("{ IYUV, I420, YV12, YUY2, YUYV, YVYU }") " ; "
197
GST_VIDEO_CAPS_RGBx " ; " GST_VIDEO_CAPS_BGRx " ; "
198
GST_VIDEO_CAPS_xRGB " ; " GST_VIDEO_CAPS_xBGR)
201
static gboolean gst_kernel_deint_hook_caps (GstKernelDeint * filter,
202
GstCaps * incaps, GstCaps * outcaps);
203
static GstFlowReturn gst_kernel_deint_transform (GstBaseTransform * btrans,
204
GstBuffer * in, GstBuffer * out);
205
static gboolean gst_kernel_deint_start (GstBaseTransform * btrans);
206
static gboolean gst_kernel_deint_stop (GstBaseTransform * btrans);
208
static void gst_kernel_deint_set_property (GObject * object, guint prop_id,
209
const GValue * value, GParamSpec * pspec);
210
static void gst_kernel_deint_get_property (GObject * object, guint prop_id,
211
GValue * value, GParamSpec * pspec);
213
GST_BOILERPLATE (GstKernelDeint, gst_kernel_deint, GstVideoFilter, GST_TYPE_VIDEO_FILTER);
215
GST_VIDEO_FILTER_SET_CAPS_BOILERPLATE_FULL (GstKernelDeint, gst_kernel_deint,
216
gst_kernel_deint_hook_caps);
218
GST_VIDEO_FILTER_GET_UNIT_SIZE_BOILERPLATE (gst_kernel_deint);
221
gst_kernel_deint_base_init (gpointer g_class)
223
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
225
gst_element_class_set_details (element_class, &kernel_deint_details);
227
gst_element_class_add_pad_template (element_class,
228
gst_static_pad_template_get (&gst_kernel_deint_sink_template));
229
gst_element_class_add_pad_template (element_class,
230
gst_static_pad_template_get (&gst_kernel_deint_src_template));
234
gst_kernel_deint_class_init (GstKernelDeintClass * g_class)
236
GObjectClass *gobject_class;
237
GstBaseTransformClass *trans_class;
239
gobject_class = G_OBJECT_CLASS (g_class);
240
trans_class = GST_BASE_TRANSFORM_CLASS (g_class);
242
GST_DEBUG_CATEGORY_INIT (kernel_deint_debug, "kerneldeint", 0, "kerneldeint");
244
gobject_class->set_property = gst_kernel_deint_set_property;
245
gobject_class->get_property = gst_kernel_deint_get_property;
247
g_object_class_install_property (gobject_class, PROP_THRESHOLD,
248
g_param_spec_uint ("threshold", "Threshold",
250
MIN_THRESHOLD, MAX_THRESHOLD, DEFAULT_THRESHOLD,
251
G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
253
g_object_class_install_property (gobject_class, PROP_SHARP,
254
g_param_spec_boolean ("sharp", "Sharp",
255
"Enable/disable additional sharping",
256
DEFAULT_SHARP, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
258
g_object_class_install_property (gobject_class, PROP_TWO_WAY,
259
g_param_spec_boolean ("two-way", "Two-Way",
260
"Enable/disable two-way sharpening",
261
DEFAULT_TWO_WAY, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
263
g_object_class_install_property (gobject_class, PROP_MAP,
264
g_param_spec_boolean ("map", "Map",
265
"Paint/ignore pixels exceeding threshold",
266
DEFAULT_MAP, G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE));
268
trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_kernel_deint_set_caps);
269
trans_class->get_unit_size = GST_DEBUG_FUNCPTR (gst_kernel_deint_get_unit_size);
270
trans_class->transform = GST_DEBUG_FUNCPTR (gst_kernel_deint_transform);
271
trans_class->start = GST_DEBUG_FUNCPTR (gst_kernel_deint_start);
272
trans_class->stop = GST_DEBUG_FUNCPTR (gst_kernel_deint_stop);
276
gst_kernel_deint_init (GstKernelDeint * filter, GstKernelDeintClass * g_class)
278
filter->threshold = DEFAULT_THRESHOLD;
279
filter->sharp = DEFAULT_SHARP;
280
filter->two_way = DEFAULT_TWO_WAY;
281
filter->map = DEFAULT_MAP;
285
gst_kernel_deint_free (GstKernelDeint * filter)
288
gst_buffer_unref (filter->prev);
293
gst_kernel_deint_hook_caps (GstKernelDeint * filter, GstCaps * incaps,
296
GstStructure *structure;
297
gboolean ret = FALSE;
299
structure = gst_caps_get_structure (incaps, 0);
301
gst_kernel_deint_free (filter);
302
if (gst_structure_has_name (structure, "video/x-raw-rgb")) {
304
filter->img = IMGFMT_RGB;
308
if ((ret = gst_structure_get_fourcc (structure, "format", &fourcc)))
310
case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
311
case GST_MAKE_FOURCC ('Y', 'Y', 'Y', 'V'):
312
case GST_MAKE_FOURCC ('Y', 'V', 'Y', 'U'):
313
filter->img = IMGFMT_YUY2;
315
case GST_MAKE_FOURCC ('I', 'Y', 'U', 'V'):
316
case GST_MAKE_FOURCC ('I', '4', '2', '0'):
317
case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
319
filter->img = IMGFMT_YUV;
327
#define PROGRESSIVE 0x00000001
330
gst_kernel_deint_do_deint (GstKernelDeint * filter, guint8 * src, guint8 * dest,
333
const guint8 *srcp, *prvp,*prvp_saved, *prvpp, *prvpn, *prvppp, *prvpnn, *prvp4p, *prvp4n;
334
const guint8 *srcp_saved;
335
const guint8 *srcpp, *srcppp, *srcpn, *srcpnn, *srcp3p, *srcp3n, *srcp4p, *srcp4n;
347
guint32 pitch, height, width;
350
guint32 order, threshold;
351
guint8 sharp, twoway, map;
353
height = filter->height;
354
width = filter->width;
356
/* always considered top-field first */
358
threshold = filter->threshold;
359
sharp = filter->sharp;
360
twoway = filter->two_way;
363
for (z = 0; z < (filter->img == IMGFMT_YUV ? 3 : 1); z++) {
368
switch (filter->img) {
370
pitch = GST_VIDEO_I420_Y_ROWSTRIDE (width);
380
offset = GST_VIDEO_I420_U_OFFSET (width, height);
381
pitch = GST_VIDEO_I420_U_ROWSTRIDE (width);
384
offset = GST_VIDEO_I420_V_OFFSET (width, height);
385
pitch = GST_VIDEO_I420_V_ROWSTRIDE (width);
388
g_assert_not_reached ();
391
srcp = srcp_saved = src + offset;
392
dstp = dstp_saved = dest + offset;
393
prvp_saved = prvp = prev + offset;
404
srcp = srcp_saved + (1 - order) * src_pitch;
405
dstp = dstp_saved + (1 - order) * dst_pitch;
406
for (y = 0; y < h; y += 2) {
407
oil_memcpy (dstp, srcp, w);
408
srcp += 2 * src_pitch;
409
dstp += 2 * dst_pitch;
412
/* Copy through the lines that will be missed below. */
413
oil_memcpy (dstp_saved + order * dst_pitch,
414
srcp_saved + (1 - order) * src_pitch, w);
415
oil_memcpy (dstp_saved + (2 + order) * dst_pitch,
416
srcp_saved + (3 - order) * src_pitch, w);
417
oil_memcpy (dstp_saved + (h - 2 + order) * dst_pitch,
418
srcp_saved + (h - 1 - order) * src_pitch, w);
419
oil_memcpy (dstp_saved + (h - 4 + order) * dst_pitch,
420
srcp_saved + (h - 3 - order) * src_pitch, w);
422
/* For the other field choose adaptively between using the previous field
423
* or the interpolant from the current field. */
424
prvp = prvp_saved + 5 * src_pitch - (1 - order) * src_pitch;
425
prvpp = prvp - src_pitch;
426
prvppp = prvp - 2 * src_pitch;
427
prvp4p = prvp - 4 * src_pitch;
428
prvpn = prvp + src_pitch;
429
prvpnn = prvp + 2 * src_pitch;
430
prvp4n = prvp + 4 * src_pitch;
431
srcp = srcp_saved + 5 * src_pitch - (1 - order) * src_pitch;
432
srcpp = srcp - src_pitch;
433
srcppp = srcp - 2 * src_pitch;
434
srcp3p = srcp - 3 * src_pitch;
435
srcp4p = srcp - 4 * src_pitch;
436
srcpn = srcp + src_pitch;
437
srcpnn = srcp + 2 * src_pitch;
438
srcp3n = srcp + 3 * src_pitch;
439
srcp4n = srcp + 4 * src_pitch;
440
dstp = dstp_saved + 5 * dst_pitch - (1 - order) * dst_pitch;
441
for (y = 5 - (1 - order); y <= h - 5 - (1 - order); y+=2) {
442
for (x = 0; x < w; x++) {
443
if ((threshold == 0) ||
444
(abs ((int) prvp[x] - (int) srcp[x]) > threshold) ||
445
(abs ((int) prvpp[x] - (int) srcpp[x]) > threshold) ||
446
(abs ((int) prvpn[x] - (int) srcpn[x]) > threshold)) {
448
switch (filter->img) {
478
switch (filter->img) {
480
hi = (z == 0) ? 235 : 240;
488
hi = (x & 1) ? 240 : 235;
495
valf = + 0.526*((int) srcpp[x] +
497
+ 0.170 * ((int) srcp[x] + (int) prvp[x])
498
- 0.116 * ((int) srcppp[x] +
499
(int) srcpnn[x] + (int) prvppp[x] +
501
- 0.026 * ((int) srcp3p[x] +
503
+ 0.031 * ((int) srcp4p[x] +
504
(int) srcp4n[x] + (int) prvp4p[x] +
507
valf = + 0.526 * ((int) srcpp[x] +
509
+ 0.170 * ((int) prvp[x])
510
- 0.116 * ((int) prvppp[x] +
512
- 0.026 * ((int) srcp3p[x] +
514
+ 0.031 * ((int) prvp4p[x] +
521
dstp[x] = (int) valf;
525
val = (8 * ((int) srcpp[x] + (int) srcpn[x]) +
526
2 * ((int) srcp[x] + (int) prvp[x]) -
530
(int) (prvpnn[x])) >> 4;
532
val = (8 * ((int) srcpp[x] + (int) srcpn[x]) +
533
2 * ((int) prvp[x]) -
535
(int) (prvpnn[x])) >> 4;
549
prvp += 2 * src_pitch;
550
prvpp += 2 * src_pitch;
551
prvppp += 2 * src_pitch;
552
prvpn += 2 * src_pitch;
553
prvpnn += 2 * src_pitch;
554
prvp4p += 2 * src_pitch;
555
prvp4n += 2 * src_pitch;
556
srcp += 2 * src_pitch;
557
srcpp += 2 * src_pitch;
558
srcppp += 2 * src_pitch;
559
srcp3p += 2 * src_pitch;
560
srcp4p += 2 * src_pitch;
561
srcpn += 2 * src_pitch;
562
srcpnn += 2 * src_pitch;
563
srcp3n += 2 * src_pitch;
564
srcp4n += 2 * src_pitch;
565
dstp += 2 * dst_pitch;
571
gst_kernel_deint_transform (GstBaseTransform * btrans, GstBuffer * in, GstBuffer * out)
573
GstKernelDeint *filter = GST_KERNEL_DEINT (btrans);
575
gst_object_sync_values (G_OBJECT (btrans), GST_BUFFER_TIMESTAMP (in));
578
gst_kernel_deint_do_deint (filter, GST_BUFFER_DATA (in), GST_BUFFER_DATA (out),
579
GST_BUFFER_DATA (filter->prev));
583
gst_buffer_unref (filter->prev);
584
/* and make sure the current one sticks around */
593
gst_kernel_deint_start (GstBaseTransform * btrans)
599
gst_kernel_deint_stop (GstBaseTransform * btrans)
601
GstKernelDeint *filter;
603
filter = GST_KERNEL_DEINT (btrans);
605
gst_kernel_deint_free (filter);
611
gst_kernel_deint_set_property (GObject * object, guint prop_id,
612
const GValue * value, GParamSpec * pspec)
616
g_return_if_fail (GST_IS_KERNEL_DEINT (object));
617
src = GST_KERNEL_DEINT (object);
621
src->threshold = g_value_get_uint (value);
624
src->sharp = g_value_get_boolean (value);
627
src->two_way = g_value_get_boolean (value);
630
src->map = g_value_get_boolean (value);
633
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
639
gst_kernel_deint_get_property (GObject * object, guint prop_id, GValue * value,
644
g_return_if_fail (GST_IS_KERNEL_DEINT (object));
645
src = GST_KERNEL_DEINT (object);
649
g_value_set_uint (value, src->threshold);
652
g_value_set_boolean (value, src->sharp);
655
g_value_set_boolean (value, src->two_way);
658
g_value_set_boolean (value, src->map);
661
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);