lavfi: add elbg filter

This commit is contained in:
Stefano Sabatini 2013-11-10 23:46:41 +01:00
parent fe55c31976
commit 8cd3685a3f
6 changed files with 244 additions and 2 deletions

View File

@ -6,6 +6,7 @@ version <next>
- HNM version 4 demuxer and video decoder
- Live HDS muxer
- setsar/setdar filters now support variables in ratio expressions
- elbg filter
version 2.1:

View File

@ -3790,6 +3790,32 @@ ffmpeg -i video.avi -filter_complex 'extractplanes=y+u+v[y][u][v]' -map '[y]' y.
@end example
@end itemize
@section elbg
Apply a posterize effect using the ELBG (Enhanced LBG) algorithm.
For each input image, the filter will compute the optimal mapping from
the input to the output given the codebook length, that is the number
of distinct output colors.
This filter accepts the following options.
@table @option
@item codebook_length, l
Set codebook length. The value must be a positive integer, and
represents the number of distinct output colors. Default value is 256.
@item nb_steps, n
Set the maximum number of iterations to apply for computing the optimal
mapping. The higher the value the better the result and the higher the
computation time. Default value is 1.
@item seed, s
Set a random seed, must be an integer included between 0 and
UINT32_MAX. If not specified, or if explicitly set to -1, the filter
will try to use a good random seed on a best effort basis.
@end table
@section fade
Apply fade-in/out effect to input video.

View File

@ -9,6 +9,7 @@ FFLIBS-$(CONFIG_ASYNCTS_FILTER) += avresample
FFLIBS-$(CONFIG_ATEMPO_FILTER) += avcodec
FFLIBS-$(CONFIG_DECIMATE_FILTER) += avcodec
FFLIBS-$(CONFIG_DESHAKE_FILTER) += avcodec
FFLIBS-$(CONFIG_ELBG_FILTER) += avcodec
FFLIBS-$(CONFIG_MCDEINT_FILTER) += avcodec
FFLIBS-$(CONFIG_MOVIE_FILTER) += avformat avcodec
FFLIBS-$(CONFIG_MP_FILTER) += avcodec
@ -130,6 +131,7 @@ OBJS-$(CONFIG_DESHAKE_FILTER) += vf_deshake.o
OBJS-$(CONFIG_DRAWBOX_FILTER) += vf_drawbox.o
OBJS-$(CONFIG_DRAWGRID_FILTER) += vf_drawbox.o
OBJS-$(CONFIG_DRAWTEXT_FILTER) += vf_drawtext.o
OBJS-$(CONFIG_ELBG_FILTER) += vf_elbg.o
OBJS-$(CONFIG_EDGEDETECT_FILTER) += vf_edgedetect.o
OBJS-$(CONFIG_EXTRACTPLANES_FILTER) += vf_extractplanes.o
OBJS-$(CONFIG_FADE_FILTER) += vf_fade.o

View File

@ -127,6 +127,7 @@ void avfilter_register_all(void)
REGISTER_FILTER(DRAWGRID, drawgrid, vf);
REGISTER_FILTER(DRAWTEXT, drawtext, vf);
REGISTER_FILTER(EDGEDETECT, edgedetect, vf);
REGISTER_FILTER(ELBG, elbg, vf);
REGISTER_FILTER(EXTRACTPLANES, extractplanes, vf);
REGISTER_FILTER(FADE, fade, vf);
REGISTER_FILTER(FIELD, field, vf);

View File

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

212
libavfilter/vf_elbg.c Normal file
View File

