1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2024-12-23 12:43:46 +02:00

lavfi: add kerndeint filter

This is a port of the kerndeint filter (libmpcodecs/vf_kerndeint) by
Donal A. Graft (original avisynth plugin author), and is based on the
work by Jérémy Tran <tran.jeremy.av@gmail.com> done for SOCIS 2012.
This commit is contained in:
Stefano Sabatini 2012-10-18 09:50:53 +02:00
parent 92f1bed14c
commit 172505b8bc
11 changed files with 389 additions and 3 deletions

View File

@ -55,6 +55,7 @@ version <next>:
- adobe and limelight publisher authentication in RTMP - adobe and limelight publisher authentication in RTMP
- data: URI scheme - data: URI scheme
- support building on the Plan 9 operating system - support building on the Plan 9 operating system
- kerndeint filter ported from MPlayer
version 1.0: version 1.0:

View File

@ -33,6 +33,7 @@ Specifically, the GPL parts of FFmpeg are
- vf_geq.c - vf_geq.c
- vf_hqdn3d.c - vf_hqdn3d.c
- vf_hue.c - vf_hue.c
- vf_kerndeint.c
- vf_mp.c - vf_mp.c
- vf_pp.c - vf_pp.c
- vf_smartblur.c - vf_smartblur.c

1
configure vendored
View File

@ -1987,6 +1987,7 @@ frei0r_src_filter_extralibs='$ldl'
geq_filter_deps="gpl" geq_filter_deps="gpl"
hqdn3d_filter_deps="gpl" hqdn3d_filter_deps="gpl"
hue_filter_deps="gpl" hue_filter_deps="gpl"
kerndeint_filter_deps="gpl"
movie_filter_deps="avcodec avformat" movie_filter_deps="avcodec avformat"
mp_filter_deps="gpl avcodec swscale inline_asm" mp_filter_deps="gpl avcodec swscale inline_asm"
mptestsrc_filter_deps="gpl" mptestsrc_filter_deps="gpl"

View File

@ -2810,6 +2810,63 @@ If a parameter is omitted, it is kept at its current value.
Interlaceing detect filter. This filter tries to detect if the input is Interlaceing detect filter. This filter tries to detect if the input is
interlaced or progressive. Top or bottom field first. interlaced or progressive. Top or bottom field first.
@section kerndeint
Deinterlace input video by applying Donald Graft's adaptive kernel
deinterling. Work on interlaced parts of a video to produce
progressive frames.
This filter accepts parameters as a list of @var{key}=@var{value}
pairs, separated by ":". If the key of the first options is omitted,
the arguments are interpreted according to the following syntax:
@var{thresh}:@var{map}:@var{order}:@var{sharp}:@var{twoway}.
The description of the accepted parameters follows.
@table @option
@item thresh
Set the threshold which affects the filter's tolerance when
determining if a pixel line must be processed. It must be an integer
in the range [0,255] and defaults to 10. A value of 0 will result in
applying the process on every pixels.
@item map
Paint pixels exceeding the threshold value to white if set to 1.
Default is 0.
@item order
Set the fields order. Swap fields if set to 1, leave fields alone if
0. Default is 0.
@item sharp
Enable additional sharpening if set to 1. Default is 0.
@item twoway
Enable twoway sharpening if set to 1. Default is 0.
@end table
@subsection Examples
@itemize
@item
Apply default values:
@example
kerndeint=thresh=10:map=0:order=0:sharp=0:twoway=0
@end example
@item
Enable additional sharpening:
@example
kerndeint=sharp=1
@end example
@item
Paint processed pixels in white:
@example
kerndeint=map=1
@end example
@end itemize
@section lut, lutrgb, lutyuv @section lut, lutrgb, lutyuv
Compute a look-up table for binding each pixel component input value Compute a look-up table for binding each pixel component input value

View File

