[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