[FFmpeg-devel] [PATCH] avfilter/palettegen, paletteuse: Extend the palette conversion filters to support palettes with alpha
Michael Niedermayer
michael at niedermayer.cc
Sun Sep 26 20:01:17 EEST 2021
On Sun, Sep 26, 2021 at 12:43:37AM +0000, Soft Works wrote:
> Usage example:
>
> ffmpeg -y -loglevel verbose -i "..\fate-suite\apng\o_sample.png" -filter_complex "split[split1][split2];[split1]palettegen=max_colors=254:use_alpha=1[pal1];[split2][pal1]paletteuse=use_alpha=1" -frames:v 1 out.png
>
> Signed-off-by: softworkz <softworkz at hotmail.com>
> ---
> doc/filters.texi | 8 ++
> libavfilter/vf_palettegen.c | 140 ++++++++++++++--------
> libavfilter/vf_paletteuse.c | 225 +++++++++++++++++++++---------------
> 3 files changed, 233 insertions(+), 140 deletions(-)
>
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 36113e5c4b..7e4806235c 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -16454,6 +16454,9 @@ Compute new histogram for each frame.
> @end table
>
> Default value is @var{full}.
> + at item use_alpha
> +Create a palette of colors with alpha components.
> +Setting this, will automatically disable 'reserve_transparent'.
> @end table
>
> The filter also exports the frame metadata @code{lavfi.color_quant_ratio}
> @@ -16532,6 +16535,11 @@ will be treated as completely opaque, and values below this threshold will be
> treated as completely transparent.
>
> The option must be an integer value in the range [0,255]. Default is @var{128}.
> +
> + at item use_alpha
> +Apply the palette by taking alpha values into account. Only useful with
> +palettes that are containing multiple colors with alpha components.
> +Setting this will automatically disable 'alpha_treshold'.
> @end table
>
> @subsection Examples
> diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
> index 4c2fbd36d7..7a74a3752f 100644
> --- a/libavfilter/vf_palettegen.c
> +++ b/libavfilter/vf_palettegen.c
> @@ -59,7 +59,7 @@ enum {
> };
>
> #define NBITS 5
> -#define HIST_SIZE (1<<(3*NBITS))
> +#define HIST_SIZE (1<<(4*NBITS))
>
> typedef struct PaletteGenContext {
> const AVClass *class;
> @@ -67,6 +67,7 @@ typedef struct PaletteGenContext {
> int max_colors;
> int reserve_transparent;
> int stats_mode;
> + int use_alpha;
>
> AVFrame *prev_frame; // previous frame used for the diff stats_mode
> struct hist_node histogram[HIST_SIZE]; // histogram/hashtable of the colors
> @@ -88,6 +89,7 @@ static const AVOption palettegen_options[] = {
> { "full", "compute full frame histograms", 0, AV_OPT_TYPE_CONST, {.i64=STATS_MODE_ALL_FRAMES}, INT_MIN, INT_MAX, FLAGS, "mode" },
> { "diff", "compute histograms only for the part that differs from previous frame", 0, AV_OPT_TYPE_CONST, {.i64=STATS_MODE_DIFF_FRAMES}, INT_MIN, INT_MAX, FLAGS, "mode" },
> { "single", "compute new histogram for each frame", 0, AV_OPT_TYPE_CONST, {.i64=STATS_MODE_SINGLE_FRAMES}, INT_MIN, INT_MAX, FLAGS, "mode" },
> + { "use_alpha", "create a palette including alpha values", OFFSET(use_alpha), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS },
> { NULL }
> };
>
> @@ -113,15 +115,16 @@ static int cmp_##name(const void *pa, const void *pb) \
> { \
> const struct color_ref * const *a = pa; \
> const struct color_ref * const *b = pb; \
> - return (int)((*a)->color >> (8 * (2 - (pos))) & 0xff) \
> - - (int)((*b)->color >> (8 * (2 - (pos))) & 0xff); \
> + return (int)((*a)->color >> (8 * (3 - (pos))) & 0xff) \
> + - (int)((*b)->color >> (8 * (3 - (pos))) & 0xff); \
> }
>
> -DECLARE_CMP_FUNC(r, 0)
> -DECLARE_CMP_FUNC(g, 1)
> -DECLARE_CMP_FUNC(b, 2)
> +DECLARE_CMP_FUNC(a, 0)
> +DECLARE_CMP_FUNC(r, 1)
> +DECLARE_CMP_FUNC(g, 2)
> +DECLARE_CMP_FUNC(b, 3)
>
> -static const cmp_func cmp_funcs[] = {cmp_r, cmp_g, cmp_b};
> +static const cmp_func cmp_funcs[] = {cmp_a, cmp_r, cmp_g, cmp_b};
>
> /**
> * Simple color comparison for sorting the final palette
> @@ -143,6 +146,17 @@ static av_always_inline int diff(const uint32_t a, const uint32_t b)
> return dr*dr + dg*dg + db*db;
> }
>
> +static av_always_inline int diff_alpha(const uint32_t a, const uint32_t b)
> +{
> + const uint8_t c1[] = {a >> 24 & 0xff, a >> 16 & 0xff, a >> 8 & 0xff, a & 0xff};
> + const uint8_t c2[] = {b >> 24 & 0xff, b >> 16 & 0xff, b >> 8 & 0xff, b & 0xff};
> + const int da = c1[0] - c2[0];
> + const int dr = c1[1] - c2[1];
> + const int dg = c1[2] - c2[2];
> + const int db = c1[3] - c2[3];
> + return da*da + dr*dr + dg*dg + db*db;
> +}
> +
> /**
> * Find the next box to split: pick the one with the highest variance
> */
> @@ -164,7 +178,10 @@ static int get_next_box_id_to_split(PaletteGenContext *s)
>
> for (i = 0; i < box->len; i++) {
> const struct color_ref *ref = s->refs[box->start + i];
> - variance += diff(ref->color, box->color) * ref->count;
> + if (s->use_alpha)
> + variance += (int64_t)diff_alpha(ref->color, box->color) * ref->count;
> + else
> + variance += (int64_t)diff(ref->color, box->color) * ref->count;
> }
> box->variance = variance;
> }
> @@ -184,24 +201,31 @@ static int get_next_box_id_to_split(PaletteGenContext *s)
> * specified box. Takes into account the weight of each color.
> */
> static uint32_t get_avg_color(struct color_ref * const *refs,
> - const struct range_box *box)
> + const struct range_box *box, int use_alpha)
> {
> int i;
> const int n = box->len;
> - uint64_t r = 0, g = 0, b = 0, div = 0;
> + uint64_t a = 0, r = 0, g = 0, b = 0, div = 0;
>
> for (i = 0; i < n; i++) {
> const struct color_ref *ref = refs[box->start + i];
> - r += (ref->color >> 16 & 0xff) * ref->count;
> - g += (ref->color >> 8 & 0xff) * ref->count;
> - b += (ref->color & 0xff) * ref->count;
> + if (use_alpha)
> + a += (ref->color >> 24 & 0xff) * ref->count;
> + r += (ref->color >> 16 & 0xff) * ref->count;
> + g += (ref->color >> 8 & 0xff) * ref->count;
> + b += (ref->color & 0xff) * ref->count;
> div += ref->count;
> }
>
> + if (use_alpha)
> + a = a / div;
> r = r / div;
> g = g / div;
> b = b / div;
>
> + if (use_alpha)
> + return a<<24 | r<<16 | g<<8 | b;
> +
> return 0xffU<<24 | r<<16 | g<<8 | b;
> }
>
> @@ -220,8 +244,8 @@ static void split_box(PaletteGenContext *s, struct range_box *box, int n)
> av_assert0(box->len >= 1);
> av_assert0(new_box->len >= 1);
>
> - box->color = get_avg_color(s->refs, box);
> - new_box->color = get_avg_color(s->refs, new_box);
> + box->color = get_avg_color(s->refs, box, s->use_alpha);
> + new_box->color = get_avg_color(s->refs, new_box, s->use_alpha);
> box->variance = -1;
> new_box->variance = -1;
> }
> @@ -242,7 +266,7 @@ static void write_palette(AVFilterContext *ctx, AVFrame *out)
> if (box_id < s->nb_boxes) {
> pal[x] = s->boxes[box_id++].color;
> if ((x || y) && pal[x] == last_color)
> - av_log(ctx, AV_LOG_WARNING, "Dupped color: %08"PRIX32"\n", pal[x]);
> + av_log(ctx, AV_LOG_WARNING, "Duped color: %08"PRIX32"\n", pal[x]);
> last_color = pal[x];
> } else {
> pal[x] = last_color; // pad with last color
should be in a seperate patch, this is not related to alpha
[...]
> - return r<<(NBITS*2) | g<<NBITS | b;
> + return r << (NBITS * 2) | g << NBITS | b;
[...]
> - const int s = kd->split;
> + const int split = kd->split;
also unrelated to the alpha change
all these changes are ok but please do them in a seperate patch, that makes the commits
easier to read in the future
thx
[...]
--
Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
Complexity theory is the science of finding the exact solution to an
approximation. Benchmarking OTOH is finding an approximation of the exact
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 195 bytes
Desc: not available
URL: <https://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20210926/b8153755/attachment.sig>
More information about the ffmpeg-devel
mailing list