@ -114,6 +114,7 @@ OBJS-$(CONFIG_HFLIP_FILTER) += vf_hflip.o
OBJS-$(CONFIG_HQDN3D_FILTER) += vf_hqdn3d.o OBJS-$(CONFIG_HQDN3D_FILTER) += vf_hqdn3d.o
OBJS-$(CONFIG_HUE_FILTER) += vf_hue.o OBJS-$(CONFIG_HUE_FILTER) += vf_hue.o
OBJS-$(CONFIG_IDET_FILTER) += vf_idet.o OBJS-$(CONFIG_IDET_FILTER) += vf_idet.o
OBJS-$(CONFIG_KERNDEINT_FILTER) += vf_kerndeint.o
OBJS-$(CONFIG_LUT_FILTER) += vf_lut.o OBJS-$(CONFIG_LUT_FILTER) += vf_lut.o
OBJS-$(CONFIG_LUTRGB_FILTER) += vf_lut.o OBJS-$(CONFIG_LUTRGB_FILTER) += vf_lut.o
OBJS-$(CONFIG_LUTYUV_FILTER) += vf_lut.o OBJS-$(CONFIG_LUTYUV_FILTER) += vf_lut.o

View File

@ -108,6 +108,7 @@ void avfilter_register_all(void)
REGISTER_FILTER(HQDN3D, hqdn3d, vf); REGISTER_FILTER(HQDN3D, hqdn3d, vf);
REGISTER_FILTER(HUE, hue, vf); REGISTER_FILTER(HUE, hue, vf);
REGISTER_FILTER(IDET, idet, vf); REGISTER_FILTER(IDET, idet, vf);
REGISTER_FILTER(KERNDEINT, kerndeint, vf);
REGISTER_FILTER(LUT, lut, vf); REGISTER_FILTER(LUT, lut, vf);
REGISTER_FILTER(LUTRGB, lutrgb, vf); REGISTER_FILTER(LUTRGB, lutrgb, vf);
REGISTER_FILTER(LUTYUV, lutyuv, vf); REGISTER_FILTER(LUTYUV, lutyuv, vf);

View File

@ -29,8 +29,8 @@
#include "libavutil/avutil.h" #include "libavutil/avutil.h"
#define LIBAVFILTER_VERSION_MAJOR 3 #define LIBAVFILTER_VERSION_MAJOR 3
#define LIBAVFILTER_VERSION_MINOR 30 #define LIBAVFILTER_VERSION_MINOR 31
#define LIBAVFILTER_VERSION_MICRO 104 #define LIBAVFILTER_VERSION_MICRO 100
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
LIBAVFILTER_VERSION_MINOR, \ LIBAVFILTER_VERSION_MINOR, \

318
libavfilter/vf_kerndeint.c Normal file
View File

