[FFmpeg-devel] [PATCH v2 02/32] avfilter/palette{gen, use}: revert support palettes with alpha

Paul B Mahol onemda at gmail.com
Tue Jan 3 21:11:28 EET 2023


Please add alpha support back.

On Wed, Dec 28, 2022 at 12:18 AM Clément Bœsch <u at pkh.me> wrote:

> This reverts commit dea673d0d548c864ec85f9260d8900d944ef7a2a.
>
> This change cannot work for several reasons, the most obvious ones are:
>
> - the alpha is being part of the scoring of the color difference, even
>   though we can not interpret the alpha as part of the perception of the
>   color (we don't even know if it's premultiplied or postmultiplied)
> - the colors are averaged with their alpha value which simply cannot
>   work
>
> The command proposed in the original thread of the patch actually
> produces a completely broken file:
>
>     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
>
> We can see that many color pixels are off, but more importantly some
> colors have a random alpha value: https://imgur.com/eFQ2UK7
>
> I don't see any easy fix for this unfortunately, the approach appears to
> be flawed by design.
> ---
>  doc/filters.texi            |   8 --
>  libavfilter/vf_palettegen.c | 127 +++++++-------------
>  libavfilter/vf_paletteuse.c | 225 +++++++++++++++---------------------
>  3 files changed, 138 insertions(+), 222 deletions(-)
>
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 9b866de5ae..f51623d16a 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -18474,9 +18474,6 @@ 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}
> @@ -18555,11 +18552,6 @@ 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 c03f62b942..bea3292796 100644
> --- a/libavfilter/vf_palettegen.c
> +++ b/libavfilter/vf_palettegen.c
> @@ -59,7 +59,7 @@ enum {
>  };
>
>  #define NBITS 5
> -#define HIST_SIZE (1<<(4*NBITS))
> +#define HIST_SIZE (1<<(3*NBITS))
>
>  typedef struct PaletteGenContext {
>      const AVClass *class;
> @@ -67,7 +67,6 @@ 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
> @@ -89,7 +88,6 @@ 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 }
>  };
>
> @@ -115,16 +113,15 @@ 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 * (3 - (pos))) & 0xff)  \
> -           - (int)((*b)->color >> (8 * (3 - (pos))) & 0xff); \
> +    return   (int)((*a)->color >> (8 * (2 - (pos))) & 0xff)  \
> +           - (int)((*b)->color >> (8 * (2 - (pos))) & 0xff); \
>  }
>
> -DECLARE_CMP_FUNC(a, 0)
> -DECLARE_CMP_FUNC(r, 1)
> -DECLARE_CMP_FUNC(g, 2)
> -DECLARE_CMP_FUNC(b, 3)
> +DECLARE_CMP_FUNC(r, 0)
> +DECLARE_CMP_FUNC(g, 1)
> +DECLARE_CMP_FUNC(b, 2)
>
> -static const cmp_func cmp_funcs[] = {cmp_a, cmp_r, cmp_g, cmp_b};
> +static const cmp_func cmp_funcs[] = {cmp_r, cmp_g, cmp_b};
>
>  /**
>   * Simple color comparison for sorting the final palette
> @@ -146,17 +143,6 @@ 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
>   */
> @@ -178,10 +164,7 @@ 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];
> -                    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;
> +                    variance += diff(ref->color, box->color) * ref->count;
>                  }
>                  box->variance = variance;
>              }
> @@ -201,31 +184,24 @@ 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, int use_alpha)
> +                              const struct range_box *box)
>  {
>      int i;
>      const int n = box->len;
> -    uint64_t a = 0, r = 0, g = 0, b = 0, div = 0;
> +    uint64_t r = 0, g = 0, b = 0, div = 0;
>
>      for (i = 0; i < n; i++) {
>          const struct color_ref *ref = refs[box->start + i];
> -        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;
> +        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;
>  }
>
> @@ -244,8 +220,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, s->use_alpha);
> -    new_box->color = get_avg_color(s->refs, new_box, s->use_alpha);
> +    box->color     = get_avg_color(s->refs, box);
> +    new_box->color = get_avg_color(s->refs, new_box);
>      box->variance     = -1;
>      new_box->variance = -1;
>  }
> @@ -275,7 +251,7 @@ static void write_palette(AVFilterContext *ctx,
> AVFrame *out)
>          pal += pal_linesize;
>      }
>
> -    if (s->reserve_transparent && !s->use_alpha) {
> +    if (s->reserve_transparent) {
>          av_assert0(s->nb_boxes < 256);
>          pal[out->width - pal_linesize - 1] =
> AV_RB32(&s->transparency_color) >> 8;
>      }
> @@ -343,49 +319,40 @@ static AVFrame *get_palette_frame(AVFilterContext
> *ctx)
>      box = &s->boxes[box_id];
>      box->len = s->nb_refs;
>      box->sorted_by = -1;
> -    box->color = get_avg_color(s->refs, box, s->use_alpha);
> +    box->color = get_avg_color(s->refs, box);
>      box->variance = -1;
>      s->nb_boxes = 1;
>
>      while (box && box->len > 1) {
> -        int i, ar, rr, gr, br, longest;
> +        int i, rr, gr, br, longest;
>          uint64_t median, box_weight = 0;
>
>          /* compute the box weight (sum all the weights of the colors in
> the
>           * range) and its boundings */
> -        uint8_t min[4] = {0xff, 0xff, 0xff, 0xff};
> -        uint8_t max[4] = {0x00, 0x00, 0x00, 0x00};
> +        uint8_t min[3] = {0xff, 0xff, 0xff};
> +        uint8_t max[3] = {0x00, 0x00, 0x00};
>          for (i = box->start; i < box->start + box->len; i++) {
>              const struct color_ref *ref = s->refs[i];
>              const uint32_t rgb = ref->color;
> -            const uint8_t a = rgb >> 24 & 0xff, r = rgb >> 16 & 0xff, g =
> rgb >> 8 & 0xff, b = rgb & 0xff;
> -            min[0] = FFMIN(a, min[0]); max[0] = FFMAX(a, max[0]);
> -            min[1] = FFMIN(r, min[1]); max[1] = FFMAX(r, max[1]);
> -            min[2] = FFMIN(g, min[2]); max[2] = FFMAX(g, max[2]);
> -            min[3] = FFMIN(b, min[3]); max[3] = FFMAX(b, max[3]);
> +            const uint8_t r = rgb >> 16 & 0xff, g = rgb >> 8 & 0xff, b =
> rgb & 0xff;
> +            min[0] = FFMIN(r, min[0]), max[0] = FFMAX(r, max[0]);
> +            min[1] = FFMIN(g, min[1]), max[1] = FFMAX(g, max[1]);
> +            min[2] = FFMIN(b, min[2]), max[2] = FFMAX(b, max[2]);
>              box_weight += ref->count;
>          }
>
>          /* define the axis to sort by according to the widest range of
> colors */
> -        ar = max[0] - min[0];
> -        rr = max[1] - min[1];
> -        gr = max[2] - min[2];
> -        br = max[3] - min[3];
> -        longest = 2; // pick green by default (the color the eye is the
> most sensitive to)
> -        if (s->use_alpha) {
> -            if (ar >= rr && ar >= br && ar >= gr) longest = 0;
> -            if (br >= rr && br >= gr && br >= ar) longest = 3;
> -            if (rr >= gr && rr >= br && rr >= ar) longest = 1;
> -            if (gr >= rr && gr >= br && gr >= ar) longest = 2; // prefer
> green again
> -        } else {
> -            if (br >= rr && br >= gr) longest = 3;
> -            if (rr >= gr && rr >= br) longest = 1;
> -            if (gr >= rr && gr >= br) longest = 2; // prefer green again
> -        }
> -
> -        ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64"
> ranges:[%2x %2x %2x %2x] sort by %c (already sorted:%c) ",
> +        rr = max[0] - min[0];
> +        gr = max[1] - min[1];
> +        br = max[2] - min[2];
> +        longest = 1; // pick green by default (the color the eye is the
> most sensitive to)
> +        if (br >= rr && br >= gr) longest = 2;
> +        if (rr >= gr && rr >= br) longest = 0;
> +        if (gr >= rr && gr >= br) longest = 1; // prefer green again
> +
> +        ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64"
> ranges:[%2x %2x %2x] sort by %c (already sorted:%c) ",
>                  box_id, box->start, box->start + box->len - 1, box->len,
> box_weight,
> -                ar, rr, gr, br, "argb"[longest], box->sorted_by ==
> longest ? 'y' : 'n');
> +                rr, gr, br, "rgb"[longest], box->sorted_by == longest ?
> 'y':'n');
>
>          /* sort the range by its longest axis if it's not already sorted
> */
>          if (box->sorted_by != longest) {
> @@ -427,27 +394,22 @@ static AVFrame *get_palette_frame(AVFilterContext
> *ctx)
>   * It keeps the NBITS least significant bit of each component to make it
>   * "random" even if the scene doesn't have much different colors.
>   */
> -static inline unsigned color_hash(uint32_t color, int use_alpha)
> +static inline unsigned color_hash(uint32_t color)
>  {
>      const uint8_t r = color >> 16 & ((1<<NBITS)-1);
>      const uint8_t g = color >>  8 & ((1<<NBITS)-1);
>      const uint8_t b = color       & ((1<<NBITS)-1);
>
> -    if (use_alpha) {
> -        const uint8_t a = color >> 24 & ((1 << NBITS) - 1);
> -        return a << (NBITS * 3) | r << (NBITS * 2) | g << NBITS | b;
> -    }
> -
>      return r << (NBITS * 2) | g << NBITS | b;
>  }
>
>  /**
>   * Locate the color in the hash table and increment its counter.
>   */
> -static int color_inc(struct hist_node *hist, uint32_t color, int
> use_alpha)
> +static int color_inc(struct hist_node *hist, uint32_t color)
>  {
>      int i;
> -    const unsigned hash = color_hash(color, use_alpha);
> +    const unsigned hash = color_hash(color);
>      struct hist_node *node = &hist[hash];
>      struct color_ref *e;
>
> @@ -472,7 +434,7 @@ static int color_inc(struct hist_node *hist, uint32_t
> color, int use_alpha)
>   * Update histogram when pixels differ from previous frame.
>   */
>  static int update_histogram_diff(struct hist_node *hist,
> -                                 const AVFrame *f1, const AVFrame *f2,
> int use_alpha)
> +                                 const AVFrame *f1, const AVFrame *f2)
>  {
>      int x, y, ret, nb_diff_colors = 0;
>
> @@ -483,7 +445,7 @@ static int update_histogram_diff(struct hist_node
> *hist,
>          for (x = 0; x < f1->width; x++) {
>              if (p[x] == q[x])
>                  continue;
> -            ret = color_inc(hist, p[x], use_alpha);
> +            ret = color_inc(hist, p[x]);
>              if (ret < 0)
>                  return ret;
>              nb_diff_colors += ret;
> @@ -495,7 +457,7 @@ static int update_histogram_diff(struct hist_node
> *hist,
>  /**
>   * Simple histogram of the frame.
>   */
> -static int update_histogram_frame(struct hist_node *hist, const AVFrame
> *f, int use_alpha)
> +static int update_histogram_frame(struct hist_node *hist, const AVFrame
> *f)
>  {
>      int x, y, ret, nb_diff_colors = 0;
>
> @@ -503,7 +465,7 @@ static int update_histogram_frame(struct hist_node
> *hist, const AVFrame *f, int
>          const uint32_t *p = (const uint32_t *)(f->data[0] +
> y*f->linesize[0]);
>
>          for (x = 0; x < f->width; x++) {
> -            ret = color_inc(hist, p[x], use_alpha);
> +            ret = color_inc(hist, p[x]);
>              if (ret < 0)
>                  return ret;
>              nb_diff_colors += ret;
> @@ -519,8 +481,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame
> *in)
>  {
>      AVFilterContext *ctx = inlink->dst;
>      PaletteGenContext *s = ctx->priv;
> -    int ret = s->prev_frame ? update_histogram_diff(s->histogram,
> s->prev_frame, in, s->use_alpha)
> -                            : update_histogram_frame(s->histogram, in,
> s->use_alpha);
> +    int ret = s->prev_frame ? update_histogram_diff(s->histogram,
> s->prev_frame, in)
> +                            : update_histogram_frame(s->histogram, in);
>
>      if (ret > 0)
>          s->nb_refs += ret;
> @@ -583,9 +545,6 @@ static int init(AVFilterContext *ctx)
>  {
>      PaletteGenContext* s = ctx->priv;
>
> -    if (s->use_alpha && s->reserve_transparent)
> -        s->reserve_transparent = 0;
> -
>      if (s->max_colors - s->reserve_transparent < 2) {
>          av_log(ctx, AV_LOG_ERROR, "max_colors=2 is only allowed without
> reserving a transparent color slot\n");
>          return AVERROR(EINVAL);
> diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
> index a6b5d5a5fa..cb18329bb7 100644
> --- a/libavfilter/vf_paletteuse.c
> +++ b/libavfilter/vf_paletteuse.c
> @@ -29,6 +29,7 @@
>  #include "libavutil/opt.h"
>  #include "libavutil/qsort.h"
>  #include "avfilter.h"
> +#include "filters.h"
>  #include "framesync.h"
>  #include "internal.h"
>
> @@ -63,7 +64,7 @@ struct color_node {
>  };
>
>  #define NBITS 5
> -#define CACHE_SIZE (1<<(4*NBITS))
> +#define CACHE_SIZE (1<<(3*NBITS))
>
>  struct cached_color {
>      uint32_t color;
> @@ -88,7 +89,6 @@ typedef struct PaletteUseContext {
>      uint32_t palette[AVPALETTE_COUNT];
>      int transparency_index; /* index in the palette of transparency. -1
> if there is no transparency in the palette. */
>      int trans_thresh;
> -    int use_alpha;
>      int palette_loaded;
>      int dither;
>      int new;
> @@ -108,7 +108,7 @@ typedef struct PaletteUseContext {
>  } PaletteUseContext;
>
>  #define OFFSET(x) offsetof(PaletteUseContext, x)
> -#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
> +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
>  static const AVOption paletteuse_options[] = {
>      { "dither", "select dithering mode", OFFSET(dither), AV_OPT_TYPE_INT,
> {.i64=DITHERING_SIERRA2_4A}, 0, NB_DITHERING-1, FLAGS, "dithering_mode" },
>          { "bayer",           "ordered 8x8 bayer dithering
> (deterministic)",                            0, AV_OPT_TYPE_CONST,
> {.i64=DITHERING_BAYER},           INT_MIN, INT_MAX, FLAGS, "dithering_mode"
> },
> @@ -121,7 +121,6 @@ static const AVOption paletteuse_options[] = {
>          { "rectangle", "process smallest different rectangle", 0,
> AV_OPT_TYPE_CONST, {.i64=DIFF_MODE_RECTANGLE}, INT_MIN, INT_MAX, FLAGS,
> "diff_mode" },
>      { "new", "take new palette for each output frame", OFFSET(new),
> AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
>      { "alpha_threshold", "set the alpha threshold for transparency",
> OFFSET(trans_thresh), AV_OPT_TYPE_INT, {.i64=128}, 0, 255, FLAGS },
> -    { "use_alpha", "use alpha channel for mapping", OFFSET(use_alpha),
> AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
>
>      /* following are the debug options, not part of the official API */
>      { "debug_kdtree", "save Graphviz graph of the kdtree in specified
> file", OFFSET(dot_filename), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
> @@ -163,41 +162,37 @@ static av_always_inline uint32_t
> dither_color(uint32_t px, int er, int eg,
>           | av_clip_uint8((px       & 0xff) + ((eb * scale) / (1<<shift)));
>  }
>
> -static av_always_inline int diff(const uint8_t *c1, const uint8_t *c2,
> const PaletteUseContext *s)
> +static av_always_inline int diff(const uint8_t *c1, const uint8_t *c2,
> const int trans_thresh)
>  {
>      // XXX: try L*a*b with CIE76 (dL*dL + da*da + db*db)
> -    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];
>
> -    if (s->use_alpha)
> -        return da*da + dr*dr + dg*dg + db*db;
> -
> -    if (c1[0] < s->trans_thresh && c2[0] < s->trans_thresh) {
> +    if (c1[0] < trans_thresh && c2[0] < trans_thresh) {
>          return 0;
> -    } else if (c1[0] >= s->trans_thresh && c2[0] >= s->trans_thresh) {
> +    } else if (c1[0] >= trans_thresh && c2[0] >= trans_thresh) {
>          return dr*dr + dg*dg + db*db;
>      } else {
>          return 255*255 + 255*255 + 255*255;
>      }
>  }
>
> -static av_always_inline uint8_t colormap_nearest_bruteforce(const
> PaletteUseContext *s, const uint8_t *argb)
> +static av_always_inline uint8_t colormap_nearest_bruteforce(const
> uint32_t *palette, const uint8_t *argb, const int trans_thresh)
>  {
>      int i, pal_id = -1, min_dist = INT_MAX;
>
>      for (i = 0; i < AVPALETTE_COUNT; i++) {
> -        const uint32_t c = s->palette[i];
> +        const uint32_t c = palette[i];
>
> -        if (s->use_alpha || c >> 24 >= s->trans_thresh) { // ignore
> transparent entry
> +        if (c >> 24 >= trans_thresh) { // ignore transparent entry
>              const uint8_t palargb[] = {
> -                s->palette[i]>>24 & 0xff,
> -                s->palette[i]>>16 & 0xff,
> -                s->palette[i]>> 8 & 0xff,
> -                s->palette[i]     & 0xff,
> +                palette[i]>>24 & 0xff,
> +                palette[i]>>16 & 0xff,
> +                palette[i]>> 8 & 0xff,
> +                palette[i]     & 0xff,
>              };
> -            const int d = diff(palargb, argb, s);
> +            const int d = diff(palargb, argb, trans_thresh);
>              if (d < min_dist) {
>                  pal_id = i;
>                  min_dist = d;
> @@ -213,17 +208,17 @@ struct nearest_color {
>      int dist_sqd;
>  };
>
> -static void colormap_nearest_node(const PaletteUseContext *s,
> -                                  const struct color_node *map,
> +static void colormap_nearest_node(const struct color_node *map,
>                                    const int node_pos,
>                                    const uint8_t *target,
> +                                  const int trans_thresh,
>                                    struct nearest_color *nearest)
>  {
>      const struct color_node *kd = map + node_pos;
> -    const int split = kd->split;
> +    const int s = kd->split;
>      int dx, nearer_kd_id, further_kd_id;
>      const uint8_t *current = kd->val;
> -    const int current_to_target = diff(target, current, s);
> +    const int current_to_target = diff(target, current, trans_thresh);
>
>      if (current_to_target < nearest->dist_sqd) {
>          nearest->node_pos = node_pos;
> @@ -231,23 +226,23 @@ static void colormap_nearest_node(const
> PaletteUseContext *s,
>      }
>
>      if (kd->left_id != -1 || kd->right_id != -1) {
> -        dx = target[split] - current[split];
> +        dx = target[s] - current[s];
>
>          if (dx <= 0) nearer_kd_id = kd->left_id,  further_kd_id =
> kd->right_id;
>          else         nearer_kd_id = kd->right_id, further_kd_id =
> kd->left_id;
>
>          if (nearer_kd_id != -1)
> -            colormap_nearest_node(s, map, nearer_kd_id, target, nearest);
> +            colormap_nearest_node(map, nearer_kd_id, target,
> trans_thresh, nearest);
>
>          if (further_kd_id != -1 && dx*dx < nearest->dist_sqd)
> -            colormap_nearest_node(s, map, further_kd_id, target, nearest);
> +            colormap_nearest_node(map, further_kd_id, target,
> trans_thresh, nearest);
>      }
>  }
>
> -static av_always_inline uint8_t colormap_nearest_recursive(const
> PaletteUseContext *s, const struct color_node *node, const uint8_t *rgb)
> +static av_always_inline uint8_t colormap_nearest_recursive(const struct
> color_node *node, const uint8_t *rgb, const int trans_thresh)
>  {
>      struct nearest_color res = {.dist_sqd = INT_MAX, .node_pos = -1};
> -    colormap_nearest_node(s, node, 0, rgb, &res);
> +    colormap_nearest_node(node, 0, rgb, trans_thresh, &res);
>      return node[res.node_pos].palette_id;
>  }
>
> @@ -256,7 +251,7 @@ struct stack_node {
>      int dx2;
>  };
>
> -static av_always_inline uint8_t colormap_nearest_iterative(const
> PaletteUseContext *s, const struct color_node *root, const uint8_t *target)
> +static av_always_inline uint8_t colormap_nearest_iterative(const struct
> color_node *root, const uint8_t *target, const int trans_thresh)
>  {
>      int pos = 0, best_node_id = -1, best_dist = INT_MAX, cur_color_id = 0;
>      struct stack_node nodes[16];
> @@ -266,7 +261,7 @@ static av_always_inline uint8_t
> colormap_nearest_iterative(const PaletteUseConte
>
>          const struct color_node *kd = &root[cur_color_id];
>          const uint8_t *current = kd->val;
> -        const int current_to_target = diff(target, current, s);
> +        const int current_to_target = diff(target, current, trans_thresh);
>
>          /* Compare current color node to the target and update our best
> node if
>           * it's actually better. */
> @@ -328,10 +323,10 @@ end:
>      return root[best_node_id].palette_id;
>  }
>
> -#define COLORMAP_NEAREST(s, search, root, target)
>             \
> -    search == COLOR_SEARCH_NNS_ITERATIVE ? colormap_nearest_iterative(s,
> root, target) :      \
> -    search == COLOR_SEARCH_NNS_RECURSIVE ? colormap_nearest_recursive(s,
> root, target) :      \
> -                                           colormap_nearest_bruteforce(s,
> target)
> +#define COLORMAP_NEAREST(search, palette, root, target, trans_thresh)
>                                 \
> +    search == COLOR_SEARCH_NNS_ITERATIVE ?
> colormap_nearest_iterative(root, target, trans_thresh) :      \
> +    search == COLOR_SEARCH_NNS_RECURSIVE ?
> colormap_nearest_recursive(root, target, trans_thresh) :      \
> +
>  colormap_nearest_bruteforce(palette, target, trans_thresh)
>
>  /**
>   * Check if the requested color is in the cache already. If not, find it
> in the
> @@ -368,13 +363,13 @@ static av_always_inline int
> color_get(PaletteUseContext *s, uint32_t color,
>      if (!e)
>          return AVERROR(ENOMEM);
>      e->color = color;
> -    e->pal_entry = COLORMAP_NEAREST(s, search_method, s->map, argb_elts);
> +    e->pal_entry = COLORMAP_NEAREST(search_method, s->palette, s->map,
> argb_elts, s->trans_thresh);
>
>      return e->pal_entry;
>  }
>
>  static av_always_inline int get_dst_color_err(PaletteUseContext *s,
> -                                              uint32_t c, int *ea, int
> *er, int *eg, int *eb,
> +                                              uint32_t c, int *er, int
> *eg, int *eb,
>                                                const enum
> color_search_method search_method)
>  {
>      const uint8_t a = c >> 24 & 0xff;
> @@ -387,9 +382,8 @@ static av_always_inline int
> get_dst_color_err(PaletteUseContext *s,
>          return dstx;
>      dstc = s->palette[dstx];
>      if (dstx == s->transparency_index) {
> -        *ea =*er = *eg = *eb = 0;
> +        *er = *eg = *eb = 0;
>      } else {
> -        *ea = (int)a - (int)(dstc >> 24 & 0xff);
>          *er = (int)r - (int)(dstc >> 16 & 0xff);
>          *eg = (int)g - (int)(dstc >>  8 & 0xff);
>          *eb = (int)b - (int)(dstc       & 0xff);
> @@ -413,7 +407,7 @@ static av_always_inline int
> set_frame(PaletteUseContext *s, AVFrame *out, AVFram
>
>      for (y = y_start; y < h; y++) {
>          for (x = x_start; x < w; x++) {
> -            int ea, er, eg, eb;
> +            int er, eg, eb;
>
>              if (dither == DITHERING_BAYER) {
>                  const int d = s->ordered_dither[(y & 7)<<3 | (x & 7)];
> @@ -433,7 +427,7 @@ static av_always_inline int
> set_frame(PaletteUseContext *s, AVFrame *out, AVFram
>
>              } else if (dither == DITHERING_HECKBERT) {
>                  const int right = x < w - 1, down = y < h - 1;
> -                const int color = get_dst_color_err(s, src[x], &ea, &er,
> &eg, &eb, search_method);
> +                const int color = get_dst_color_err(s, src[x], &er, &eg,
> &eb, search_method);
>
>                  if (color < 0)
>                      return color;
> @@ -445,7 +439,7 @@ static av_always_inline int
> set_frame(PaletteUseContext *s, AVFrame *out, AVFram
>
>              } else if (dither == DITHERING_FLOYD_STEINBERG) {
>                  const int right = x < w - 1, down = y < h - 1, left = x >
> x_start;
> -                const int color = get_dst_color_err(s, src[x], &ea, &er,
> &eg, &eb, search_method);
> +                const int color = get_dst_color_err(s, src[x], &er, &eg,
> &eb, search_method);
>
>                  if (color < 0)
>                      return color;
> @@ -459,7 +453,7 @@ static av_always_inline int
> set_frame(PaletteUseContext *s, AVFrame *out, AVFram
>              } else if (dither == DITHERING_SIERRA2) {
>                  const int right  = x < w - 1, down  = y < h - 1, left  =
> x > x_start;
>                  const int right2 = x < w - 2,                    left2 =
> x > x_start + 1;
> -                const int color = get_dst_color_err(s, src[x], &ea, &er,
> &eg, &eb, search_method);
> +                const int color = get_dst_color_err(s, src[x], &er, &eg,
> &eb, search_method);
>
>                  if (color < 0)
>                      return color;
> @@ -478,7 +472,7 @@ static av_always_inline int
> set_frame(PaletteUseContext *s, AVFrame *out, AVFram
>
>              } else if (dither == DITHERING_SIERRA2_4A) {
>                  const int right = x < w - 1, down = y < h - 1, left = x >
> x_start;
> -                const int color = get_dst_color_err(s, src[x], &ea, &er,
> &eg, &eb, search_method);
> +                const int color = get_dst_color_err(s, src[x], &er, &eg,
> &eb, search_method);
>
>                  if (color < 0)
>                      return color;
> @@ -561,7 +555,8 @@ static int disp_tree(const struct color_node *node,
> const char *fname)
>      return 0;
>  }
>
> -static int debug_accuracy(const PaletteUseContext *s)
> +static int debug_accuracy(const struct color_node *node, const uint32_t
> *palette, const int trans_thresh,
> +                          const enum color_search_method search_method)
>  {
>      int r, g, b, ret = 0;
>
> @@ -569,26 +564,19 @@ static int debug_accuracy(const PaletteUseContext *s)
>          for (g = 0; g < 256; g++) {
>              for (b = 0; b < 256; b++) {
>                  const uint8_t argb[] = {0xff, r, g, b};
> -                const int r1 = COLORMAP_NEAREST(s,
> s->color_search_method, s->map, argb);
> -                const int r2 = colormap_nearest_bruteforce(s, argb);
> +                const int r1 = COLORMAP_NEAREST(search_method, palette,
> node, argb, trans_thresh);
> +                const int r2 = colormap_nearest_bruteforce(palette, argb,
> trans_thresh);
>                  if (r1 != r2) {
> -                    const uint32_t c1 = s->palette[r1];
> -                    const uint32_t c2 = s->palette[r2];
> -                    const uint8_t a1 = s->use_alpha ? c1>>24 & 0xff :
> 0xff;
> -                    const uint8_t a2 = s->use_alpha ? c2>>24 & 0xff :
> 0xff;
> -                    const uint8_t palargb1[] = { a1, c1>>16 & 0xff, c1>>
> 8 & 0xff, c1 & 0xff };
> -                    const uint8_t palargb2[] = { a2, c2>>16 & 0xff, c2>>
> 8 & 0xff, c2 & 0xff };
> -                    const int d1 = diff(palargb1, argb, s);
> -                    const int d2 = diff(palargb2, argb, s);
> +                    const uint32_t c1 = palette[r1];
> +                    const uint32_t c2 = palette[r2];
> +                    const uint8_t palargb1[] = { 0xff, c1>>16 & 0xff,
> c1>> 8 & 0xff, c1 & 0xff };
> +                    const uint8_t palargb2[] = { 0xff, c2>>16 & 0xff,
> c2>> 8 & 0xff, c2 & 0xff };
> +                    const int d1 = diff(palargb1, argb, trans_thresh);
> +                    const int d2 = diff(palargb2, argb, trans_thresh);
>                      if (d1 != d2) {
> -                        if (s->use_alpha)
> -                            av_log(NULL, AV_LOG_ERROR,
> -                                   "/!\\ %02X%02X%02X: %d ! %d
> (%08"PRIX32" ! %08"PRIX32") / dist: %d ! %d\n",
> -                                   r, g, b, r1, r2, c1, c2, d1, d2);
> -                        else
> -                            av_log(NULL, AV_LOG_ERROR,
> -                                   "/!\\ %02X%02X%02X: %d ! %d
> (%06"PRIX32" ! %06"PRIX32") / dist: %d ! %d\n",
> -                                   r, g, b, r1, r2, c1 & 0xffffff, c2 &
> 0xffffff, d1, d2);
> +                        av_log(NULL, AV_LOG_ERROR,
> +                               "/!\\ %02X%02X%02X: %d ! %d (%06"PRIX32" !
> %06"PRIX32") / dist: %d ! %d\n",
> +                               r, g, b, r1, r2, c1 & 0xffffff, c2 &
> 0xffffff, d1, d2);
>                          ret = 1;
>                      }
>                  }
> @@ -604,8 +592,8 @@ struct color {
>  };
>
>  struct color_rect {
> -    uint8_t min[4];
> -    uint8_t max[4];
> +    uint8_t min[3];
> +    uint8_t max[3];
>  };
>
>  typedef int (*cmp_func)(const void *, const void *);
> @@ -626,47 +614,43 @@ DECLARE_CMP_FUNC(b, 3)
>
>  static const cmp_func cmp_funcs[] = {cmp_a, cmp_r, cmp_g, cmp_b};
>
> -static int get_next_color(const uint8_t *color_used, const
> PaletteUseContext *s,
> +static int get_next_color(const uint8_t *color_used, const uint32_t
> *palette,
> +                          const int trans_thresh,
>                            int *component, const struct color_rect *box)
>  {
> -    int wa, wr, wg, wb;
> +    int wr, wg, wb;
>      int i, longest = 0;
>      unsigned nb_color = 0;
>      struct color_rect ranges;
>      struct color tmp_pal[256];
>      cmp_func cmpf;
>
> -    ranges.min[0] = ranges.min[1] = ranges.min[2]  = ranges.min[3]= 0xff;
> -    ranges.max[0] = ranges.max[1] = ranges.max[2]  = ranges.max[3]= 0x00;
> +    ranges.min[0] = ranges.min[1] = ranges.min[2] = 0xff;
> +    ranges.max[0] = ranges.max[1] = ranges.max[2] = 0x00;
>
>      for (i = 0; i < AVPALETTE_COUNT; i++) {
> -        const uint32_t c = s->palette[i];
> +        const uint32_t c = palette[i];
>          const uint8_t a = c >> 24 & 0xff;
>          const uint8_t r = c >> 16 & 0xff;
>          const uint8_t g = c >>  8 & 0xff;
>          const uint8_t b = c       & 0xff;
>
> -        if (!s->use_alpha && a < s->trans_thresh) {
> +        if (a < trans_thresh) {
>              continue;
>          }
>
> -        if (color_used[i] || (a != 0xff && !s->use_alpha) ||
> -            r < box->min[1] || g < box->min[2] || b < box->min[3] ||
> -            r > box->max[1] || g > box->max[2] || b > box->max[3])
> +        if (color_used[i] || (a != 0xff) ||
> +            r < box->min[0] || g < box->min[1] || b < box->min[2] ||
> +            r > box->max[0] || g > box->max[1] || b > box->max[2])
>              continue;
>
> -        if (s->use_alpha && (a < box->min[0] || a > box->max[0]))
> -            continue;
> -
> -        if (a < ranges.min[0]) ranges.min[0] = a;
> -        if (r < ranges.min[1]) ranges.min[1] = r;
> -        if (g < ranges.min[2]) ranges.min[2] = g;
> -        if (b < ranges.min[3]) ranges.min[3] = b;
> +        if (r < ranges.min[0]) ranges.min[0] = r;
> +        if (g < ranges.min[1]) ranges.min[1] = g;
> +        if (b < ranges.min[2]) ranges.min[2] = b;
>
> -        if (a > ranges.max[0]) ranges.max[0] = a;
> -        if (r > ranges.max[1]) ranges.max[1] = r;
> -        if (g > ranges.max[2]) ranges.max[2] = g;
> -        if (b > ranges.max[3]) ranges.max[3] = b;
> +        if (r > ranges.max[0]) ranges.max[0] = r;
> +        if (g > ranges.max[1]) ranges.max[1] = g;
> +        if (b > ranges.max[2]) ranges.max[2] = b;
>
>          tmp_pal[nb_color].value  = c;
>          tmp_pal[nb_color].pal_id = i;
> @@ -678,22 +662,12 @@ static int get_next_color(const uint8_t *color_used,
> const PaletteUseContext *s,
>          return -1;
>
>      /* define longest axis that will be the split component */
> -    wa = ranges.max[0] - ranges.min[0];
> -    wr = ranges.max[1] - ranges.min[1];
> -    wg = ranges.max[2] - ranges.min[2];
> -    wb = ranges.max[3] - ranges.min[3];
> -
> -    if (s->use_alpha) {
> -        if (wa >= wr && wa >= wb && wa >= wg) longest = 0;
> -        if (wr >= wg && wr >= wb && wr >= wa) longest = 1;
> -        if (wg >= wr && wg >= wb && wg >= wa) longest = 2;
> -        if (wb >= wr && wb >= wg && wb >= wa) longest = 3;
> -    } else {
> -        if (wr >= wg && wr >= wb) longest = 1;
> -        if (wg >= wr && wg >= wb) longest = 2;
> -        if (wb >= wr && wb >= wg) longest = 3;
> -    }
> -
> +    wr = ranges.max[0] - ranges.min[0];
> +    wg = ranges.max[1] - ranges.min[1];
> +    wb = ranges.max[2] - ranges.min[2];
> +    if (wr >= wg && wr >= wb) longest = 1;
> +    if (wg >= wr && wg >= wb) longest = 2;
> +    if (wb >= wr && wb >= wg) longest = 3;
>      cmpf = cmp_funcs[longest];
>      *component = longest;
>
> @@ -706,7 +680,8 @@ static int get_next_color(const uint8_t *color_used,
> const PaletteUseContext *s,
>  static int colormap_insert(struct color_node *map,
>                             uint8_t *color_used,
>                             int *nb_used,
> -                           const PaletteUseContext *s,
> +                           const uint32_t *palette,
> +                           const int trans_thresh,
>                             const struct color_rect *box)
>  {
>      uint32_t c;
> @@ -714,14 +689,14 @@ static int colormap_insert(struct color_node *map,
>      int node_left_id = -1, node_right_id = -1;
>      struct color_node *node;
>      struct color_rect box1, box2;
> -    const int pal_id = get_next_color(color_used, s, &component, box);
> +    const int pal_id = get_next_color(color_used, palette, trans_thresh,
> &component, box);
>
>      if (pal_id < 0)
>          return -1;
>
>      /* create new node with that color */
>      cur_id = (*nb_used)++;
> -    c = s->palette[pal_id];
> +    c = palette[pal_id];
>      node = &map[cur_id];
>      node->split = component;
>      node->palette_id = pal_id;
> @@ -734,13 +709,13 @@ static int colormap_insert(struct color_node *map,
>
>      /* get the two boxes this node creates */
>      box1 = box2 = *box;
> -    box1.max[component] = node->val[component];
> -    box2.min[component] = FFMIN(node->val[component] + 1, 255);
> +    box1.max[component-1] = node->val[component];
> +    box2.min[component-1] = FFMIN(node->val[component] + 1, 255);
>
> -    node_left_id = colormap_insert(map, color_used, nb_used, s, &box1);
> +    node_left_id = colormap_insert(map, color_used, nb_used, palette,
> trans_thresh, &box1);
>
> -    if (box2.min[component] <= box2.max[component])
> -        node_right_id = colormap_insert(map, color_used, nb_used, s,
> &box2);
> +    if (box2.min[component-1] <= box2.max[component-1])
> +        node_right_id = colormap_insert(map, color_used, nb_used,
> palette, trans_thresh, &box2);
>
>      node->left_id  = node_left_id;
>      node->right_id = node_right_id;
> @@ -755,13 +730,6 @@ static int cmp_pal_entry(const void *a, const void *b)
>      return c1 - c2;
>  }
>
> -static int cmp_pal_entry_alpha(const void *a, const void *b)
> -{
> -    const int c1 = *(const uint32_t *)a;
> -    const int c2 = *(const uint32_t *)b;
> -    return c1 - c2;
> -}
> -
>  static void load_colormap(PaletteUseContext *s)
>  {
>      int i, nb_used = 0;
> @@ -769,13 +737,12 @@ static void load_colormap(PaletteUseContext *s)
>      uint32_t last_color = 0;
>      struct color_rect box;
>
> -    if (!s->use_alpha && s->transparency_index >= 0) {
> +    if (s->transparency_index >= 0) {
>          FFSWAP(uint32_t, s->palette[s->transparency_index],
> s->palette[255]);
>      }
>
>      /* disable transparent colors and dups */
> -    qsort(s->palette, AVPALETTE_COUNT-(s->transparency_index >= 0),
> sizeof(*s->palette),
> -        s->use_alpha ? cmp_pal_entry_alpha : cmp_pal_entry);
> +    qsort(s->palette, AVPALETTE_COUNT-(s->transparency_index >= 0),
> sizeof(*s->palette), cmp_pal_entry);
>
>      for (i = 0; i < AVPALETTE_COUNT; i++) {
>          const uint32_t c = s->palette[i];
> @@ -784,22 +751,22 @@ static void load_colormap(PaletteUseContext *s)
>              continue;
>          }
>          last_color = c;
> -        if (!s->use_alpha && c >> 24 < s->trans_thresh) {
> +        if (c >> 24 < s->trans_thresh) {
>              color_used[i] = 1; // ignore transparent color(s)
>              continue;
>          }
>      }
>
> -    box.min[0] = box.min[1] = box.min[2] = box.min[3] = 0x00;
> -    box.max[0] = box.max[1] = box.max[2] = box.max[3] = 0xff;
> +    box.min[0] = box.min[1] = box.min[2] = 0x00;
> +    box.max[0] = box.max[1] = box.max[2] = 0xff;
>
> -    colormap_insert(s->map, color_used, &nb_used, s, &box);
> +    colormap_insert(s->map, color_used, &nb_used, s->palette,
> s->trans_thresh, &box);
>
>      if (s->dot_filename)
>          disp_tree(s->map, s->dot_filename);
>
>      if (s->debug_accuracy) {
> -        if (!debug_accuracy(s))
> +        if (!debug_accuracy(s->map, s->palette, s->trans_thresh,
> s->color_search_method))
>              av_log(NULL, AV_LOG_INFO, "Accuracy check passed\n");
>      }
>  }
> @@ -813,18 +780,16 @@ static void debug_mean_error(PaletteUseContext *s,
> const AVFrame *in1,
>      uint8_t  *src2 =             in2->data[0];
>      const int src1_linesize = in1->linesize[0] >> 2;
>      const int src2_linesize = in2->linesize[0];
> -    const float div = in1->width * in1->height * (s->use_alpha ? 4 : 3);
> +    const float div = in1->width * in1->height * 3;
>      unsigned mean_err = 0;
>
>      for (y = 0; y < in1->height; y++) {
>          for (x = 0; x < in1->width; x++) {
>              const uint32_t c1 = src1[x];
>              const uint32_t c2 = palette[src2[x]];
> -            const uint8_t a1 = s->use_alpha ? c1>>24 & 0xff : 0xff;
> -            const uint8_t a2 = s->use_alpha ? c2>>24 & 0xff : 0xff;
> -            const uint8_t argb1[] = {a1, c1 >> 16 & 0xff, c1 >> 8 & 0xff,
> c1 & 0xff};
> -            const uint8_t argb2[] = {a2, c2 >> 16 & 0xff, c2 >> 8 & 0xff,
> c2 & 0xff};
> -            mean_err += diff(argb1, argb2, s);
> +            const uint8_t argb1[] = {0xff, c1 >> 16 & 0xff, c1 >> 8 &
> 0xff, c1 & 0xff};
> +            const uint8_t argb2[] = {0xff, c2 >> 16 & 0xff, c2 >> 8 &
> 0xff, c2 & 0xff};
> +            mean_err += diff(argb1, argb2, s->trans_thresh);
>          }
>          src1 += src1_linesize;
>          src2 += src2_linesize;
> @@ -1024,7 +989,7 @@ static void load_palette(PaletteUseContext *s, const
> AVFrame *palette_frame)
>      for (y = 0; y < palette_frame->height; y++) {
>          for (x = 0; x < palette_frame->width; x++) {
>              s->palette[i] = p[x];
> -            if (!s->use_alpha && p[x]>>24 < s->trans_thresh) {
> +            if (p[x]>>24 < s->trans_thresh) {
>                  s->transparency_index = i; // we are assuming at most one
> transparent color in palette
>              }
>              i++;
> --
> 2.39.0
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".
>


More information about the ffmpeg-devel mailing list