@ -0,0 +1,212 @@
/*
* Copyright (c) 2013 Stefano Sabatini
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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
* video quantizer filter based on ELBG
*/
#include "libavcodec/elbg.h"
#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
#include "libavutil/random_seed.h"
#include "avfilter.h"
#include "drawutils.h"
#include "internal.h"
#include "video.h"
typedef struct {
const AVClass *class;
AVLFG lfg;
int lfg_seed;
int max_steps_nb;
int *codeword;
int codeword_length;
int *codeword_closest_codebook_idxs;
int *codebook;
int codebook_length;
const AVPixFmtDescriptor *pix_desc;
uint8_t rgba_map[4];
} ELBGContext;
#define OFFSET(x) offsetof(ELBGContext, x)
#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption elbg_options[] = {
{ "codebook_length", "set codebook length", OFFSET(codebook_length), AV_OPT_TYPE_INT, { .i64 = 256 }, 1, INT_MAX, FLAGS },
{ "l", "set codebook length", OFFSET(codebook_length), AV_OPT_TYPE_INT, { .i64 = 256 }, 1, INT_MAX, FLAGS },
{ "nb_steps", "set max number of steps used to compute the mapping", OFFSET(max_steps_nb), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, INT_MAX, FLAGS },
{ "n", "set max number of steps used to compute the mapping", OFFSET(max_steps_nb), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, INT_MAX, FLAGS },
{ "seed", "set the random seed", OFFSET(lfg_seed), AV_OPT_TYPE_INT, {.i64 = -1}, -1, UINT32_MAX, FLAGS },
{ "s", "set the random seed", OFFSET(lfg_seed), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS },
{ NULL }
};
AVFILTER_DEFINE_CLASS(elbg);
static av_cold int init(AVFilterContext *ctx)
{
ELBGContext *elbg = ctx->priv;
if (elbg->lfg_seed == -1)
elbg->lfg_seed = av_get_random_seed();
av_lfg_init(&elbg->lfg, elbg->lfg_seed);
return 0;
}
static int query_formats(AVFilterContext *ctx)
{
static const enum PixelFormat pix_fmts[] = {
AV_PIX_FMT_ARGB, AV_PIX_FMT_RGBA, AV_PIX_FMT_ABGR, AV_PIX_FMT_BGRA,
AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
AV_PIX_FMT_NONE
};
ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
return 0;
}
#define NB_COMPONENTS 3
static int config_input(AVFilterLink *inlink)
{
AVFilterContext *ctx = inlink->dst;
ELBGContext *elbg = ctx->priv;
elbg->pix_desc = av_pix_fmt_desc_get(inlink->format);
elbg->codeword_length = inlink->w * inlink->h;
elbg->codeword = av_realloc_f(elbg->codeword, elbg->codeword_length,
NB_COMPONENTS * sizeof(*elbg->codeword));
if (!elbg->codeword)
return AVERROR(ENOMEM);
elbg->codeword_closest_codebook_idxs =
av_realloc_f(elbg->codeword_closest_codebook_idxs, elbg->codeword_length,
sizeof(*elbg->codeword_closest_codebook_idxs));
if (!elbg->codeword_closest_codebook_idxs)
return AVERROR(ENOMEM);
elbg->codebook = av_realloc_f(elbg->codebook, elbg->codebook_length,
NB_COMPONENTS * sizeof(*elbg->codebook));
if (!elbg->codebook)
return AVERROR(ENOMEM);
ff_fill_rgba_map(elbg->rgba_map, inlink->format);
return 0;
}
#define R 0
#define G 1
#define B 2
static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
{
ELBGContext *elbg = inlink->dst->priv;
int i, j, k;
uint8_t *p, *p0;
const uint8_t r_idx = elbg->rgba_map[R];
const uint8_t g_idx = elbg->rgba_map[G];
const uint8_t b_idx = elbg->rgba_map[B];
/* build the codeword */
p0 = frame->data[0];
k = 0;
for (i = 0; i < inlink->h; i++) {
p = p0;
for (j = 0; j < inlink->w; j++) {
elbg->codeword[k++] = p[r_idx];
elbg->codeword[k++] = p[g_idx];
elbg->codeword[k++] = p[b_idx];
p += elbg->pix_desc->nb_components;
}
p0 += frame->linesize[0];
}
/* compute the codebook */
avpriv_init_elbg(elbg->codeword, NB_COMPONENTS, elbg->codeword_length,
elbg->codebook, elbg->codebook_length, elbg->max_steps_nb,
elbg->codeword_closest_codebook_idxs, &elbg->lfg);
avpriv_do_elbg(elbg->codeword, NB_COMPONENTS, elbg->codeword_length,
elbg->codebook, elbg->codebook_length, elbg->max_steps_nb,
elbg->codeword_closest_codebook_idxs, &elbg->lfg);
/* fill the output with the codebook values */
p0 = frame->data[0];
k = 0;
for (i = 0; i < inlink->h; i++) {
p = p0;
for (j = 0; j < inlink->w; j++) {
int cb_idx = NB_COMPONENTS * elbg->codeword_closest_codebook_idxs[k++];
p[r_idx] = elbg->codebook[cb_idx];
p[g_idx] = elbg->codebook[cb_idx+1];
p[b_idx] = elbg->codebook[cb_idx+2];
p += elbg->pix_desc->nb_components;
}
p0 += frame->linesize[0];
}
return ff_filter_frame(inlink->dst->outputs[0], frame);
}
static av_cold void uninit(AVFilterContext *ctx)
{
ELBGContext *elbg = ctx->priv;
av_freep(&elbg->codebook);
av_freep(&elbg->codeword);
av_freep(&elbg->codeword_closest_codebook_idxs);
}
static const AVFilterPad elbg_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.config_props = config_input,
.filter_frame = filter_frame,
.needs_writable = 1,
},
{ NULL }
};
static const AVFilterPad elbg_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
},
{ NULL }
};
AVFilter ff_vf_elbg = {
.name = "elbg",
.description = NULL_IF_CONFIG_SMALL("Apply posterize effect, using the ELBG algorithm."),
.priv_size = sizeof(ELBGContext),
.priv_class = &elbg_class,
.query_formats = query_formats,
.init = init,
.uninit = uninit,
.inputs = elbg_inputs,
.outputs = elbg_outputs,
};