@ -0,0 +1,318 @@
/*
* Copyright (c) 2012 Jeremy Tran
* Copyright (c) 2004 Tobias Diedrich
* Copyright (c) 2003 Donald A. Graft
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* FFmpeg 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with FFmpeg; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/**
* @file
* Kernel Deinterlacer
* Ported from MPlayer libmpcodecs/vf_kerndeint.c.
*/
#include "libavutil/imgutils.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
#include "avfilter.h"
#include "formats.h"
#include "internal.h"
typedef struct {
const AVClass *class;
int frame; ///< frame count, starting from 0
int thresh, map, order, sharp, twoway;
int vsub;
uint8_t *tmp_data [4]; ///< temporary plane data buffer
int tmp_bwidth[4]; ///< temporary plane byte width
int pixel_step;
} KerndeintContext;
#define OFFSET(x) offsetof(KerndeintContext, x)
#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption kerndeint_options[] = {
{ "thresh", "set the threshold", OFFSET(thresh), AV_OPT_TYPE_INT, {.i64=10}, 0, 255, FLAGS },
{ "map", "set the map", OFFSET(map), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS },
{ "order", "set the order", OFFSET(order), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS },
{ "sharp", "enable sharpening", OFFSET(sharp), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS },
{ "twoway", "enable twoway", OFFSET(twoway), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS },
{ NULL }
};
AVFILTER_DEFINE_CLASS(kerndeint);
static av_cold int init(AVFilterContext *ctx, const char *args)
{
KerndeintContext *kerndeint = ctx->priv;
const char const * shorthand[] = { "thresh", "map", "order", "sharp", "twoway", NULL };
kerndeint->class = &kerndeint_class;
av_opt_set_defaults(kerndeint);
return av_opt_set_from_string(kerndeint, args, shorthand, "=", ":");
}
static av_cold void uninit(AVFilterContext *ctx)
{
KerndeintContext *kerndeint = ctx->priv;
av_free(kerndeint->tmp_data[0]);
av_opt_free(kerndeint);
}
static int query_formats(AVFilterContext *ctx)
{
static const enum PixelFormat pix_fmts[] = {
AV_PIX_FMT_YUV420P,
AV_PIX_FMT_YUYV422,
AV_PIX_FMT_ARGB,
AV_PIX_FMT_NONE
};
ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
return 0;
}
static int config_props(AVFilterLink *inlink)
{
KerndeintContext *kerndeint = inlink->dst->priv;
const AVPixFmtDescriptor *desc = &av_pix_fmt_descriptors[inlink->format];
kerndeint->vsub = desc->log2_chroma_h;
kerndeint->pixel_step = av_get_bits_per_pixel(desc) >> 3;
return av_image_alloc(kerndeint->tmp_data, kerndeint->tmp_bwidth,
inlink->w, inlink->h, inlink->format, 1);
}
static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *inpic)
{
KerndeintContext *kerndeint = inlink->dst->priv;
AVFilterLink *outlink = inlink->dst->outputs[0];
AVFilterBufferRef *outpic;
const uint8_t *prvp; ///< Previous field's pixel line number n
const uint8_t *prvpp; ///< Previous field's pixel line number (n - 1)
const uint8_t *prvpn; ///< Previous field's pixel line number (n + 1)
const uint8_t *prvppp; ///< Previous field's pixel line number (n - 2)
const uint8_t *prvpnn; ///< Previous field's pixel line number (n + 2)
const uint8_t *prvp4p; ///< Previous field's pixel line number (n - 4)
const uint8_t *prvp4n; ///< Previous field's pixel line number (n + 4)
const uint8_t *srcp; ///< Current field's pixel line number n
const uint8_t *srcpp; ///< Current field's pixel line number (n - 1)
const uint8_t *srcpn; ///< Current field's pixel line number (n + 1)
const uint8_t *srcppp; ///< Current field's pixel line number (n - 2)
const uint8_t *srcpnn; ///< Current field's pixel line number (n + 2)
const uint8_t *srcp3p; ///< Current field's pixel line number (n - 3)
const uint8_t *srcp3n; ///< Current field's pixel line number (n + 3)
const uint8_t *srcp4p; ///< Current field's pixel line number (n - 4)
const uint8_t *srcp4n; ///< Current field's pixel line number (n + 4)
uint8_t *dstp, *dstp_saved;
const uint8_t *srcp_saved;
int src_linesize, psrc_linesize, dst_linesize, bwidth;
int x, y, plane, val, hi, lo, g, h, n = kerndeint->frame++;
double valf;
const int thresh = kerndeint->thresh;
const int order = kerndeint->order;
const int map = kerndeint->map;
const int sharp = kerndeint->sharp;
const int twoway = kerndeint->twoway;
outpic = ff_get_video_buffer(outlink, AV_PERM_WRITE|AV_PERM_ALIGN, outlink->w, outlink->h);
if (!outpic) {
avfilter_unref_bufferp(&inpic);
return AVERROR(ENOMEM);
}
avfilter_copy_buffer_ref_props(outpic, inpic);
outpic->video->interlaced = 0;
for (plane = 0; inpic->data[plane] && plane < 4; plane++) {
h = plane == 0 ? inlink->h : inlink->h >> kerndeint->vsub;
bwidth = kerndeint->tmp_bwidth[plane];
srcp = srcp_saved = inpic->data[plane];
src_linesize = inpic->linesize[plane];
psrc_linesize = outpic->linesize[plane];
dstp = dstp_saved = outpic->data[plane];
dst_linesize = outpic->linesize[plane];
srcp = srcp_saved + (1 - order) * src_linesize;
dstp = dstp_saved + (1 - order) * dst_linesize;
for (y = 0; y < h; y += 2) {
memcpy(dstp, srcp, bwidth);
srcp += 2 * src_linesize;
dstp += 2 * dst_linesize;
}
// Copy through the lines that will be missed below.
memcpy(dstp_saved + order * dst_linesize, srcp_saved + (1 - order) * src_linesize, bwidth);
memcpy(dstp_saved + (2 + order ) * dst_linesize, srcp_saved + (3 - order) * src_linesize, bwidth);
memcpy(dstp_saved + (h - 2 + order) * dst_linesize, srcp_saved + (h - 1 - order) * src_linesize, bwidth);
memcpy(dstp_saved + (h - 4 + order) * dst_linesize, srcp_saved + (h - 3 - order) * src_linesize, bwidth);
/* For the other field choose adaptively between using the previous field
or the interpolant from the current field. */
prvp = kerndeint->tmp_data[plane] + 5 * psrc_linesize - (1 - order) * psrc_linesize;
prvpp = prvp - psrc_linesize;
prvppp = prvp - 2 * psrc_linesize;
prvp4p = prvp - 4 * psrc_linesize;
prvpn = prvp + psrc_linesize;
prvpnn = prvp + 2 * psrc_linesize;
prvp4n = prvp + 4 * psrc_linesize;
srcp = srcp_saved + 5 * src_linesize - (1 - order) * src_linesize;
srcpp = srcp - src_linesize;
srcppp = srcp - 2 * src_linesize;
srcp3p = srcp - 3 * src_linesize;
srcp4p = srcp - 4 * src_linesize;
srcpn = srcp + src_linesize;
srcpnn = srcp + 2 * src_linesize;
srcp3n = srcp + 3 * src_linesize;
srcp4n = srcp + 4 * src_linesize;
dstp = dstp_saved + 5 * dst_linesize - (1 - order) * dst_linesize;
for (y = 5 - (1 - order); y <= h - 5 - (1 - order); y += 2) {
for (x = 0; x < bwidth; x++) {
if (thresh == 0 || n == 0 ||
(abs((int)prvp[x] - (int)srcp[x]) > thresh) ||
(abs((int)prvpp[x] - (int)srcpp[x]) > thresh) ||
(abs((int)prvpn[x] - (int)srcpn[x]) > thresh)) {
if (map) {
g = x & ~3;
if (inlink->format == AV_PIX_FMT_RGBA) {
AV_WB32(dstp + g, 0xffffffff);
x = g + 3;
} else if (inlink->format == AV_PIX_FMT_YUYV422) {
// y <- 235, u <- 128, y <- 235, v <- 128
AV_WB32(dstp + g, 0xeb80eb80);
x = g + 3;
} else {
dstp[x] = plane == 0 ? 235 : 128;
}
} else {
if (inlink->format == AV_PIX_FMT_RGBA) {
hi = 255;
lo = 0;
} else if (inlink->format == AV_PIX_FMT_YUYV422) {
hi = x & 1 ? 240 : 235;
lo = 16;
} else {
hi = plane == 0 ? 235 : 240;
lo = 16;
}
if (sharp) {
if (twoway) {
valf = + 0.526 * ((int)srcpp[x] + (int)srcpn[x])
+ 0.170 * ((int)srcp[x] + (int)prvp[x])
- 0.116 * ((int)srcppp[x] + (int)srcpnn[x] + (int)prvppp[x] + (int)prvpnn[x])
- 0.026 * ((int)srcp3p[x] + (int)srcp3n[x])
+ 0.031 * ((int)srcp4p[x] + (int)srcp4n[x] + (int)prvp4p[x] + (int)prvp4n[x]);
} else {
valf = + 0.526 * ((int)srcpp[x] + (int)srcpn[x])
+ 0.170 * ((int)prvp[x])
- 0.116 * ((int)prvppp[x] + (int)prvpnn[x])
- 0.026 * ((int)srcp3p[x] + (int)srcp3n[x])
+ 0.031 * ((int)prvp4p[x] + (int)prvp4p[x]);
}
dstp[x] = av_clip(valf, lo, hi);
} else {
if (twoway) {
val = (8 * ((int)srcpp[x] + (int)srcpn[x]) + 2 * ((int)srcp[x] + (int)prvp[x])
- (int)(srcppp[x]) - (int)(srcpnn[x])
- (int)(prvppp[x]) - (int)(prvpnn[x])) >> 4;
} else {
val = (8 * ((int)srcpp[x] + (int)srcpn[x]) + 2 * ((int)prvp[x])
- (int)(prvppp[x]) - (int)(prvpnn[x])) >> 4;
}
dstp[x] = av_clip(val, lo, hi);
}
}
} else {
dstp[x] = srcp[x];
}
}
prvp += 2 * psrc_linesize;
prvpp += 2 * psrc_linesize;
prvppp += 2 * psrc_linesize;
prvpn += 2 * psrc_linesize;
prvpnn += 2 * psrc_linesize;
prvp4p += 2 * psrc_linesize;
prvp4n += 2 * psrc_linesize;
srcp += 2 * src_linesize;
srcpp += 2 * src_linesize;
srcppp += 2 * src_linesize;
srcp3p += 2 * src_linesize;
srcp4p += 2 * src_linesize;
srcpn += 2 * src_linesize;
srcpnn += 2 * src_linesize;
srcp3n += 2 * src_linesize;
srcp4n += 2 * src_linesize;
dstp += 2 * dst_linesize;
}
srcp = inpic->data[plane];
dstp = kerndeint->tmp_data[plane];
av_image_copy_plane(dstp, psrc_linesize, srcp, src_linesize, bwidth, h);
}
avfilter_unref_buffer(inpic);
return ff_filter_frame(outlink, outpic);
}
static const AVFilterPad kerndeint_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.filter_frame = filter_frame,
.config_props = config_props,
.min_perms = AV_PERM_READ,
},
{ NULL }
};
static const AVFilterPad kerndeint_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
},
{ NULL }
};
AVFilter avfilter_vf_kerndeint = {
.name = "kerndeint",
.description = NULL_IF_CONFIG_SMALL("Apply kernel deinterlacing to the input."),
.priv_size = sizeof(KerndeintContext),
.init = init,
.uninit = uninit,
.query_formats = query_formats,
.inputs = kerndeint_inputs,
.outputs = kerndeint_outputs,
.priv_class = &kerndeint_class,
};

