2 * Copyright (c) 2013 Stefano Sabatini
4 * This file is part of FFmpeg.
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 * video quantizer filter based on ELBG
26 #include "libavcodec/elbg.h"
27 #include "libavutil/opt.h"
28 #include "libavutil/pixdesc.h"
29 #include "libavutil/random_seed.h"
32 #include "drawutils.h"
36 typedef struct ColorContext
{
39 unsigned int lfg_seed
;
43 int *codeword_closest_codebook_idxs
;
46 const AVPixFmtDescriptor
*pix_desc
;
50 #define OFFSET(x) offsetof(ELBGContext, x)
51 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
53 static const AVOption elbg_options
[] = {
54 { "codebook_length", "set codebook length", OFFSET(codebook_length
), AV_OPT_TYPE_INT
, { .i64
= 256 }, 1, INT_MAX
, FLAGS
},
55 { "l", "set codebook length", OFFSET(codebook_length
), AV_OPT_TYPE_INT
, { .i64
= 256 }, 1, INT_MAX
, FLAGS
},
56 { "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
},
57 { "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
},
58 { "seed", "set the random seed", OFFSET(lfg_seed
), AV_OPT_TYPE_INT
, {.i64
= -1}, -1, UINT32_MAX
, FLAGS
},
59 { "s", "set the random seed", OFFSET(lfg_seed
), AV_OPT_TYPE_INT
, { .i64
= -1 }, -1, UINT32_MAX
, FLAGS
},
63 AVFILTER_DEFINE_CLASS(elbg
);
65 static av_cold
int init(AVFilterContext
*ctx
)
67 ELBGContext
*elbg
= ctx
->priv
;
69 if (elbg
->lfg_seed
== -1)
70 elbg
->lfg_seed
= av_get_random_seed();
72 av_lfg_init(&elbg
->lfg
, elbg
->lfg_seed
);
76 static int query_formats(AVFilterContext
*ctx
)
78 static const enum PixelFormat pix_fmts
[] = {
79 AV_PIX_FMT_ARGB
, AV_PIX_FMT_RGBA
, AV_PIX_FMT_ABGR
, AV_PIX_FMT_BGRA
,
80 AV_PIX_FMT_RGB24
, AV_PIX_FMT_BGR24
,
84 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
89 #define NB_COMPONENTS 3
91 static int config_input(AVFilterLink
*inlink
)
93 AVFilterContext
*ctx
= inlink
->dst
;
94 ELBGContext
*elbg
= ctx
->priv
;
96 elbg
->pix_desc
= av_pix_fmt_desc_get(inlink
->format
);
97 elbg
->codeword_length
= inlink
->w
* inlink
->h
;
98 elbg
->codeword
= av_realloc_f(elbg
->codeword
, elbg
->codeword_length
,
99 NB_COMPONENTS
* sizeof(*elbg
->codeword
));
101 return AVERROR(ENOMEM
);
103 elbg
->codeword_closest_codebook_idxs
=
104 av_realloc_f(elbg
->codeword_closest_codebook_idxs
, elbg
->codeword_length
,
105 sizeof(*elbg
->codeword_closest_codebook_idxs
));
106 if (!elbg
->codeword_closest_codebook_idxs
)
107 return AVERROR(ENOMEM
);
109 elbg
->codebook
= av_realloc_f(elbg
->codebook
, elbg
->codebook_length
,
110 NB_COMPONENTS
* sizeof(*elbg
->codebook
));
112 return AVERROR(ENOMEM
);
114 ff_fill_rgba_map(elbg
->rgba_map
, inlink
->format
);
123 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*frame
)
125 ELBGContext
*elbg
= inlink
->dst
->priv
;
129 const uint8_t r_idx
= elbg
->rgba_map
[R
];
130 const uint8_t g_idx
= elbg
->rgba_map
[G
];
131 const uint8_t b_idx
= elbg
->rgba_map
[B
];
133 /* build the codeword */
136 for (i
= 0; i
< inlink
->h
; i
++) {
138 for (j
= 0; j
< inlink
->w
; j
++) {
139 elbg
->codeword
[k
++] = p
[r_idx
];
140 elbg
->codeword
[k
++] = p
[g_idx
];
141 elbg
->codeword
[k
++] = p
[b_idx
];
142 p
+= elbg
->pix_desc
->nb_components
;
144 p0
+= frame
->linesize
[0];
147 /* compute the codebook */
148 avpriv_init_elbg(elbg
->codeword
, NB_COMPONENTS
, elbg
->codeword_length
,
149 elbg
->codebook
, elbg
->codebook_length
, elbg
->max_steps_nb
,
150 elbg
->codeword_closest_codebook_idxs
, &elbg
->lfg
);
151 avpriv_do_elbg(elbg
->codeword
, NB_COMPONENTS
, elbg
->codeword_length
,
152 elbg
->codebook
, elbg
->codebook_length
, elbg
->max_steps_nb
,
153 elbg
->codeword_closest_codebook_idxs
, &elbg
->lfg
);
155 /* fill the output with the codebook values */
159 for (i
= 0; i
< inlink
->h
; i
++) {
161 for (j
= 0; j
< inlink
->w
; j
++) {
162 int cb_idx
= NB_COMPONENTS
* elbg
->codeword_closest_codebook_idxs
[k
++];
163 p
[r_idx
] = elbg
->codebook
[cb_idx
];
164 p
[g_idx
] = elbg
->codebook
[cb_idx
+1];
165 p
[b_idx
] = elbg
->codebook
[cb_idx
+2];
166 p
+= elbg
->pix_desc
->nb_components
;
168 p0
+= frame
->linesize
[0];
171 return ff_filter_frame(inlink
->dst
->outputs
[0], frame
);
174 static av_cold
void uninit(AVFilterContext
*ctx
)
176 ELBGContext
*elbg
= ctx
->priv
;
178 av_freep(&elbg
->codebook
);
179 av_freep(&elbg
->codeword
);
180 av_freep(&elbg
->codeword_closest_codebook_idxs
);
183 static const AVFilterPad elbg_inputs
[] = {
186 .type
= AVMEDIA_TYPE_VIDEO
,
187 .config_props
= config_input
,
188 .filter_frame
= filter_frame
,
194 static const AVFilterPad elbg_outputs
[] = {
197 .type
= AVMEDIA_TYPE_VIDEO
,
202 AVFilter ff_vf_elbg
= {
204 .description
= NULL_IF_CONFIG_SMALL("Apply posterize effect, using the ELBG algorithm."),
205 .priv_size
= sizeof(ELBGContext
),
206 .priv_class
= &elbg_class
,
207 .query_formats
= query_formats
,
210 .inputs
= elbg_inputs
,
211 .outputs
= elbg_outputs
,