View File

@ -11,6 +11,7 @@ FATE_LAVFI = fate-lavfi-alphaextract_rgb \
fate-lavfi-fade \ fate-lavfi-fade \
fate-lavfi-field \ fate-lavfi-field \
fate-lavfi-idet \ fate-lavfi-idet \
fate-lavfi-kerndeint \
fate-lavfi-life \ fate-lavfi-life \
fate-lavfi-null \ fate-lavfi-null \
fate-lavfi-overlay \ fate-lavfi-overlay \

View File

@ -80,6 +80,7 @@ do_lavfi_pixfmts(){
test ${test%_[bl]e} = $testname || return 0 test ${test%_[bl]e} = $testname || return 0
filter=$2 filter=$2
filter_args=$3 filter_args=$3
prefilter_chain=$4
showfiltfmts="$target_exec $target_path/libavfilter/filtfmts-test" showfiltfmts="$target_exec $target_path/libavfilter/filtfmts-test"
scale_exclude_fmts=${outfile}${testname}_scale_exclude_fmts scale_exclude_fmts=${outfile}${testname}_scale_exclude_fmts
@ -96,7 +97,7 @@ do_lavfi_pixfmts(){
pix_fmts=$(comm -12 $scale_exclude_fmts $in_fmts) pix_fmts=$(comm -12 $scale_exclude_fmts $in_fmts)
for pix_fmt in $pix_fmts; do for pix_fmt in $pix_fmts; do
do_video_filter $pix_fmt "format=$pix_fmt,$filter=$filter_args" -pix_fmt $pix_fmt do_video_filter $pix_fmt "${prefilter_chain}format=$pix_fmt,$filter=$filter_args" -pix_fmt $pix_fmt
done done
rm $in_fmts $scale_in_fmts $scale_out_fmts $scale_exclude_fmts rm $in_fmts $scale_in_fmts $scale_out_fmts $scale_exclude_fmts
@ -104,6 +105,7 @@ do_lavfi_pixfmts(){
# all these filters have exactly one input and exactly one output # all these filters have exactly one input and exactly one output
do_lavfi_pixfmts "field" "field" "bottom" do_lavfi_pixfmts "field" "field" "bottom"
do_lavfi_pixfmts "kerndeint" "kerndeint" "" "tinterlace=interleave_top,"
do_lavfi_pixfmts "pixfmts_copy" "copy" "" do_lavfi_pixfmts "pixfmts_copy" "copy" ""
do_lavfi_pixfmts "pixfmts_crop" "crop" "100:100:100:100" do_lavfi_pixfmts "pixfmts_crop" "crop" "100:100:100:100"
do_lavfi_pixfmts "pixfmts_hflip" "hflip" "" do_lavfi_pixfmts "pixfmts_hflip" "hflip" ""

View File

@ -0,0 +1,3 @@
argb 484893f83e13c937328f13a7c84d2f50
yuv420p a935cce07c5287b92c6d5220361866ed
yuyv422 f549c98059ba9ce50e28204256d13b5d