* [FFmpeg-devel] Rework color quantization in palette{gen,use}
@ 2022-11-05 15:26 Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 01/15] Revert "avfilter/vf_palette(gen|use): support palettes with alpha" Clément Bœsch
` (19 more replies)
0 siblings, 20 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-11-05 15:26 UTC (permalink / raw)
To: ffmpeg-devel
Hi,
This patchset essentially fixes a few core problems in these filters and
switches to a perceptual model.
I've generated a report for each key commit on this (temporary) page:
http://big.pkh.me/pal/ (warning: heavy page, ~500M; I did try to add some lazy
loading of the images but I'm not sure it's actually working as expected).
It is easy for me to add specific samples and re-run the whole thing, so feel
free to suggest one.
A summary on a few important observed differences can be found on the page, but
I'm duplicating it here for the record:
- Current: current state on master
- Paletteuse Perceptual
+ same palette but better selection: instead of rgb triplet distance, it uses a
colorspace designed for uniform perceptual color differences (OkLab)
+ overall impact not that visible immediately, but it will make sure the
palette is used the best way possible, meaning improvements to palettegen
will be honored
+ observations (with no dither):
* file02 (rooftops) in max_colors=8 or 16: sky pattern is more stable
* file06 (parrot) in max_colors=8: better color for the parrot beak
* overall seems to favor slightly brighter colors in the currently
offered palette
- Palettegen Linear Average
+ sRGB colors are gamma encoded, averaging them naively is incorrect, we
need to do that in linear space
+ observations (with no dither):
* file00 (colorful drawing) in max_colors=8: contrast and color skin
look better
* file07 (abstract flower) in max_color=128 or 256: this picture
composed of 1M different colors in the source is now more balanced
(better spreading of the colors)
- Palettegen Perceptual
+ similar to the paletteuse perceptual, we use OkLab for the color distance
+ observations (with no dither):
* file07 (abstract flower): in max_colors=128 or 256 we can see the
picture offering a much finer grain.
- Palettegen Variance per axis
+ When deciding on spliting a box along an axis, instead of picking the
longest one, we pick the one with the most color variance
+ Not that much impact
Overall, the most brutal change is probably in file07 between current and last,
256 colors no dither in particular.
Finally, I do believe a lot of other color filters could at least benefit from
fixing their gamma handling (I know I'm guilty of this in various other
filters).
Regards,
--
Clément B.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH 01/15] Revert "avfilter/vf_palette(gen|use): support palettes with alpha"
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
@ 2022-11-05 15:26 ` Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 02/15] avfilter: add palette utils Clément Bœsch
` (18 subsequent siblings)
19 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-11-05 15:26 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
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 | 135 +++++++---------------
libavfilter/vf_paletteuse.c | 225 +++++++++++++++---------------------
3 files changed, 138 insertions(+), 230 deletions(-)
diff --git a/doc/filters.texi b/doc/filters.texi
index 721549c2c7..251f647ee9 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -18288,9 +18288,6 @@ Compute new histogram for each frame.
@end table
Default value is @var{full}.
-@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}
@@ -18369,11 +18366,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}.
-
-@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 27f74fd147..d335ef91e6 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;
@@ -579,16 +541,6 @@ static int config_output(AVFilterLink *outlink)
return 0;
}
-static int init(AVFilterContext *ctx)
-{
- PaletteGenContext* s = ctx->priv;
-
- if (s->use_alpha && s->reserve_transparent)
- s->reserve_transparent = 0;
-
- return 0;
-}
-
static av_cold void uninit(AVFilterContext *ctx)
{
int i;
@@ -621,7 +573,6 @@ const AVFilter ff_vf_palettegen = {
.name = "palettegen",
.description = NULL_IF_CONFIG_SMALL("Find the optimal palette for a given stream."),
.priv_size = sizeof(PaletteGenContext),
- .init = init,
.uninit = uninit,
FILTER_INPUTS(palettegen_inputs),
FILTER_OUTPUTS(palettegen_outputs),
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.38.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH 02/15] avfilter: add palette utils
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 01/15] Revert "avfilter/vf_palette(gen|use): support palettes with alpha" Clément Bœsch
@ 2022-11-05 15:26 ` Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 03/15] avfilter/palette{use, gen}: simplify a few alpha masks Clément Bœsch
` (17 subsequent siblings)
19 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-11-05 15:26 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
These color management helpers will be shared by palettegen and
paletteuse in the following commits.
Note that it probably makes sense to share at least the sRGB/linear
functions with other filters at some point.
---
libavfilter/palette.c | 101 ++++++++++++++++++++++++++++++++++++++++++
libavfilter/palette.h | 54 ++++++++++++++++++++++
2 files changed, 155 insertions(+)
create mode 100644 libavfilter/palette.c
create mode 100644 libavfilter/palette.h
diff --git a/libavfilter/palette.c b/libavfilter/palette.c
new file mode 100644
index 0000000000..80514c436b
--- /dev/null
+++ b/libavfilter/palette.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2020 Björn Ottosson
+ * Copyright (c) 2022 Clément Bœsch <u pkh me>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <math.h>
+
+#include "libavutil/common.h"
+#include "palette.h"
+
+/**
+ * Table mapping formula:
+ * f(x) = x < 0.04045 ? x/12.92 : ((x+.055)/(1+.055))^2.4
+ * Where x is the normalized index in the table.
+ */
+static const float srgb2linear[256] = {
+ 0.000000, 0.000304, 0.000607, 0.000911, 0.001214, 0.001518, 0.001821, 0.002125,
+ 0.002428, 0.002732, 0.003035, 0.003347, 0.003677, 0.004025, 0.004391, 0.004777,
+ 0.005182, 0.005605, 0.006049, 0.006512, 0.006995, 0.007499, 0.008023, 0.008568,
+ 0.009134, 0.009721, 0.010330, 0.010960, 0.011612, 0.012286, 0.012983, 0.013702,
+ 0.014444, 0.015209, 0.015996, 0.016807, 0.017642, 0.018500, 0.019382, 0.020289,
+ 0.021219, 0.022174, 0.023153, 0.024158, 0.025187, 0.026241, 0.027321, 0.028426,
+ 0.029557, 0.030713, 0.031896, 0.033105, 0.034340, 0.035601, 0.036889, 0.038204,
+ 0.039546, 0.040915, 0.042311, 0.043735, 0.045186, 0.046665, 0.048172, 0.049707,
+ 0.051269, 0.052861, 0.054480, 0.056128, 0.057805, 0.059511, 0.061246, 0.063010,
+ 0.064803, 0.066626, 0.068478, 0.070360, 0.072272, 0.074214, 0.076185, 0.078187,
+ 0.080220, 0.082283, 0.084376, 0.086500, 0.088656, 0.090842, 0.093059, 0.095307,
+ 0.097587, 0.099899, 0.102242, 0.104616, 0.107023, 0.109462, 0.111932, 0.114435,
+ 0.116971, 0.119538, 0.122139, 0.124772, 0.127438, 0.130136, 0.132868, 0.135633,
+ 0.138432, 0.141263, 0.144128, 0.147027, 0.149960, 0.152926, 0.155926, 0.158961,
+ 0.162029, 0.165132, 0.168269, 0.171441, 0.174647, 0.177888, 0.181164, 0.184475,
+ 0.187821, 0.191202, 0.194618, 0.198069, 0.201556, 0.205079, 0.208637, 0.212231,
+ 0.215861, 0.219526, 0.223228, 0.226966, 0.230740, 0.234551, 0.238398, 0.242281,
+ 0.246201, 0.250158, 0.254152, 0.258183, 0.262251, 0.266356, 0.270498, 0.274677,
+ 0.278894, 0.283149, 0.287441, 0.291771, 0.296138, 0.300544, 0.304987, 0.309469,
+ 0.313989, 0.318547, 0.323143, 0.327778, 0.332452, 0.337164, 0.341914, 0.346704,
+ 0.351533, 0.356400, 0.361307, 0.366253, 0.371238, 0.376262, 0.381326, 0.386429,
+ 0.391572, 0.396755, 0.401978, 0.407240, 0.412543, 0.417885, 0.423268, 0.428690,
+ 0.434154, 0.439657, 0.445201, 0.450786, 0.456411, 0.462077, 0.467784, 0.473531,
+ 0.479320, 0.485150, 0.491021, 0.496933, 0.502886, 0.508881, 0.514918, 0.520996,
+ 0.527115, 0.533276, 0.539479, 0.545724, 0.552011, 0.558340, 0.564712, 0.571125,
+ 0.577580, 0.584078, 0.590619, 0.597202, 0.603827, 0.610496, 0.617207, 0.623960,
+ 0.630757, 0.637597, 0.644480, 0.651406, 0.658375, 0.665387, 0.672443, 0.679542,
+ 0.686685, 0.693872, 0.701102, 0.708376, 0.715694, 0.723055, 0.730461, 0.737910,
+ 0.745404, 0.752942, 0.760525, 0.768151, 0.775822, 0.783538, 0.791298, 0.799103,
+ 0.806952, 0.814847, 0.822786, 0.830770, 0.838799, 0.846873, 0.854993, 0.863157,
+ 0.871367, 0.879622, 0.887923, 0.896269, 0.904661, 0.913099, 0.921582, 0.930111,
+ 0.938686, 0.947307, 0.955973, 0.964686, 0.973445, 0.982251, 0.991102, 1.000000,
+};
+
+float ff_srgb_u8_to_linear_f32(uint8_t x)
+{
+ return srgb2linear[x];
+}
+
+uint8_t ff_linear_f32_to_srgb_u8(float x)
+{
+ const float v = x < 0.0031308f ? x * 12.92f : powf(1.055f * x, 1.f/2.4f) - 0.055f;
+ return av_clip_uint8(lrintf(v * 255.f));
+}
+
+struct Lab ff_srgb_u8_to_oklab(uint32_t srgb)
+{
+ const float r = srgb2linear[srgb >> 16 & 0xff];
+ const float g = srgb2linear[srgb >> 8 & 0xff];
+ const float b = srgb2linear[srgb & 0xff];
+
+ const float l = 0.4122214708f * r + 0.5363325363f * g + 0.0514459929f * b;
+ const float m = 0.2119034982f * r + 0.6806995451f * g + 0.1073969566f * b;
+ const float s = 0.0883024619f * r + 0.2817188376f * g + 0.6299787005f * b;
+
+ const float l_ = cbrtf(l);
+ const float m_ = cbrtf(m);
+ const float s_ = cbrtf(s);
+
+ const struct Lab ret = {
+ .L = 0.2104542553f * l_ + 0.7936177850f * m_ - 0.0040720468f * s_,
+ .a = 1.9779984951f * l_ - 2.4285922050f * m_ + 0.4505937099f * s_,
+ .b = 0.0259040371f * l_ + 0.7827717662f * m_ - 0.8086757660f * s_,
+ };
+
+ return ret;
+}
diff --git a/libavfilter/palette.h b/libavfilter/palette.h
new file mode 100644
index 0000000000..2c1c609ba2
--- /dev/null
+++ b/libavfilter/palette.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Björn Ottosson
+ * Copyright (c) 2022 Clément Bœsch <u pkh me>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef AVFILTER_PALETTE_H
+#define AVFILTER_PALETTE_H
+
+#include <math.h>
+#include <stdint.h>
+
+#include "libavutil/attributes.h"
+
+struct Lab {
+ float L, a, b;
+};
+
+/**
+ * Map sRGB 8-bit color component to linear float value (gamma expand from
+ * electrical to optical value).
+ */
+float ff_srgb_u8_to_linear_f32(uint8_t x);
+
+/**
+ * Map a linear float value to a sRGB 8-bit color component (gamma compressed
+ * from optical to electrical value).
+ */
+uint8_t ff_linear_f32_to_srgb_u8(float x);
+
+/**
+ * sRGB (non-linear) to OkLab conversion
+ * @see https://bottosson.github.io/posts/oklab/
+ */
+struct Lab ff_srgb_u8_to_oklab(uint32_t srgb);
+
+#endif /* AVFILTER_PALETTE_H */
--
2.38.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH 03/15] avfilter/palette{use, gen}: simplify a few alpha masks
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 01/15] Revert "avfilter/vf_palette(gen|use): support palettes with alpha" Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 02/15] avfilter: add palette utils Clément Bœsch
@ 2022-11-05 15:26 ` Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 04/15] avfilter/paletteuse: switch from u8[4] to u32 for color code Clément Bœsch
` (16 subsequent siblings)
19 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-11-05 15:26 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
---
libavfilter/vf_paletteuse.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index cb18329bb7..f9d8a1cdfc 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -156,7 +156,7 @@ static int query_formats(AVFilterContext *ctx)
static av_always_inline uint32_t dither_color(uint32_t px, int er, int eg,
int eb, int scale, int shift)
{
- return px >> 24 << 24
+ return (px & 0xff000000)
| av_clip_uint8((px >> 16 & 0xff) + ((er * scale) / (1<<shift))) << 16
| av_clip_uint8((px >> 8 & 0xff) + ((eg * scale) / (1<<shift))) << 8
| av_clip_uint8((px & 0xff) + ((eb * scale) / (1<<shift)));
@@ -187,7 +187,7 @@ static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *pale
if (c >> 24 >= trans_thresh) { // ignore transparent entry
const uint8_t palargb[] = {
- palette[i]>>24 & 0xff,
+ palette[i]>>24,
palette[i]>>16 & 0xff,
palette[i]>> 8 & 0xff,
palette[i] & 0xff,
@@ -372,7 +372,7 @@ static av_always_inline int get_dst_color_err(PaletteUseContext *s,
uint32_t c, int *er, int *eg, int *eb,
const enum color_search_method search_method)
{
- const uint8_t a = c >> 24 & 0xff;
+ const uint8_t a = c >> 24;
const uint8_t r = c >> 16 & 0xff;
const uint8_t g = c >> 8 & 0xff;
const uint8_t b = c & 0xff;
@@ -411,7 +411,7 @@ static av_always_inline int set_frame(PaletteUseContext *s, AVFrame *out, AVFram
if (dither == DITHERING_BAYER) {
const int d = s->ordered_dither[(y & 7)<<3 | (x & 7)];
- const uint8_t a8 = src[x] >> 24 & 0xff;
+ const uint8_t a8 = src[x] >> 24;
const uint8_t r8 = src[x] >> 16 & 0xff;
const uint8_t g8 = src[x] >> 8 & 0xff;
const uint8_t b8 = src[x] & 0xff;
@@ -483,7 +483,7 @@ static av_always_inline int set_frame(PaletteUseContext *s, AVFrame *out, AVFram
if ( down) src[src_linesize + x ] = dither_color(src[src_linesize + x ], er, eg, eb, 1, 2);
} else {
- const uint8_t a = src[x] >> 24 & 0xff;
+ const uint8_t a = src[x] >> 24;
const uint8_t r = src[x] >> 16 & 0xff;
const uint8_t g = src[x] >> 8 & 0xff;
const uint8_t b = src[x] & 0xff;
@@ -630,7 +630,7 @@ static int get_next_color(const uint8_t *color_used, const uint32_t *palette,
for (i = 0; i < AVPALETTE_COUNT; i++) {
const uint32_t c = palette[i];
- const uint8_t a = c >> 24 & 0xff;
+ const uint8_t a = c >> 24;
const uint8_t r = c >> 16 & 0xff;
const uint8_t g = c >> 8 & 0xff;
const uint8_t b = c & 0xff;
@@ -700,7 +700,7 @@ static int colormap_insert(struct color_node *map,
node = &map[cur_id];
node->split = component;
node->palette_id = pal_id;
- node->val[0] = c>>24 & 0xff;
+ node->val[0] = c>>24;
node->val[1] = c>>16 & 0xff;
node->val[2] = c>> 8 & 0xff;
node->val[3] = c & 0xff;
--
2.38.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH 04/15] avfilter/paletteuse: switch from u8[4] to u32 for color code
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
` (2 preceding siblings ...)
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 03/15] avfilter/palette{use, gen}: simplify a few alpha masks Clément Bœsch
@ 2022-11-05 15:26 ` Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 05/15] avfilter/paletteuse: name target color arg consistently in colormap functions Clément Bœsch
` (15 subsequent siblings)
19 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-11-05 15:26 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
This change simplifies the code quite a bit and make it consistent with
how it's done in palettegen.
---
libavfilter/vf_paletteuse.c | 98 ++++++++++++++++---------------------
1 file changed, 41 insertions(+), 57 deletions(-)
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index f9d8a1cdfc..fb4016b11c 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -57,7 +57,7 @@ enum diff_mode {
};
struct color_node {
- uint8_t val[4];
+ uint32_t val;
uint8_t palette_id;
int split;
int left_id, right_id;
@@ -162,9 +162,11 @@ 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 int trans_thresh)
+static av_always_inline int diff(const uint32_t a, const uint32_t b, const int trans_thresh)
{
// XXX: try L*a*b with CIE76 (dL*dL + da*da + db*db)
+ const uint8_t c1[] = {a >> 24, a >> 16 & 0xff, a >> 8 & 0xff, a & 0xff};
+ const uint8_t c2[] = {b >> 24, b >> 16 & 0xff, b >> 8 & 0xff, b & 0xff};
const int dr = c1[1] - c2[1];
const int dg = c1[2] - c2[2];
const int db = c1[3] - c2[3];
@@ -178,7 +180,7 @@ static av_always_inline int diff(const uint8_t *c1, const uint8_t *c2, const int
}
}
-static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *palette, const uint8_t *argb, const int trans_thresh)
+static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *palette, const uint32_t argb, const int trans_thresh)
{
int i, pal_id = -1, min_dist = INT_MAX;
@@ -186,13 +188,7 @@ static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *pale
const uint32_t c = palette[i];
if (c >> 24 >= trans_thresh) { // ignore transparent entry
- const uint8_t palargb[] = {
- palette[i]>>24,
- palette[i]>>16 & 0xff,
- palette[i]>> 8 & 0xff,
- palette[i] & 0xff,
- };
- const int d = diff(palargb, argb, trans_thresh);
+ const int d = diff(palette[i], argb, trans_thresh);
if (d < min_dist) {
pal_id = i;
min_dist = d;
@@ -210,14 +206,14 @@ struct nearest_color {
static void colormap_nearest_node(const struct color_node *map,
const int node_pos,
- const uint8_t *target,
+ const uint32_t target,
const int trans_thresh,
struct nearest_color *nearest)
{
const struct color_node *kd = map + node_pos;
- const int s = kd->split;
+ const int shift = (3 - kd->split) * 8;
int dx, nearer_kd_id, further_kd_id;
- const uint8_t *current = kd->val;
+ const uint32_t current = kd->val;
const int current_to_target = diff(target, current, trans_thresh);
if (current_to_target < nearest->dist_sqd) {
@@ -226,7 +222,7 @@ static void colormap_nearest_node(const struct color_node *map,
}
if (kd->left_id != -1 || kd->right_id != -1) {
- dx = target[s] - current[s];
+ dx = (int)(target>>shift & 0xff) - (int)(current>>shift & 0xff);
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;
@@ -239,7 +235,7 @@ static void colormap_nearest_node(const struct color_node *map,
}
}
-static av_always_inline uint8_t colormap_nearest_recursive(const struct color_node *node, const uint8_t *rgb, const int trans_thresh)
+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(node, 0, rgb, trans_thresh, &res);
@@ -251,7 +247,7 @@ struct stack_node {
int dx2;
};
-static av_always_inline uint8_t colormap_nearest_iterative(const struct color_node *root, const uint8_t *target, const int trans_thresh)
+static av_always_inline uint8_t colormap_nearest_iterative(const struct color_node *root, const uint32_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];
@@ -260,7 +256,7 @@ static av_always_inline uint8_t colormap_nearest_iterative(const struct color_no
for (;;) {
const struct color_node *kd = &root[cur_color_id];
- const uint8_t *current = kd->val;
+ const uint32_t current = kd->val;
const int current_to_target = diff(target, current, trans_thresh);
/* Compare current color node to the target and update our best node if
@@ -274,8 +270,8 @@ static av_always_inline uint8_t colormap_nearest_iterative(const struct color_no
/* Check if it's not a leaf */
if (kd->left_id != -1 || kd->right_id != -1) {
- const int split = kd->split;
- const int dx = target[split] - current[split];
+ const int shift = (3 - kd->split) * 8;
+ const int dx = (target>>shift & 0xff) - (current>>shift & 0xff);
int nearer_kd_id, further_kd_id;
/* Define which side is the most interesting. */
@@ -331,24 +327,20 @@ end:
/**
* Check if the requested color is in the cache already. If not, find it in the
* color tree and cache it.
- * Note: a, r, g, and b are the components of color, but are passed as well to avoid
- * recomputing them (they are generally computed by the caller for other uses).
*/
static av_always_inline int color_get(PaletteUseContext *s, uint32_t color,
- uint8_t a, uint8_t r, uint8_t g, uint8_t b,
const enum color_search_method search_method)
{
int i;
- const uint8_t argb_elts[] = {a, r, g, b};
- const uint8_t rhash = r & ((1<<NBITS)-1);
- const uint8_t ghash = g & ((1<<NBITS)-1);
- const uint8_t bhash = b & ((1<<NBITS)-1);
+ const uint8_t rhash = (color>>16) & ((1<<NBITS)-1);
+ const uint8_t ghash = (color>> 8) & ((1<<NBITS)-1);
+ const uint8_t bhash = color & ((1<<NBITS)-1);
const unsigned hash = rhash<<(NBITS*2) | ghash<<NBITS | bhash;
struct cache_node *node = &s->cache[hash];
struct cached_color *e;
// first, check for transparency
- if (a < s->trans_thresh && s->transparency_index >= 0) {
+ if (color>>24 < s->trans_thresh && s->transparency_index >= 0) {
return s->transparency_index;
}
@@ -363,7 +355,7 @@ 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(search_method, s->palette, s->map, argb_elts, s->trans_thresh);
+ e->pal_entry = COLORMAP_NEAREST(search_method, s->palette, s->map, color, s->trans_thresh);
return e->pal_entry;
}
@@ -372,12 +364,11 @@ static av_always_inline int get_dst_color_err(PaletteUseContext *s,
uint32_t c, int *er, int *eg, int *eb,
const enum color_search_method search_method)
{
- const uint8_t a = c >> 24;
const uint8_t r = c >> 16 & 0xff;
const uint8_t g = c >> 8 & 0xff;
const uint8_t b = c & 0xff;
uint32_t dstc;
- const int dstx = color_get(s, c, a, r, g, b, search_method);
+ const int dstx = color_get(s, c, search_method);
if (dstx < 0)
return dstx;
dstc = s->palette[dstx];
@@ -419,7 +410,7 @@ static av_always_inline int set_frame(PaletteUseContext *s, AVFrame *out, AVFram
const uint8_t g = av_clip_uint8(g8 + d);
const uint8_t b = av_clip_uint8(b8 + d);
const uint32_t color_new = (unsigned)(a8) << 24 | r << 16 | g << 8 | b;
- const int color = color_get(s, color_new, a8, r, g, b, search_method);
+ const int color = color_get(s, color_new, search_method);
if (color < 0)
return color;
@@ -483,11 +474,7 @@ static av_always_inline int set_frame(PaletteUseContext *s, AVFrame *out, AVFram
if ( down) src[src_linesize + x ] = dither_color(src[src_linesize + x ], er, eg, eb, 1, 2);
} else {
- const uint8_t a = src[x] >> 24;
- const uint8_t r = src[x] >> 16 & 0xff;
- const uint8_t g = src[x] >> 8 & 0xff;
- const uint8_t b = src[x] & 0xff;
- const int color = color_get(s, src[x], a, r, g, b, search_method);
+ const int color = color_get(s, src[x], search_method);
if (color < 0)
return color;
@@ -507,20 +494,20 @@ static void disp_node(AVBPrint *buf,
int depth)
{
const struct color_node *node = &map[node_id];
- const uint32_t fontcolor = node->val[1] > 0x50 &&
- node->val[2] > 0x50 &&
- node->val[3] > 0x50 ? 0 : 0xffffff;
+ const uint32_t fontcolor = (node->val>>16 & 0xff) > 0x50 &&
+ (node->val>> 8 & 0xff) > 0x50 &&
+ (node->val & 0xff) > 0x50 ? 0 : 0xffffff;
const int rgb_comp = node->split - 1;
av_bprintf(buf, "%*cnode%d ["
"label=\"%c%02X%c%02X%c%02X%c\" "
- "fillcolor=\"#%02x%02x%02x\" "
+ "fillcolor=\"#%06"PRIX32"\" "
"fontcolor=\"#%06"PRIX32"\"]\n",
depth*INDENT, ' ', node->palette_id,
- "[ "[rgb_comp], node->val[1],
- "][ "[rgb_comp], node->val[2],
- " ]["[rgb_comp], node->val[3],
+ "[ "[rgb_comp], node->val>>16 & 0xff,
+ "][ "[rgb_comp], node->val>> 8 & 0xff,
+ " ]["[rgb_comp], node->val & 0xff,
" ]"[rgb_comp],
- node->val[1], node->val[2], node->val[3],
+ node->val & 0xffffff,
fontcolor);
if (parent_id != -1)
av_bprintf(buf, "%*cnode%d -> node%d\n", depth*INDENT, ' ',
@@ -563,16 +550,14 @@ static int debug_accuracy(const struct color_node *node, const uint32_t *palette
for (r = 0; r < 256; r++) {
for (g = 0; g < 256; g++) {
for (b = 0; b < 256; b++) {
- const uint8_t argb[] = {0xff, r, g, b};
+ const uint32_t argb = 0xff000000 | r<<16 | g<<8 | b;
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 = 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);
+ const int d1 = diff(0xff000000 | c1, argb, trans_thresh);
+ const int d2 = diff(0xff000000 | c2, argb, trans_thresh);
if (d1 != d2) {
av_log(NULL, AV_LOG_ERROR,
"/!\\ %02X%02X%02X: %d ! %d (%06"PRIX32" ! %06"PRIX32") / dist: %d ! %d\n",
@@ -686,6 +671,7 @@ static int colormap_insert(struct color_node *map,
{
uint32_t c;
int component, cur_id;
+ uint8_t comp_value;
int node_left_id = -1, node_right_id = -1;
struct color_node *node;
struct color_rect box1, box2;
@@ -700,17 +686,15 @@ static int colormap_insert(struct color_node *map,
node = &map[cur_id];
node->split = component;
node->palette_id = pal_id;
- node->val[0] = c>>24;
- node->val[1] = c>>16 & 0xff;
- node->val[2] = c>> 8 & 0xff;
- node->val[3] = c & 0xff;
+ node->val = c;
color_used[pal_id] = 1;
/* get the two boxes this node creates */
box1 = box2 = *box;
- box1.max[component-1] = node->val[component];
- box2.min[component-1] = FFMIN(node->val[component] + 1, 255);
+ comp_value = node->val >> ((3 - component) * 8) & 0xff;
+ box1.max[component-1] = comp_value;
+ box2.min[component-1] = FFMIN(comp_value + 1, 255);
node_left_id = colormap_insert(map, color_used, nb_used, palette, trans_thresh, &box1);
@@ -787,8 +771,8 @@ static void debug_mean_error(PaletteUseContext *s, const AVFrame *in1,
for (x = 0; x < in1->width; x++) {
const uint32_t c1 = src1[x];
const uint32_t c2 = palette[src2[x]];
- 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};
+ const uint32_t argb1 = 0xff000000 | c1;
+ const uint32_t argb2 = 0xff000000 | c2;
mean_err += diff(argb1, argb2, s->trans_thresh);
}
src1 += src1_linesize;
--
2.38.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH 05/15] avfilter/paletteuse: name target color arg consistently in colormap functions
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
` (3 preceding siblings ...)
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 04/15] avfilter/paletteuse: switch from u8[4] to u32 for color code Clément Bœsch
@ 2022-11-05 15:26 ` Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 06/15] avfilter/paletteuse: remove unused alpha split dimension Clément Bœsch
` (14 subsequent siblings)
19 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-11-05 15:26 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
---
libavfilter/vf_paletteuse.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index fb4016b11c..f43f077454 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -180,7 +180,7 @@ static av_always_inline int diff(const uint32_t a, const uint32_t b, const int t
}
}
-static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *palette, const uint32_t argb, const int trans_thresh)
+static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *palette, const uint32_t target, const int trans_thresh)
{
int i, pal_id = -1, min_dist = INT_MAX;
@@ -188,7 +188,7 @@ static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *pale
const uint32_t c = palette[i];
if (c >> 24 >= trans_thresh) { // ignore transparent entry
- const int d = diff(palette[i], argb, trans_thresh);
+ const int d = diff(palette[i], target, trans_thresh);
if (d < min_dist) {
pal_id = i;
min_dist = d;
@@ -235,10 +235,10 @@ static void colormap_nearest_node(const struct color_node *map,
}
}
-static av_always_inline uint8_t colormap_nearest_recursive(const struct color_node *node, const uint8_t rgb, const int trans_thresh)
+static av_always_inline uint8_t colormap_nearest_recursive(const struct color_node *node, const uint8_t target, const int trans_thresh)
{
struct nearest_color res = {.dist_sqd = INT_MAX, .node_pos = -1};
- colormap_nearest_node(node, 0, rgb, trans_thresh, &res);
+ colormap_nearest_node(node, 0, target, trans_thresh, &res);
return node[res.node_pos].palette_id;
}
--
2.38.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH 06/15] avfilter/paletteuse: remove unused alpha split dimension
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
` (4 preceding siblings ...)
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 05/15] avfilter/paletteuse: name target color arg consistently in colormap functions Clément Bœsch
@ 2022-11-05 15:26 ` Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 07/15] avfilter/paletteuse: remove redundant alpha condition Clément Bœsch
` (13 subsequent siblings)
19 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-11-05 15:26 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
The equalities in the w{r,g,b} range checks make sure longest is never
0. Even if the alpha ended up being selected in get_next_color() it
would cause underread memory accesses in its caller (colormap_insert).
---
libavfilter/vf_paletteuse.c | 33 ++++++++++++++++-----------------
1 file changed, 16 insertions(+), 17 deletions(-)
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index f43f077454..8954a02524 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -211,7 +211,7 @@ static void colormap_nearest_node(const struct color_node *map,
struct nearest_color *nearest)
{
const struct color_node *kd = map + node_pos;
- const int shift = (3 - kd->split) * 8;
+ const int shift = (2 - kd->split) * 8;
int dx, nearer_kd_id, further_kd_id;
const uint32_t current = kd->val;
const int current_to_target = diff(target, current, trans_thresh);
@@ -270,7 +270,7 @@ static av_always_inline uint8_t colormap_nearest_iterative(const struct color_no
/* Check if it's not a leaf */
if (kd->left_id != -1 || kd->right_id != -1) {
- const int shift = (3 - kd->split) * 8;
+ const int shift = (2 - kd->split) * 8;
const int dx = (target>>shift & 0xff) - (current>>shift & 0xff);
int nearer_kd_id, further_kd_id;
@@ -497,7 +497,7 @@ static void disp_node(AVBPrint *buf,
const uint32_t fontcolor = (node->val>>16 & 0xff) > 0x50 &&
(node->val>> 8 & 0xff) > 0x50 &&
(node->val & 0xff) > 0x50 ? 0 : 0xffffff;
- const int rgb_comp = node->split - 1;
+ const int rgb_comp = node->split;
av_bprintf(buf, "%*cnode%d ["
"label=\"%c%02X%c%02X%c%02X%c\" "
"fillcolor=\"#%06"PRIX32"\" "
@@ -588,16 +588,15 @@ static int cmp_##name(const void *pa, const void *pb) \
{ \
const struct color *a = pa; \
const struct color *b = pb; \
- return (int)(a->value >> (8 * (3 - (pos))) & 0xff) \
- - (int)(b->value >> (8 * (3 - (pos))) & 0xff); \
+ return (int)(a->value >> (8 * (2 - (pos))) & 0xff) \
+ - (int)(b->value >> (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};
static int get_next_color(const uint8_t *color_used, const uint32_t *palette,
const int trans_thresh,
@@ -650,9 +649,9 @@ static int get_next_color(const uint8_t *color_used, const uint32_t *palette,
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;
+ if (wr >= wg && wr >= wb) longest = 0;
+ if (wg >= wr && wg >= wb) longest = 1;
+ if (wb >= wr && wb >= wg) longest = 2;
cmpf = cmp_funcs[longest];
*component = longest;
@@ -692,13 +691,13 @@ static int colormap_insert(struct color_node *map,
/* get the two boxes this node creates */
box1 = box2 = *box;
- comp_value = node->val >> ((3 - component) * 8) & 0xff;
- box1.max[component-1] = comp_value;
- box2.min[component-1] = FFMIN(comp_value + 1, 255);
+ comp_value = node->val >> ((2 - component) * 8) & 0xff;
+ box1.max[component] = comp_value;
+ box2.min[component] = FFMIN(comp_value + 1, 255);
node_left_id = colormap_insert(map, color_used, nb_used, palette, trans_thresh, &box1);
- if (box2.min[component-1] <= box2.max[component-1])
+ if (box2.min[component] <= box2.max[component])
node_right_id = colormap_insert(map, color_used, nb_used, palette, trans_thresh, &box2);
node->left_id = node_left_id;
--
2.38.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH 07/15] avfilter/paletteuse: remove redundant alpha condition
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
` (5 preceding siblings ...)
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 06/15] avfilter/paletteuse: remove unused alpha split dimension Clément Bœsch
@ 2022-11-05 15:26 ` Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 08/15] avfilter/paletteuse: switch to a perceptual model Clément Bœsch
` (12 subsequent siblings)
19 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-11-05 15:26 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
This is redundant with a != 0xff below.
---
libavfilter/vf_paletteuse.c | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index 8954a02524..0861a70a0b 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -599,7 +599,6 @@ DECLARE_CMP_FUNC(b, 2)
static const cmp_func cmp_funcs[] = {cmp_r, cmp_g, cmp_b};
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 wr, wg, wb;
@@ -619,10 +618,6 @@ static int get_next_color(const uint8_t *color_used, const uint32_t *palette,
const uint8_t g = c >> 8 & 0xff;
const uint8_t b = c & 0xff;
- if (a < trans_thresh) {
- continue;
- }
-
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])
@@ -674,7 +669,7 @@ 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, palette, trans_thresh, &component, box);
+ const int pal_id = get_next_color(color_used, palette, &component, box);
if (pal_id < 0)
return -1;
--
2.38.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH 08/15] avfilter/paletteuse: switch to a perceptual model
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
` (6 preceding siblings ...)
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 07/15] avfilter/paletteuse: remove redundant alpha condition Clément Bœsch
@ 2022-11-05 15:26 ` Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 09/15] avfilter/palettegen: average color in linear space Clément Bœsch
` (11 subsequent siblings)
19 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-11-05 15:26 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
Now the selection of the color is based on a distance built around human
perception of color instead of the unreliable sRGB triplet one.
---
libavfilter/Makefile | 2 +-
libavfilter/vf_paletteuse.c | 196 ++++++++++----------
tests/ref/fate/filter-paletteuse-bayer | 142 +++++++-------
tests/ref/fate/filter-paletteuse-bayer0 | 142 +++++++-------
tests/ref/fate/filter-paletteuse-nodither | 142 +++++++-------
tests/ref/fate/filter-paletteuse-sierra2_4a | 142 +++++++-------
6 files changed, 387 insertions(+), 379 deletions(-)
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index ace0e60ba1..e6b6d59d2d 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -402,7 +402,7 @@ OBJS-$(CONFIG_OWDENOISE_FILTER) += vf_owdenoise.o
OBJS-$(CONFIG_PAD_FILTER) += vf_pad.o
OBJS-$(CONFIG_PAD_OPENCL_FILTER) += vf_pad_opencl.o opencl.o opencl/pad.o
OBJS-$(CONFIG_PALETTEGEN_FILTER) += vf_palettegen.o
-OBJS-$(CONFIG_PALETTEUSE_FILTER) += vf_paletteuse.o framesync.o
+OBJS-$(CONFIG_PALETTEUSE_FILTER) += vf_paletteuse.o framesync.o palette.o
OBJS-$(CONFIG_PERMS_FILTER) += f_perms.o
OBJS-$(CONFIG_PERSPECTIVE_FILTER) += vf_perspective.o
OBJS-$(CONFIG_PHASE_FILTER) += vf_phase.o
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index 0861a70a0b..7c64a63722 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -23,6 +23,8 @@
* Use a palette to downsample an input video stream.
*/
+#include <float.h>
+
#include "libavutil/bprint.h"
#include "libavutil/file_open.h"
#include "libavutil/internal.h"
@@ -32,6 +34,7 @@
#include "filters.h"
#include "framesync.h"
#include "internal.h"
+#include "palette.h"
enum dithering_mode {
DITHERING_NONE,
@@ -56,8 +59,13 @@ enum diff_mode {
NB_DIFF_MODE
};
+struct color_info {
+ uint32_t srgb;
+ float lab[3];
+};
+
struct color_node {
- uint32_t val;
+ struct color_info c;
uint8_t palette_id;
int split;
int left_id, right_id;
@@ -103,7 +111,7 @@ typedef struct PaletteUseContext {
char *dot_filename;
int color_search_method;
int calc_mean_err;
- uint64_t total_mean_err;
+ double total_mean_err;
int debug_accuracy;
} PaletteUseContext;
@@ -162,33 +170,41 @@ 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 uint32_t a, const uint32_t b, const int trans_thresh)
+static av_always_inline float diff(const struct color_info *a, const struct color_info *b, const int trans_thresh)
{
- // XXX: try L*a*b with CIE76 (dL*dL + da*da + db*db)
- const uint8_t c1[] = {a >> 24, a >> 16 & 0xff, a >> 8 & 0xff, a & 0xff};
- const uint8_t c2[] = {b >> 24, b >> 16 & 0xff, b >> 8 & 0xff, b & 0xff};
- const int dr = c1[1] - c2[1];
- const int dg = c1[2] - c2[2];
- const int db = c1[3] - c2[3];
-
- if (c1[0] < trans_thresh && c2[0] < trans_thresh) {
+ const uint8_t alpha_a = a->srgb >> 24;
+ const uint8_t alpha_b = b->srgb >> 24;
+
+ if (alpha_a < trans_thresh && alpha_b < trans_thresh) {
return 0;
- } else if (c1[0] >= trans_thresh && c2[0] >= trans_thresh) {
- return dr*dr + dg*dg + db*db;
+ } else if (alpha_a >= trans_thresh && alpha_b >= trans_thresh) {
+ const float dL = a->lab[0] - b->lab[0];
+ const float da = a->lab[1] - b->lab[1];
+ const float db = a->lab[2] - b->lab[2];
+ return dL*dL + da*da + db*db;
} else {
- return 255*255 + 255*255 + 255*255;
+ return 2.f; /* above the oklab max diff */
}
}
-static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *palette, const uint32_t target, const int trans_thresh)
+static struct color_info get_color_from_srgb(uint32_t srgb)
+{
+ const struct Lab lab = ff_srgb_u8_to_oklab(srgb);
+ struct color_info ret = {.srgb=srgb, .lab={lab.L, lab.a, lab.b}};
+ return ret;
+}
+
+static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *palette, const struct color_info *target, const int trans_thresh)
{
- int i, pal_id = -1, min_dist = INT_MAX;
+ int i, pal_id = -1;
+ float min_dist = FLT_MAX;
for (i = 0; i < AVPALETTE_COUNT; i++) {
const uint32_t c = palette[i];
if (c >> 24 >= trans_thresh) { // ignore transparent entry
- const int d = diff(palette[i], target, trans_thresh);
+ const struct color_info pal_color = get_color_from_srgb(palette[i]);
+ const float d = diff(&pal_color, target, trans_thresh);
if (d < min_dist) {
pal_id = i;
min_dist = d;
@@ -206,14 +222,13 @@ struct nearest_color {
static void colormap_nearest_node(const struct color_node *map,
const int node_pos,
- const uint32_t target,
+ const struct color_info *target,
const int trans_thresh,
struct nearest_color *nearest)
{
const struct color_node *kd = map + node_pos;
- const int shift = (2 - kd->split) * 8;
- int dx, nearer_kd_id, further_kd_id;
- const uint32_t current = kd->val;
+ int nearer_kd_id, further_kd_id;
+ const struct color_info *current = &kd->c;
const int current_to_target = diff(target, current, trans_thresh);
if (current_to_target < nearest->dist_sqd) {
@@ -222,7 +237,7 @@ static void colormap_nearest_node(const struct color_node *map,
}
if (kd->left_id != -1 || kd->right_id != -1) {
- dx = (int)(target>>shift & 0xff) - (int)(current>>shift & 0xff);
+ const float dx = target->lab[kd->split] - current->lab[kd->split];
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;
@@ -235,7 +250,7 @@ static void colormap_nearest_node(const struct color_node *map,
}
}
-static av_always_inline uint8_t colormap_nearest_recursive(const struct color_node *node, const uint8_t target, const int trans_thresh)
+static av_always_inline uint8_t colormap_nearest_recursive(const struct color_node *node, const struct color_info *target, const int trans_thresh)
{
struct nearest_color res = {.dist_sqd = INT_MAX, .node_pos = -1};
colormap_nearest_node(node, 0, target, trans_thresh, &res);
@@ -247,17 +262,18 @@ struct stack_node {
int dx2;
};
-static av_always_inline uint8_t colormap_nearest_iterative(const struct color_node *root, const uint32_t target, const int trans_thresh)
+static av_always_inline uint8_t colormap_nearest_iterative(const struct color_node *root, const struct color_info *target, const int trans_thresh)
{
- int pos = 0, best_node_id = -1, best_dist = INT_MAX, cur_color_id = 0;
+ int pos = 0, best_node_id = -1, cur_color_id = 0;
+ float best_dist = FLT_MAX;
struct stack_node nodes[16];
struct stack_node *node = &nodes[0];
for (;;) {
const struct color_node *kd = &root[cur_color_id];
- const uint32_t current = kd->val;
- const int current_to_target = diff(target, current, trans_thresh);
+ const struct color_info *current = &kd->c;
+ const float 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. */
@@ -270,8 +286,7 @@ static av_always_inline uint8_t colormap_nearest_iterative(const struct color_no
/* Check if it's not a leaf */
if (kd->left_id != -1 || kd->right_id != -1) {
- const int shift = (2 - kd->split) * 8;
- const int dx = (target>>shift & 0xff) - (current>>shift & 0xff);
+ const float dx = target->lab[kd->split] - current->lab[kd->split];
int nearer_kd_id, further_kd_id;
/* Define which side is the most interesting. */
@@ -332,6 +347,7 @@ static av_always_inline int color_get(PaletteUseContext *s, uint32_t color,
const enum color_search_method search_method)
{
int i;
+ struct color_info clrinfo;
const uint8_t rhash = (color>>16) & ((1<<NBITS)-1);
const uint8_t ghash = (color>> 8) & ((1<<NBITS)-1);
const uint8_t bhash = color & ((1<<NBITS)-1);
@@ -355,7 +371,8 @@ 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(search_method, s->palette, s->map, color, s->trans_thresh);
+ clrinfo = get_color_from_srgb(color);
+ e->pal_entry = COLORMAP_NEAREST(search_method, s->palette, s->map, &clrinfo, s->trans_thresh);
return e->pal_entry;
}
@@ -494,20 +511,18 @@ static void disp_node(AVBPrint *buf,
int depth)
{
const struct color_node *node = &map[node_id];
- const uint32_t fontcolor = (node->val>>16 & 0xff) > 0x50 &&
- (node->val>> 8 & 0xff) > 0x50 &&
- (node->val & 0xff) > 0x50 ? 0 : 0xffffff;
- const int rgb_comp = node->split;
+ const uint32_t fontcolor = node->c.lab[0] > 0.5 ? 0 : 0xffffff;
+ const int lab_comp = node->split;
av_bprintf(buf, "%*cnode%d ["
- "label=\"%c%02X%c%02X%c%02X%c\" "
+ "label=\"%c%.3f%c%.3f%c%.3f%c\" "
"fillcolor=\"#%06"PRIX32"\" "
"fontcolor=\"#%06"PRIX32"\"]\n",
depth*INDENT, ' ', node->palette_id,
- "[ "[rgb_comp], node->val>>16 & 0xff,
- "][ "[rgb_comp], node->val>> 8 & 0xff,
- " ]["[rgb_comp], node->val & 0xff,
- " ]"[rgb_comp],
- node->val & 0xffffff,
+ "[ "[lab_comp], node->c.lab[0],
+ "][ "[lab_comp], node->c.lab[1],
+ " ]["[lab_comp], node->c.lab[2],
+ " ]"[lab_comp],
+ node->c.srgb & 0xffffff,
fontcolor);
if (parent_id != -1)
av_bprintf(buf, "%*cnode%d -> node%d\n", depth*INDENT, ' ',
@@ -550,18 +565,18 @@ static int debug_accuracy(const struct color_node *node, const uint32_t *palette
for (r = 0; r < 256; r++) {
for (g = 0; g < 256; g++) {
for (b = 0; b < 256; b++) {
- const uint32_t argb = 0xff000000 | r<<16 | g<<8 | b;
- const int r1 = COLORMAP_NEAREST(search_method, palette, node, argb, trans_thresh);
- const int r2 = colormap_nearest_bruteforce(palette, argb, trans_thresh);
+ const struct color_info target = get_color_from_srgb(0xff000000 | r<<16 | g<<8 | b);
+ const int r1 = COLORMAP_NEAREST(search_method, palette, node, &target, trans_thresh);
+ const int r2 = colormap_nearest_bruteforce(palette, &target, trans_thresh);
if (r1 != r2) {
- const uint32_t c1 = palette[r1];
- const uint32_t c2 = palette[r2];
- const int d1 = diff(0xff000000 | c1, argb, trans_thresh);
- const int d2 = diff(0xff000000 | c2, argb, trans_thresh);
- if (d1 != d2) {
+ const struct color_info pal_c1 = get_color_from_srgb(0xff000000 | palette[r1]);
+ const struct color_info pal_c2 = get_color_from_srgb(0xff000000 | palette[r2]);
+ const float d1 = diff(&pal_c1, &target, trans_thresh);
+ const float d2 = diff(&pal_c2, &target, trans_thresh);
+ if (fabsf(d1 - d2) > 0.0001) {
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);
+ "/!\\ %02X%02X%02X: %d ! %d (%06"PRIX32" ! %06"PRIX32") / dist: %g ! %g\n",
+ r, g, b, r1, r2, pal_c1.srgb & 0xffffff, pal_c2.srgb & 0xffffff, d1, d2);
ret = 1;
}
}
@@ -572,66 +587,63 @@ static int debug_accuracy(const struct color_node *node, const uint32_t *palette
}
struct color {
- uint32_t value;
+ struct Lab value;
uint8_t pal_id;
};
struct color_rect {
- uint8_t min[3];
- uint8_t max[3];
+ float min[3];
+ float max[3];
};
typedef int (*cmp_func)(const void *, const void *);
-#define DECLARE_CMP_FUNC(name, pos) \
+#define DECLARE_CMP_FUNC(name) \
static int cmp_##name(const void *pa, const void *pb) \
{ \
const struct color *a = pa; \
const struct color *b = pb; \
- return (int)(a->value >> (8 * (2 - (pos))) & 0xff) \
- - (int)(b->value >> (8 * (2 - (pos))) & 0xff); \
+ return FFDIFFSIGN(a->value.name, b->value.name); \
}
-DECLARE_CMP_FUNC(r, 0)
-DECLARE_CMP_FUNC(g, 1)
-DECLARE_CMP_FUNC(b, 2)
+DECLARE_CMP_FUNC(L)
+DECLARE_CMP_FUNC(a)
+DECLARE_CMP_FUNC(b)
-static const cmp_func cmp_funcs[] = {cmp_r, cmp_g, cmp_b};
+static const cmp_func cmp_funcs[] = {cmp_L, cmp_a, cmp_b};
static int get_next_color(const uint8_t *color_used, const uint32_t *palette,
int *component, const struct color_rect *box)
{
- int wr, wg, wb;
+ float wL, wa, 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] = 0xff;
- ranges.max[0] = ranges.max[1] = ranges.max[2] = 0x00;
+ ranges.min[0] = ranges.min[1] = ranges.min[2] = FLT_MAX;
+ ranges.max[0] = ranges.max[1] = ranges.max[2] = -FLT_MAX;
for (i = 0; i < AVPALETTE_COUNT; i++) {
const uint32_t c = palette[i];
const uint8_t a = c >> 24;
- const uint8_t r = c >> 16 & 0xff;
- const uint8_t g = c >> 8 & 0xff;
- const uint8_t b = c & 0xff;
+ const struct Lab lab = ff_srgb_u8_to_oklab(c);
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])
+ lab.L < box->min[0] || lab.a < box->min[1] || lab.b < box->min[2] ||
+ lab.L > box->max[0] || lab.a > box->max[1] || lab.b > box->max[2])
continue;
- 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 (lab.L < ranges.min[0]) ranges.min[0] = lab.L;
+ if (lab.a < ranges.min[1]) ranges.min[1] = lab.a;
+ if (lab.b < ranges.min[2]) ranges.min[2] = lab.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;
+ if (lab.L > ranges.max[0]) ranges.max[0] = lab.L;
+ if (lab.a > ranges.max[1]) ranges.max[1] = lab.a;
+ if (lab.b > ranges.max[2]) ranges.max[2] = lab.b;
- tmp_pal[nb_color].value = c;
+ tmp_pal[nb_color].value = lab;
tmp_pal[nb_color].pal_id = i;
nb_color++;
@@ -641,12 +653,12 @@ static int get_next_color(const uint8_t *color_used, const uint32_t *palette,
return -1;
/* define longest axis that will be the split component */
- wr = ranges.max[0] - ranges.min[0];
- wg = ranges.max[1] - ranges.min[1];
+ wL = ranges.max[0] - ranges.min[0];
+ wa = ranges.max[1] - ranges.min[1];
wb = ranges.max[2] - ranges.min[2];
- if (wr >= wg && wr >= wb) longest = 0;
- if (wg >= wr && wg >= wb) longest = 1;
- if (wb >= wr && wb >= wg) longest = 2;
+ if (wb >= wL && wb >= wa) longest = 2;
+ if (wa >= wL && wa >= wb) longest = 1;
+ if (wL >= wa && wL >= wb) longest = 0;
cmpf = cmp_funcs[longest];
*component = longest;
@@ -663,9 +675,8 @@ static int colormap_insert(struct color_node *map,
const int trans_thresh,
const struct color_rect *box)
{
- uint32_t c;
int component, cur_id;
- uint8_t comp_value;
+ float comp_value;
int node_left_id = -1, node_right_id = -1;
struct color_node *node;
struct color_rect box1, box2;
@@ -676,19 +687,18 @@ static int colormap_insert(struct color_node *map,
/* create new node with that color */
cur_id = (*nb_used)++;
- c = palette[pal_id];
node = &map[cur_id];
node->split = component;
node->palette_id = pal_id;
- node->val = c;
+ node->c = get_color_from_srgb(palette[pal_id]);
color_used[pal_id] = 1;
/* get the two boxes this node creates */
box1 = box2 = *box;
- comp_value = node->val >> ((2 - component) * 8) & 0xff;
+ comp_value = node->c.lab[component];
box1.max[component] = comp_value;
- box2.min[component] = FFMIN(comp_value + 1, 255);
+ box2.min[component] = comp_value + FLT_EPSILON;
node_left_id = colormap_insert(map, color_used, nb_used, palette, trans_thresh, &box1);
@@ -735,8 +745,8 @@ static void load_colormap(PaletteUseContext *s)
}
}
- box.min[0] = box.min[1] = box.min[2] = 0x00;
- box.max[0] = box.max[1] = box.max[2] = 0xff;
+ box.min[0] = box.min[1] = box.min[2] = -FLT_MAX;
+ box.max[0] = box.max[1] = box.max[2] = FLT_MAX;
colormap_insert(s->map, color_used, &nb_used, s->palette, s->trans_thresh, &box);
@@ -759,15 +769,13 @@ static void debug_mean_error(PaletteUseContext *s, const AVFrame *in1,
const int src1_linesize = in1->linesize[0] >> 2;
const int src2_linesize = in2->linesize[0];
const float div = in1->width * in1->height * 3;
- unsigned mean_err = 0;
+ float mean_err = 0.f;
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 uint32_t argb1 = 0xff000000 | c1;
- const uint32_t argb2 = 0xff000000 | c2;
- mean_err += diff(argb1, argb2, s->trans_thresh);
+ const struct color_info c1 = get_color_from_srgb(0xff000000 | src1[x]);
+ const struct color_info c2 = get_color_from_srgb(0xff000000 | palette[src2[x]]);
+ mean_err += diff(&c1, &c2, s->trans_thresh);
}
src1 += src1_linesize;
src2 += src2_linesize;
diff --git a/tests/ref/fate/filter-paletteuse-bayer b/tests/ref/fate/filter-paletteuse-bayer
index 5ca0115053..00c3a07229 100644
--- a/tests/ref/fate/filter-paletteuse-bayer
+++ b/tests/ref/fate/filter-paletteuse-bayer
@@ -3,74 +3,74 @@
#codec_id 0: rawvideo
#dimensions 0: 320x180
#sar 0: 1/1
-0, 0, 0, 1, 230400, 0x7b259d08
-0, 1, 1, 1, 230400, 0xf04095e0
-0, 2, 2, 1, 230400, 0x84d49cd5
-0, 3, 3, 1, 230400, 0xd7a29aaf
-0, 4, 4, 1, 230400, 0x9047947c
-0, 5, 5, 1, 230400, 0xfeb990e7
-0, 6, 6, 1, 230400, 0x51ee9295
-0, 7, 7, 1, 230400, 0x66fd4833
-0, 8, 8, 1, 230400, 0x4c0948f0
-0, 9, 9, 1, 230400, 0x632b4776
-0, 10, 10, 1, 230400, 0x7a3c87e2
-0, 11, 11, 1, 230400, 0x4a9286ba
-0, 12, 12, 1, 230400, 0x54dc8649
-0, 13, 13, 1, 230400, 0x92628944
-0, 14, 14, 1, 230400, 0x80f9899f
-0, 15, 15, 1, 230400, 0x5cd78bd8
-0, 16, 16, 1, 230400, 0x4b4ca390
-0, 17, 17, 1, 230400, 0x82cca153
-0, 18, 18, 1, 230400, 0x65f1a2d0
-0, 19, 19, 1, 230400, 0x7df6ae4c
-0, 20, 20, 1, 230400, 0x909baccc
-0, 21, 21, 1, 230400, 0x1892ac65
-0, 22, 22, 1, 230400, 0x3247bb32
-0, 23, 23, 1, 230400, 0x592fbbe5
-0, 24, 24, 1, 230400, 0x189db9d5
-0, 25, 25, 1, 230400, 0x1a38b8da
-0, 26, 26, 1, 230400, 0xccd6bd07
-0, 27, 27, 1, 230400, 0xd4a2bc53
-0, 28, 28, 1, 230400, 0x9ce3bb4e
-0, 29, 29, 1, 230400, 0x5ffdc4db
-0, 30, 30, 1, 230400, 0xc885c7c9
-0, 31, 31, 1, 230400, 0xe27b9d33
-0, 32, 32, 1, 230400, 0xac03a256
-0, 33, 33, 1, 230400, 0xa2c73929
-0, 34, 34, 1, 230400, 0x33793b73
-0, 35, 35, 1, 230400, 0x1e400add
-0, 36, 36, 1, 230400, 0x98e50c6e
-0, 37, 37, 1, 230400, 0x68ed226d
-0, 38, 38, 1, 230400, 0x569e23cb
-0, 39, 39, 1, 230400, 0x82bf3fc0
-0, 40, 40, 1, 230400, 0x2b202e86
-0, 41, 41, 1, 230400, 0x7acd2dee
-0, 42, 42, 1, 230400, 0xfe872e42
-0, 43, 43, 1, 230400, 0x026c12e5
-0, 44, 44, 1, 230400, 0x81561399
-0, 45, 45, 1, 230400, 0xa08c13b6
-0, 46, 46, 1, 230400, 0x89e712f5
-0, 47, 47, 1, 230400, 0x569011ac
-0, 48, 48, 1, 230400, 0xd4691112
-0, 49, 49, 1, 230400, 0x2e50165a
-0, 50, 50, 1, 230400, 0x0a1215b6
-0, 51, 51, 1, 230400, 0x3c5316e3
-0, 52, 52, 1, 230400, 0x079c1393
-0, 53, 53, 1, 230400, 0x39ca1c48
-0, 54, 54, 1, 230400, 0xe27f199c
-0, 55, 55, 1, 230400, 0x10ab1bab
-0, 56, 56, 1, 230400, 0xeab017c3
-0, 57, 57, 1, 230400, 0x5f701f77
-0, 58, 58, 1, 230400, 0x01371d7d
-0, 59, 59, 1, 230400, 0x22751e99
-0, 60, 60, 1, 230400, 0xaee91a97
-0, 61, 61, 1, 230400, 0x27b41f32
-0, 62, 62, 1, 230400, 0x4ff32bb1
-0, 63, 63, 1, 230400, 0x86e02864
-0, 64, 64, 1, 230400, 0x5eb52b3e
-0, 65, 65, 1, 230400, 0xd9252ba8
-0, 66, 66, 1, 230400, 0x72232d9b
-0, 67, 67, 1, 230400, 0x599a206f
-0, 68, 68, 1, 230400, 0x4d2c1ca5
-0, 69, 69, 1, 230400, 0x9166293b
-0, 70, 70, 1, 230400, 0x00992453
+0, 0, 0, 1, 230400, 0xb10296af
+0, 1, 1, 1, 230400, 0x46299027
+0, 2, 2, 1, 230400, 0xe0209504
+0, 3, 3, 1, 230400, 0xfe8e93a0
+0, 4, 4, 1, 230400, 0x3ec88da3
+0, 5, 5, 1, 230400, 0x06918aaa
+0, 6, 6, 1, 230400, 0x215a8ba4
+0, 7, 7, 1, 230400, 0x74614048
+0, 8, 8, 1, 230400, 0xe46a409e
+0, 9, 9, 1, 230400, 0x20834060
+0, 10, 10, 1, 230400, 0x29258036
+0, 11, 11, 1, 230400, 0x16207f8b
+0, 12, 12, 1, 230400, 0x90dc7e61
+0, 13, 13, 1, 230400, 0x59937f90
+0, 14, 14, 1, 230400, 0x6a487fe8
+0, 15, 15, 1, 230400, 0x73e98214
+0, 16, 16, 1, 230400, 0x48119a78
+0, 17, 17, 1, 230400, 0x999899df
+0, 18, 18, 1, 230400, 0x0d7f9b24
+0, 19, 19, 1, 230400, 0xc581a5b7
+0, 20, 20, 1, 230400, 0xaf1ea56f
+0, 21, 21, 1, 230400, 0xe010a52c
+0, 22, 22, 1, 230400, 0x8eacb3ab
+0, 23, 23, 1, 230400, 0x9e60b383
+0, 24, 24, 1, 230400, 0x69aeb25b
+0, 25, 25, 1, 230400, 0x1febb1e1
+0, 26, 26, 1, 230400, 0x5d65b4e0
+0, 27, 27, 1, 230400, 0x79ecb43f
+0, 28, 28, 1, 230400, 0x3c3eb35a
+0, 29, 29, 1, 230400, 0xb96cc086
+0, 30, 30, 1, 230400, 0x07b0c1ba
+0, 31, 31, 1, 230400, 0xe3c798ae
+0, 32, 32, 1, 230400, 0xf7389e03
+0, 33, 33, 1, 230400, 0x63222e23
+0, 34, 34, 1, 230400, 0xbe063030
+0, 35, 35, 1, 230400, 0x2819fdf3
+0, 36, 36, 1, 230400, 0x7e0bfef1
+0, 37, 37, 1, 230400, 0xee321a8b
+0, 38, 38, 1, 230400, 0x5fce1a96
+0, 39, 39, 1, 230400, 0x530a35a4
+0, 40, 40, 1, 230400, 0xa5ce2647
+0, 41, 41, 1, 230400, 0x969a25d4
+0, 42, 42, 1, 230400, 0x4f012620
+0, 43, 43, 1, 230400, 0x50bc0870
+0, 44, 44, 1, 230400, 0xc6de08a6
+0, 45, 45, 1, 230400, 0xe08b092e
+0, 46, 46, 1, 230400, 0x9a6a08d7
+0, 47, 47, 1, 230400, 0x788d074c
+0, 48, 48, 1, 230400, 0x08124376
+0, 49, 49, 1, 230400, 0xcb3748ed
+0, 50, 50, 1, 230400, 0x33e747f1
+0, 51, 51, 1, 230400, 0x9a2f4994
+0, 52, 52, 1, 230400, 0x501246e6
+0, 53, 53, 1, 230400, 0x11644de9
+0, 54, 54, 1, 230400, 0xef294aca
+0, 55, 55, 1, 230400, 0x3ade4cb3
+0, 56, 56, 1, 230400, 0x56954a34
+0, 57, 57, 1, 230400, 0x513e4feb
+0, 58, 58, 1, 230400, 0x828f4ea6
+0, 59, 59, 1, 230400, 0x76b04fc7
+0, 60, 60, 1, 230400, 0x67434c78
+0, 61, 61, 1, 230400, 0xf05250fd
+0, 62, 62, 1, 230400, 0xf4cd5ac5
+0, 63, 63, 1, 230400, 0x4e4056d9
+0, 64, 64, 1, 230400, 0x6af258fa
+0, 65, 65, 1, 230400, 0xa4525a55
+0, 66, 66, 1, 230400, 0xf3c15c01
+0, 67, 67, 1, 230400, 0x407553ae
+0, 68, 68, 1, 230400, 0x913e5009
+0, 69, 69, 1, 230400, 0x7fe95b6b
+0, 70, 70, 1, 230400, 0x24cb57fe
diff --git a/tests/ref/fate/filter-paletteuse-bayer0 b/tests/ref/fate/filter-paletteuse-bayer0
index 85b3832f19..b7b80257d6 100644
--- a/tests/ref/fate/filter-paletteuse-bayer0
+++ b/tests/ref/fate/filter-paletteuse-bayer0
@@ -3,74 +3,74 @@
#codec_id 0: rawvideo
#dimensions 0: 320x180
#sar 0: 1/1
-0, 0, 0, 1, 230400, 0xfb6042d2
-0, 1, 1, 1, 230400, 0x1c193c09
-0, 2, 2, 1, 230400, 0x183442f8
-0, 3, 3, 1, 230400, 0xa9634084
-0, 4, 4, 1, 230400, 0x90df3d2f
-0, 5, 5, 1, 230400, 0x59d7389f
-0, 6, 6, 1, 230400, 0xb9bd3a30
-0, 7, 7, 1, 230400, 0x9874ee38
-0, 8, 8, 1, 230400, 0xf661f01f
-0, 9, 9, 1, 230400, 0xacbcedbd
-0, 10, 10, 1, 230400, 0x05f02d59
-0, 11, 11, 1, 230400, 0xc54c2cc8
-0, 12, 12, 1, 230400, 0x19c92d61
-0, 13, 13, 1, 230400, 0x14902fb2
-0, 14, 14, 1, 230400, 0x99b62fb6
-0, 15, 15, 1, 230400, 0x3fc63293
-0, 16, 16, 1, 230400, 0x1eed4b38
-0, 17, 17, 1, 230400, 0xe9d747e0
-0, 18, 18, 1, 230400, 0x9825496f
-0, 19, 19, 1, 230400, 0x94625411
-0, 20, 20, 1, 230400, 0xed7052a3
-0, 21, 21, 1, 230400, 0x80d552dc
-0, 22, 22, 1, 230400, 0x89b360bb
-0, 23, 23, 1, 230400, 0xee9a616a
-0, 24, 24, 1, 230400, 0x30bb5f86
-0, 25, 25, 1, 230400, 0x5ec15eae
-0, 26, 26, 1, 230400, 0x0956633e
-0, 27, 27, 1, 230400, 0x72df62fa
-0, 28, 28, 1, 230400, 0xbafd61d0
-0, 29, 29, 1, 230400, 0x393f81f3
-0, 30, 30, 1, 230400, 0xba6a848c
-0, 31, 31, 1, 230400, 0x502ba0d9
-0, 32, 32, 1, 230400, 0xc81ba71d
-0, 33, 33, 1, 230400, 0x54cdf270
-0, 34, 34, 1, 230400, 0xe951f3e2
-0, 35, 35, 1, 230400, 0xbf15baa1
-0, 36, 36, 1, 230400, 0xbf96bb12
-0, 37, 37, 1, 230400, 0xcdd5cafe
-0, 38, 38, 1, 230400, 0x97b1cbb4
-0, 39, 39, 1, 230400, 0x955ae28f
-0, 40, 40, 1, 230400, 0x6a8dd28f
-0, 41, 41, 1, 230400, 0x8f02d268
-0, 42, 42, 1, 230400, 0x3075d269
-0, 43, 43, 1, 230400, 0x29e8b910
-0, 44, 44, 1, 230400, 0xb35ab888
-0, 45, 45, 1, 230400, 0xc3afb942
-0, 46, 46, 1, 230400, 0xeba8b860
-0, 47, 47, 1, 230400, 0x5de8b7ab
-0, 48, 48, 1, 230400, 0x90233679
-0, 49, 49, 1, 230400, 0x5fbc3abb
-0, 50, 50, 1, 230400, 0xeaa73b87
-0, 51, 51, 1, 230400, 0xbd0a3c4b
-0, 52, 52, 1, 230400, 0xeddb39ba
-0, 53, 53, 1, 230400, 0x269d4131
-0, 54, 54, 1, 230400, 0xae3e3e8c
-0, 55, 55, 1, 230400, 0x65f54056
-0, 56, 56, 1, 230400, 0xf2173c5b
-0, 57, 57, 1, 230400, 0xbd714477
-0, 58, 58, 1, 230400, 0xb60c42ed
-0, 59, 59, 1, 230400, 0x8def43a5
-0, 60, 60, 1, 230400, 0xe6a73f05
-0, 61, 61, 1, 230400, 0xedfe4430
-0, 62, 62, 1, 230400, 0x76c5505a
-0, 63, 63, 1, 230400, 0xf48d4d04
-0, 64, 64, 1, 230400, 0xa49950b5
-0, 65, 65, 1, 230400, 0xc64d51d8
-0, 66, 66, 1, 230400, 0xa08253ec
-0, 67, 67, 1, 230400, 0xd6ef4609
-0, 68, 68, 1, 230400, 0x27a241e7
-0, 69, 69, 1, 230400, 0xe5f74b4a
-0, 70, 70, 1, 230400, 0xb0194751
+0, 0, 0, 1, 230400, 0x01fe2d8a
+0, 1, 1, 1, 230400, 0x6f4627f6
+0, 2, 2, 1, 230400, 0xce492cae
+0, 3, 3, 1, 230400, 0x95bf2b0e
+0, 4, 4, 1, 230400, 0x4d212758
+0, 5, 5, 1, 230400, 0xfddd244c
+0, 6, 6, 1, 230400, 0x2bdc258d
+0, 7, 7, 1, 230400, 0x98bedba9
+0, 8, 8, 1, 230400, 0x1708dd0a
+0, 9, 9, 1, 230400, 0xff3ddc41
+0, 10, 10, 1, 230400, 0xed4318e1
+0, 11, 11, 1, 230400, 0xb38718ac
+0, 12, 12, 1, 230400, 0x0789190e
+0, 13, 13, 1, 230400, 0x5f271ae5
+0, 14, 14, 1, 230400, 0x76651b29
+0, 15, 15, 1, 230400, 0x6c301d7c
+0, 16, 16, 1, 230400, 0xb62d3725
+0, 17, 17, 1, 230400, 0x54dc33c3
+0, 18, 18, 1, 230400, 0xa9ee3569
+0, 19, 19, 1, 230400, 0xf6d23cec
+0, 20, 20, 1, 230400, 0xdcad3c8a
+0, 21, 21, 1, 230400, 0x00363bf2
+0, 22, 22, 1, 230400, 0xd12d4957
+0, 23, 23, 1, 230400, 0xab8c49a6
+0, 24, 24, 1, 230400, 0x1ad84813
+0, 25, 25, 1, 230400, 0xabd9478b
+0, 26, 26, 1, 230400, 0x12e14baf
+0, 27, 27, 1, 230400, 0x9ee14ae0
+0, 28, 28, 1, 230400, 0xd12449c8
+0, 29, 29, 1, 230400, 0x0e2a6b56
+0, 30, 30, 1, 230400, 0xe8576d6f
+0, 31, 31, 1, 230400, 0x37d98437
+0, 32, 32, 1, 230400, 0xb289885b
+0, 33, 33, 1, 230400, 0x0089d63d
+0, 34, 34, 1, 230400, 0x0014d741
+0, 35, 35, 1, 230400, 0x5a7fa00f
+0, 36, 36, 1, 230400, 0x7b6fa0fe
+0, 37, 37, 1, 230400, 0xf282b3cf
+0, 38, 38, 1, 230400, 0xc677b478
+0, 39, 39, 1, 230400, 0x8baec95a
+0, 40, 40, 1, 230400, 0x6983b858
+0, 41, 41, 1, 230400, 0x465eb79e
+0, 42, 42, 1, 230400, 0x5e30b848
+0, 43, 43, 1, 230400, 0x910e9ed3
+0, 44, 44, 1, 230400, 0x5f3e9f2d
+0, 45, 45, 1, 230400, 0xccf39fbf
+0, 46, 46, 1, 230400, 0x781a9f7d
+0, 47, 47, 1, 230400, 0x59e49e00
+0, 48, 48, 1, 230400, 0xe7c97e5b
+0, 49, 49, 1, 230400, 0xda7e838b
+0, 50, 50, 1, 230400, 0x34088343
+0, 51, 51, 1, 230400, 0x731b844c
+0, 52, 52, 1, 230400, 0x8e1581fc
+0, 53, 53, 1, 230400, 0x51d888c0
+0, 54, 54, 1, 230400, 0x461185dd
+0, 55, 55, 1, 230400, 0xc933880d
+0, 56, 56, 1, 230400, 0xb9c28413
+0, 57, 57, 1, 230400, 0x33f08cbc
+0, 58, 58, 1, 230400, 0x02688a80
+0, 59, 59, 1, 230400, 0x424c8b6a
+0, 60, 60, 1, 230400, 0xf1288742
+0, 61, 61, 1, 230400, 0x4780898b
+0, 62, 62, 1, 230400, 0xfbd59439
+0, 63, 63, 1, 230400, 0x770d9032
+0, 64, 64, 1, 230400, 0x53b99251
+0, 65, 65, 1, 230400, 0x8de79376
+0, 66, 66, 1, 230400, 0x292a95c3
+0, 67, 67, 1, 230400, 0x60dc8b46
+0, 68, 68, 1, 230400, 0xeb4288ca
+0, 69, 69, 1, 230400, 0xb9da9475
+0, 70, 70, 1, 230400, 0xc524912c
diff --git a/tests/ref/fate/filter-paletteuse-nodither b/tests/ref/fate/filter-paletteuse-nodither
index a2e61c3690..2e49c90642 100644
--- a/tests/ref/fate/filter-paletteuse-nodither
+++ b/tests/ref/fate/filter-paletteuse-nodither
@@ -3,74 +3,74 @@
#codec_id 0: rawvideo
#dimensions 0: 320x180
#sar 0: 1/1
-0, 0, 0, 1, 230400, 0x690560cb
-0, 1, 1, 1, 230400, 0x197a5a54
-0, 2, 2, 1, 230400, 0x665961db
-0, 3, 3, 1, 230400, 0xce0b5fa8
-0, 4, 4, 1, 230400, 0xa40e5cb0
-0, 5, 5, 1, 230400, 0xa5aa58da
-0, 6, 6, 1, 230400, 0x8e0259bb
-0, 7, 7, 1, 230400, 0x476d0dba
-0, 8, 8, 1, 230400, 0xfb1b0e8c
-0, 9, 9, 1, 230400, 0x50f60d3b
-0, 10, 10, 1, 230400, 0x12cd4bab
-0, 11, 11, 1, 230400, 0x4c274b13
-0, 12, 12, 1, 230400, 0xea494b0a
-0, 13, 13, 1, 230400, 0x118c4cc1
-0, 14, 14, 1, 230400, 0xd4224db7
-0, 15, 15, 1, 230400, 0xc3014f88
-0, 16, 16, 1, 230400, 0xe07a6838
-0, 17, 17, 1, 230400, 0x1b97659a
-0, 18, 18, 1, 230400, 0xf104670c
-0, 19, 19, 1, 230400, 0x7b63733d
-0, 20, 20, 1, 230400, 0x2c237200
-0, 21, 21, 1, 230400, 0x775d7248
-0, 22, 22, 1, 230400, 0xcaee7f9e
-0, 23, 23, 1, 230400, 0x4e4680a1
-0, 24, 24, 1, 230400, 0x21fb7e53
-0, 25, 25, 1, 230400, 0xf0297db6
-0, 26, 26, 1, 230400, 0x79a9829d
-0, 27, 27, 1, 230400, 0x8ccb80f7
-0, 28, 28, 1, 230400, 0xf4dd807f
-0, 29, 29, 1, 230400, 0xb6cc8696
-0, 30, 30, 1, 230400, 0x6c8a8917
-0, 31, 31, 1, 230400, 0x9e08615a
-0, 32, 32, 1, 230400, 0xc098685b
-0, 33, 33, 1, 230400, 0x5c09e710
-0, 34, 34, 1, 230400, 0xe4c4e9be
-0, 35, 35, 1, 230400, 0xac59c150
-0, 36, 36, 1, 230400, 0x6045c272
-0, 37, 37, 1, 230400, 0xf71ee6dc
-0, 38, 38, 1, 230400, 0xc82ce6f6
-0, 39, 39, 1, 230400, 0xb7ed039a
-0, 40, 40, 1, 230400, 0xda93f241
-0, 41, 41, 1, 230400, 0x194bf23b
-0, 42, 42, 1, 230400, 0xe7e6f2e2
-0, 43, 43, 1, 230400, 0xe479d834
-0, 44, 44, 1, 230400, 0xefdfd87e
-0, 45, 45, 1, 230400, 0xec66d8c0
-0, 46, 46, 1, 230400, 0x3a6bd81b
-0, 47, 47, 1, 230400, 0xb5d1d700
-0, 48, 48, 1, 230400, 0x3bc69e8b
-0, 49, 49, 1, 230400, 0x723fa455
-0, 50, 50, 1, 230400, 0x7c49a392
-0, 51, 51, 1, 230400, 0x272ea4b7
-0, 52, 52, 1, 230400, 0xebdda081
-0, 53, 53, 1, 230400, 0xfd26ab99
-0, 54, 54, 1, 230400, 0xfa02a891
-0, 55, 55, 1, 230400, 0xda2caa7f
-0, 56, 56, 1, 230400, 0x2360a611
-0, 57, 57, 1, 230400, 0xaa3baefd
-0, 58, 58, 1, 230400, 0x0961ad5c
-0, 59, 59, 1, 230400, 0x48d2ae47
-0, 60, 60, 1, 230400, 0x20eda81b
-0, 61, 61, 1, 230400, 0x8821adbb
-0, 62, 62, 1, 230400, 0x1150b810
-0, 63, 63, 1, 230400, 0x08dab596
-0, 64, 64, 1, 230400, 0x4731b7a5
-0, 65, 65, 1, 230400, 0xf382b87e
-0, 66, 66, 1, 230400, 0xdba7bac2
-0, 67, 67, 1, 230400, 0xf569acf9
-0, 68, 68, 1, 230400, 0x22d8a95d
-0, 69, 69, 1, 230400, 0xed0bb4fb
-0, 70, 70, 1, 230400, 0x2dccb218
+0, 0, 0, 1, 230400, 0xda3668c3
+0, 1, 1, 1, 230400, 0xe38c63c9
+0, 2, 2, 1, 230400, 0xc3e56916
+0, 3, 3, 1, 230400, 0x8ce566f1
+0, 4, 4, 1, 230400, 0x99b2640a
+0, 5, 5, 1, 230400, 0xa5066135
+0, 6, 6, 1, 230400, 0x35616241
+0, 7, 7, 1, 230400, 0x745a16de
+0, 8, 8, 1, 230400, 0x53af17d6
+0, 9, 9, 1, 230400, 0x566416cb
+0, 10, 10, 1, 230400, 0x8244546a
+0, 11, 11, 1, 230400, 0x214353d6
+0, 12, 12, 1, 230400, 0xdf925444
+0, 13, 13, 1, 230400, 0xc248565a
+0, 14, 14, 1, 230400, 0x2fa85718
+0, 15, 15, 1, 230400, 0x520b5943
+0, 16, 16, 1, 230400, 0xc4cf70cb
+0, 17, 17, 1, 230400, 0x3a6270db
+0, 18, 18, 1, 230400, 0xf7bd71e3
+0, 19, 19, 1, 230400, 0x12807d95
+0, 20, 20, 1, 230400, 0x332f7c3f
+0, 21, 21, 1, 230400, 0xcf4c7c0f
+0, 22, 22, 1, 230400, 0x911f8a0e
+0, 23, 23, 1, 230400, 0x3e228a70
+0, 24, 24, 1, 230400, 0x61c988ac
+0, 25, 25, 1, 230400, 0xb0858866
+0, 26, 26, 1, 230400, 0x57de8c17
+0, 27, 27, 1, 230400, 0x4bfc8ac8
+0, 28, 28, 1, 230400, 0x74a489e5
+0, 29, 29, 1, 230400, 0xdb5490df
+0, 30, 30, 1, 230400, 0x776292cf
+0, 31, 31, 1, 230400, 0xae486ba1
+0, 32, 32, 1, 230400, 0x6e3f707d
+0, 33, 33, 1, 230400, 0x263bfbca
+0, 34, 34, 1, 230400, 0xe1d7fdc4
+0, 35, 35, 1, 230400, 0xdc58c970
+0, 36, 36, 1, 230400, 0x74d4cb3c
+0, 37, 37, 1, 230400, 0x18d9ec35
+0, 38, 38, 1, 230400, 0x3760ec21
+0, 39, 39, 1, 230400, 0xc4fe0800
+0, 40, 40, 1, 230400, 0xa455f91a
+0, 41, 41, 1, 230400, 0xce4bf7e4
+0, 42, 42, 1, 230400, 0xeafef8f9
+0, 43, 43, 1, 230400, 0x4ab1df5d
+0, 44, 44, 1, 230400, 0x97d6df79
+0, 45, 45, 1, 230400, 0x1b64e00c
+0, 46, 46, 1, 230400, 0x5f33df02
+0, 47, 47, 1, 230400, 0x1badde2f
+0, 48, 48, 1, 230400, 0xac7fba78
+0, 49, 49, 1, 230400, 0xe945bf9c
+0, 50, 50, 1, 230400, 0x8ce6bed7
+0, 51, 51, 1, 230400, 0x209abff6
+0, 52, 52, 1, 230400, 0x229dbcb3
+0, 53, 53, 1, 230400, 0xb3ccc5f7
+0, 54, 54, 1, 230400, 0x9d9dc246
+0, 55, 55, 1, 230400, 0x8bbec364
+0, 56, 56, 1, 230400, 0x3699c05f
+0, 57, 57, 1, 230400, 0x37b7c94b
+0, 58, 58, 1, 230400, 0x7c91c7e8
+0, 59, 59, 1, 230400, 0xc7dfc81c
+0, 60, 60, 1, 230400, 0x3d6dc2b3
+0, 61, 61, 1, 230400, 0x1a80c7d8
+0, 62, 62, 1, 230400, 0xabced0e4
+0, 63, 63, 1, 230400, 0x922fcdfd
+0, 64, 64, 1, 230400, 0x251ad0b1
+0, 65, 65, 1, 230400, 0xde2cd0df
+0, 66, 66, 1, 230400, 0x74d2d3f4
+0, 67, 67, 1, 230400, 0x0bb5c9c1
+0, 68, 68, 1, 230400, 0x312dc634
+0, 69, 69, 1, 230400, 0x499bd13c
+0, 70, 70, 1, 230400, 0x8ec5cf57
diff --git a/tests/ref/fate/filter-paletteuse-sierra2_4a b/tests/ref/fate/filter-paletteuse-sierra2_4a
index d257820a32..e654a5c8f3 100644
--- a/tests/ref/fate/filter-paletteuse-sierra2_4a
+++ b/tests/ref/fate/filter-paletteuse-sierra2_4a
@@ -3,74 +3,74 @@
#codec_id 0: rawvideo
#dimensions 0: 320x180
#sar 0: 1/1
-0, 0, 0, 1, 230400, 0xa4f85758
-0, 1, 1, 1, 230400, 0xbe83505c
-0, 2, 2, 1, 230400, 0x0a09584e
-0, 3, 3, 1, 230400, 0xd2065629
-0, 4, 4, 1, 230400, 0x11eb5319
-0, 5, 5, 1, 230400, 0x61024f4c
-0, 6, 6, 1, 230400, 0xd5384faa
-0, 7, 7, 1, 230400, 0xdeae0343
-0, 8, 8, 1, 230400, 0xcb640541
-0, 9, 9, 1, 230400, 0xea2602c3
-0, 10, 10, 1, 230400, 0xa7974293
-0, 11, 11, 1, 230400, 0x67cd4287
-0, 12, 12, 1, 230400, 0x83fa437a
-0, 13, 13, 1, 230400, 0x852b42bf
-0, 14, 14, 1, 230400, 0x6d2d434c
-0, 15, 15, 1, 230400, 0x20c44629
-0, 16, 16, 1, 230400, 0xf2a35f57
-0, 17, 17, 1, 230400, 0x232959ec
-0, 18, 18, 1, 230400, 0x1f8e5c48
-0, 19, 19, 1, 230400, 0x88dc69bd
-0, 20, 20, 1, 230400, 0x4b6866f3
-0, 21, 21, 1, 230400, 0xe8f966dc
-0, 22, 22, 1, 230400, 0xe0877466
-0, 23, 23, 1, 230400, 0x8799748c
-0, 24, 24, 1, 230400, 0xcab871bc
-0, 25, 25, 1, 230400, 0x2e0372b4
-0, 26, 26, 1, 230400, 0x15fb77d5
-0, 27, 27, 1, 230400, 0xbadf75fc
-0, 28, 28, 1, 230400, 0xa4977626
-0, 29, 29, 1, 230400, 0x5b987943
-0, 30, 30, 1, 230400, 0x9ed57c09
-0, 31, 31, 1, 230400, 0x565d5105
-0, 32, 32, 1, 230400, 0x901b5a07
-0, 33, 33, 1, 230400, 0x8dc4e9a8
-0, 34, 34, 1, 230400, 0x0b9cee1c
-0, 35, 35, 1, 230400, 0x2bcdbe37
-0, 36, 36, 1, 230400, 0xf3e2bf71
-0, 37, 37, 1, 230400, 0xb718da67
-0, 38, 38, 1, 230400, 0x8f59da64
-0, 39, 39, 1, 230400, 0x8812f9aa
-0, 40, 40, 1, 230400, 0xe0dae6a3
-0, 41, 41, 1, 230400, 0xd2c7e5b7
-0, 42, 42, 1, 230400, 0xea2ae5d2
-0, 43, 43, 1, 230400, 0x2d66ca25
-0, 44, 44, 1, 230400, 0xf0d3cac6
-0, 45, 45, 1, 230400, 0xb9acccac
-0, 46, 46, 1, 230400, 0x8523ca4a
-0, 47, 47, 1, 230400, 0x92b9c9ef
-0, 48, 48, 1, 230400, 0x0a88946e
-0, 49, 49, 1, 230400, 0xe33699b8
-0, 50, 50, 1, 230400, 0x5e7b9917
-0, 51, 51, 1, 230400, 0xdac99998
-0, 52, 52, 1, 230400, 0xb5c995fc
-0, 53, 53, 1, 230400, 0x908b9f50
-0, 54, 54, 1, 230400, 0x60d59ced
-0, 55, 55, 1, 230400, 0x212e9f55
-0, 56, 56, 1, 230400, 0x95e69b2a
-0, 57, 57, 1, 230400, 0x6c38a34a
-0, 58, 58, 1, 230400, 0xeb32a103
-0, 59, 59, 1, 230400, 0x0131a1b7
-0, 60, 60, 1, 230400, 0xd59b9c4e
-0, 61, 61, 1, 230400, 0x2fc0a13f
-0, 62, 62, 1, 230400, 0x7a40adf9
-0, 63, 63, 1, 230400, 0x5cdbab2f
-0, 64, 64, 1, 230400, 0xcdc0ada8
-0, 65, 65, 1, 230400, 0x2f5faf32
-0, 66, 66, 1, 230400, 0xd463b224
-0, 67, 67, 1, 230400, 0xe337a2d5
-0, 68, 68, 1, 230400, 0xe775a0c1
-0, 69, 69, 1, 230400, 0x726aab49
-0, 70, 70, 1, 230400, 0x74dda81e
+0, 0, 0, 1, 230400, 0x020e46e2
+0, 1, 1, 1, 230400, 0x2e5b3ece
+0, 2, 2, 1, 230400, 0xc2a8456d
+0, 3, 3, 1, 230400, 0xd91341d5
+0, 4, 4, 1, 230400, 0xf46a3f61
+0, 5, 5, 1, 230400, 0x9ef53c6e
+0, 6, 6, 1, 230400, 0xe5803ec3
+0, 7, 7, 1, 230400, 0xb6f6f233
+0, 8, 8, 1, 230400, 0x2f84f2af
+0, 9, 9, 1, 230400, 0x690bf214
+0, 10, 10, 1, 230400, 0x358531c8
+0, 11, 11, 1, 230400, 0x5f8a3093
+0, 12, 12, 1, 230400, 0x1a17321d
+0, 13, 13, 1, 230400, 0xf088328d
+0, 14, 14, 1, 230400, 0x5e2731fe
+0, 15, 15, 1, 230400, 0x17e03504
+0, 16, 16, 1, 230400, 0x2fad4ec8
+0, 17, 17, 1, 230400, 0x6a514ce8
+0, 18, 18, 1, 230400, 0x213c4e3e
+0, 19, 19, 1, 230400, 0x520c59b2
+0, 20, 20, 1, 230400, 0x853a583a
+0, 21, 21, 1, 230400, 0xdb1456a9
+0, 22, 22, 1, 230400, 0xfe0863eb
+0, 23, 23, 1, 230400, 0x57906589
+0, 24, 24, 1, 230400, 0xeb5063f3
+0, 25, 25, 1, 230400, 0x4a1f6338
+0, 26, 26, 1, 230400, 0x46ff6867
+0, 27, 27, 1, 230400, 0x59e2666e
+0, 28, 28, 1, 230400, 0x8f22656f
+0, 29, 29, 1, 230400, 0x1079673d
+0, 30, 30, 1, 230400, 0xbe566ad0
+0, 31, 31, 1, 230400, 0xb5454233
+0, 32, 32, 1, 230400, 0x241a48c2
+0, 33, 33, 1, 230400, 0x16dddaf2
+0, 34, 34, 1, 230400, 0xb2addfae
+0, 35, 35, 1, 230400, 0x78ccaeda
+0, 36, 36, 1, 230400, 0x3e27ad31
+0, 37, 37, 1, 230400, 0x7384c6b3
+0, 38, 38, 1, 230400, 0xea3ac6a5
+0, 39, 39, 1, 230400, 0x651be84a
+0, 40, 40, 1, 230400, 0x46a6d505
+0, 41, 41, 1, 230400, 0xbe48d2fb
+0, 42, 42, 1, 230400, 0x6addd4c8
+0, 43, 43, 1, 230400, 0xc6a6b808
+0, 44, 44, 1, 230400, 0xb909b874
+0, 45, 45, 1, 230400, 0xf915b8e8
+0, 46, 46, 1, 230400, 0x467fb903
+0, 47, 47, 1, 230400, 0x7865b875
+0, 48, 48, 1, 230400, 0x1125af18
+0, 49, 49, 1, 230400, 0xf390b5ef
+0, 50, 50, 1, 230400, 0xbc76b349
+0, 51, 51, 1, 230400, 0x8426b721
+0, 52, 52, 1, 230400, 0x5b67b444
+0, 53, 53, 1, 230400, 0x4388bb8b
+0, 54, 54, 1, 230400, 0xdb06b95d
+0, 55, 55, 1, 230400, 0xc192bc26
+0, 56, 56, 1, 230400, 0x2b62b745
+0, 57, 57, 1, 230400, 0x7e3ec04f
+0, 58, 58, 1, 230400, 0x4954bdec
+0, 59, 59, 1, 230400, 0x988bbe84
+0, 60, 60, 1, 230400, 0xeb6bba83
+0, 61, 61, 1, 230400, 0x5610c02c
+0, 62, 62, 1, 230400, 0x7d76cac6
+0, 63, 63, 1, 230400, 0xcfa4c72d
+0, 64, 64, 1, 230400, 0x9c7cc904
+0, 65, 65, 1, 230400, 0xdda0c95a
+0, 66, 66, 1, 230400, 0xe9c0cc0f
+0, 67, 67, 1, 230400, 0x45abbf0d
+0, 68, 68, 1, 230400, 0x4a6eb99f
+0, 69, 69, 1, 230400, 0x7337c806
+0, 70, 70, 1, 230400, 0xf143c589
--
2.38.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH 09/15] avfilter/palettegen: average color in linear space
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
` (7 preceding siblings ...)
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 08/15] avfilter/paletteuse: switch to a perceptual model Clément Bœsch
@ 2022-11-05 15:26 ` Clément Bœsch
2022-11-05 15:39 ` Paul B Mahol
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 10/15] avfilter/palettegen: move box variance computation in a dedicated function Clément Bœsch
` (10 subsequent siblings)
19 siblings, 1 reply; 75+ messages in thread
From: Clément Bœsch @ 2022-11-05 15:26 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
sRGB colors are gamma encoded, averaging them naively is incorrect.
---
libavfilter/vf_palettegen.c | 18 ++++++++++--------
tests/ref/fate/filter-palettegen-1 | 2 +-
tests/ref/fate/filter-palettegen-2 | 2 +-
3 files changed, 12 insertions(+), 10 deletions(-)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index d335ef91e6..00bc323d17 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -30,6 +30,7 @@
#include "libavutil/intreadwrite.h"
#include "avfilter.h"
#include "internal.h"
+#include "palette.h"
/* Reference a color and how much it's used */
struct color_ref {
@@ -186,21 +187,22 @@ static int get_next_box_id_to_split(PaletteGenContext *s)
static uint32_t get_avg_color(struct color_ref * const *refs,
const struct range_box *box)
{
- int i;
+ int i, r, g, b;
const int n = box->len;
- uint64_t r = 0, g = 0, b = 0, div = 0;
+ uint64_t div = 0;
+ double rf = 0.0, gf = 0.0, bf = 0.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;
+ rf += ff_srgb_u8_to_linear_f32(ref->color >> 16 & 0xff) * ref->count;
+ gf += ff_srgb_u8_to_linear_f32(ref->color >> 8 & 0xff) * ref->count;
+ bf += ff_srgb_u8_to_linear_f32(ref->color & 0xff) * ref->count;
div += ref->count;
}
- r = r / div;
- g = g / div;
- b = b / div;
+ r = ff_linear_f32_to_srgb_u8(rf / div);
+ g = ff_linear_f32_to_srgb_u8(gf / div);
+ b = ff_linear_f32_to_srgb_u8(bf / div);
return 0xffU<<24 | r<<16 | g<<8 | b;
}
diff --git a/tests/ref/fate/filter-palettegen-1 b/tests/ref/fate/filter-palettegen-1
index bebfd24e19..df3b714ebb 100644
--- a/tests/ref/fate/filter-palettegen-1
+++ b/tests/ref/fate/filter-palettegen-1
@@ -3,4 +3,4 @@
#codec_id 0: rawvideo
#dimensions 0: 16x16
#sar 0: 1/1
-0, 0, 0, 1, 1024, 0x3395ef5a
+0, 0, 0, 1, 1024, 0x69ec37aa
diff --git a/tests/ref/fate/filter-palettegen-2 b/tests/ref/fate/filter-palettegen-2
index 9abec0fe8e..08320a8359 100644
--- a/tests/ref/fate/filter-palettegen-2
+++ b/tests/ref/fate/filter-palettegen-2
@@ -3,4 +3,4 @@
#codec_id 0: rawvideo
#dimensions 0: 16x16
#sar 0: 1/1
-0, 0, 0, 1, 1024, 0x23e072c8
+0, 0, 0, 1, 1024, 0x76078b2e
--
2.38.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH 10/15] avfilter/palettegen: move box variance computation in a dedicated function
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
` (8 preceding siblings ...)
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 09/15] avfilter/palettegen: average color in linear space Clément Bœsch
@ 2022-11-05 15:26 ` Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 11/15] avfilter/palettegen: comment on the unnormalized variance Clément Bœsch
` (9 subsequent siblings)
19 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-11-05 15:26 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
---
libavfilter/vf_palettegen.c | 25 ++++++++++++++-----------
1 file changed, 14 insertions(+), 11 deletions(-)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index 00bc323d17..2b412cdb55 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -144,12 +144,23 @@ static av_always_inline int diff(const uint32_t a, const uint32_t b)
return dr*dr + dg*dg + db*db;
}
+static void compute_box_variance(PaletteGenContext *s, struct range_box *box)
+{
+ int64_t variance = 0;
+
+ for (int i = 0; i < box->len; i++) {
+ const struct color_ref *ref = s->refs[box->start + i];
+ variance += diff(ref->color, box->color) * ref->count;
+ }
+ box->variance = variance;
+}
+
/**
* Find the next box to split: pick the one with the highest variance
*/
static int get_next_box_id_to_split(PaletteGenContext *s)
{
- int box_id, i, best_box_id = -1;
+ int box_id, best_box_id = -1;
int64_t max_variance = -1;
if (s->nb_boxes == s->max_colors - s->reserve_transparent)
@@ -159,16 +170,8 @@ static int get_next_box_id_to_split(PaletteGenContext *s)
struct range_box *box = &s->boxes[box_id];
if (s->boxes[box_id].len >= 2) {
-
- if (box->variance == -1) {
- int64_t variance = 0;
-
- 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;
- }
- box->variance = variance;
- }
+ if (box->variance == -1)
+ compute_box_variance(s, box);
if (box->variance > max_variance) {
best_box_id = box_id;
max_variance = box->variance;
--
2.38.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH 11/15] avfilter/palettegen: comment on the unnormalized variance
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
` (9 preceding siblings ...)
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 10/15] avfilter/palettegen: move box variance computation in a dedicated function Clément Bœsch
@ 2022-11-05 15:26 ` Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 12/15] avfilter/palettegen: base split decision on a perceptual model Clément Bœsch
` (8 subsequent siblings)
19 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-11-05 15:26 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
---
libavfilter/vf_palettegen.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index 2b412cdb55..b8e4463539 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -152,6 +152,24 @@ static void compute_box_variance(PaletteGenContext *s, struct range_box *box)
const struct color_ref *ref = s->refs[box->start + i];
variance += diff(ref->color, box->color) * ref->count;
}
+ /*
+ * The variance is computed as a Mean Squared Error of the distance of the
+ * current color to the box color average, with an important difference:
+ * the final sum is not normalized using the total weight of the box (the
+ * sum of the ref->count).
+ *
+ * One may expect that in order to compare the variance of the boxes
+ * between each others a normalization makes sense. Unfortunately, the
+ * normalization has the side effect of taking the "size" of the box out of
+ * the equation. In practice, this has a tendency to cause dramatic banding
+ * effects (in particular without dithering). Typically a scene where the
+ * sky is omnipresent may get much less colors assigned than its
+ * surroundings.
+ *
+ * Not normalizing causes a bias towards boxes with many colors, but that's
+ * exactly what we want. The downside is that vivid/accent colors found in
+ * small quantities tend to disappear.
+ */
box->variance = variance;
}
--
2.38.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH 12/15] avfilter/palettegen: base split decision on a perceptual model
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
` (10 preceding siblings ...)
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 11/15] avfilter/palettegen: comment on the unnormalized variance Clément Bœsch
@ 2022-11-05 15:26 ` Clément Bœsch
2022-11-05 19:07 ` Andreas Rheinhardt
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 13/15] avfilter/palettegen: use variance per-axis instead of the range Clément Bœsch
` (7 subsequent siblings)
19 siblings, 1 reply; 75+ messages in thread
From: Clément Bœsch @ 2022-11-05 15:26 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
Similar to the change in paletteuse, we rely on a perceptual model to
decide how and where to split the box.
---
libavfilter/Makefile | 2 +-
libavfilter/vf_palettegen.c | 79 ++++++++++++++++--------------
tests/ref/fate/filter-palettegen-1 | 2 +-
tests/ref/fate/filter-palettegen-2 | 2 +-
4 files changed, 44 insertions(+), 41 deletions(-)
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index e6b6d59d2d..0a31b76c6a 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -401,7 +401,7 @@ OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER) += vf_overlay_vulkan.o vulkan.o vul
OBJS-$(CONFIG_OWDENOISE_FILTER) += vf_owdenoise.o
OBJS-$(CONFIG_PAD_FILTER) += vf_pad.o
OBJS-$(CONFIG_PAD_OPENCL_FILTER) += vf_pad_opencl.o opencl.o opencl/pad.o
-OBJS-$(CONFIG_PALETTEGEN_FILTER) += vf_palettegen.o
+OBJS-$(CONFIG_PALETTEGEN_FILTER) += vf_palettegen.o palette.o
OBJS-$(CONFIG_PALETTEUSE_FILTER) += vf_paletteuse.o framesync.o palette.o
OBJS-$(CONFIG_PERMS_FILTER) += f_perms.o
OBJS-$(CONFIG_PERSPECTIVE_FILTER) += vf_perspective.o
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index b8e4463539..4c2bcba7f7 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -23,6 +23,8 @@
* Generate one palette for a whole video stream.
*/
+#include <float.h>
+
#include "libavutil/avassert.h"
#include "libavutil/internal.h"
#include "libavutil/opt.h"
@@ -35,13 +37,14 @@
/* Reference a color and how much it's used */
struct color_ref {
uint32_t color;
+ struct Lab lab;
uint64_t count;
};
/* Store a range of colors */
struct range_box {
uint32_t color; // average color
- int64_t variance; // overall variance of the box (how much the colors are spread)
+ double variance; // overall variance of the box (how much the colors are spread)
int start; // index in PaletteGenContext->refs
int len; // number of referenced colors
int sorted_by; // whether range of colors is sorted by red (0), green (1) or blue (2)
@@ -109,20 +112,19 @@ static int query_formats(AVFilterContext *ctx)
typedef int (*cmp_func)(const void *, const void *);
-#define DECLARE_CMP_FUNC(name, pos) \
+#define DECLARE_CMP_FUNC(name) \
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 FFDIFFSIGN((*a)->lab.name, (*b)->lab.name); \
}
-DECLARE_CMP_FUNC(r, 0)
-DECLARE_CMP_FUNC(g, 1)
-DECLARE_CMP_FUNC(b, 2)
+DECLARE_CMP_FUNC(L)
+DECLARE_CMP_FUNC(a)
+DECLARE_CMP_FUNC(b)
-static const cmp_func cmp_funcs[] = {cmp_r, cmp_g, cmp_b};
+static const cmp_func cmp_funcs[] = {cmp_L, cmp_a, cmp_b};
/**
* Simple color comparison for sorting the final palette
@@ -134,19 +136,19 @@ static int cmp_color(const void *a, const void *b)
return FFDIFFSIGN(box1->color , box2->color);
}
-static av_always_inline int diff(const uint32_t a, const uint32_t b)
+static av_always_inline float diff(const uint32_t a, const uint32_t b)
{
- const uint8_t c1[] = {a >> 16 & 0xff, a >> 8 & 0xff, a & 0xff};
- const uint8_t c2[] = {b >> 16 & 0xff, b >> 8 & 0xff, b & 0xff};
- const int dr = c1[0] - c2[0];
- const int dg = c1[1] - c2[1];
- const int db = c1[2] - c2[2];
- return dr*dr + dg*dg + db*db;
+ const struct Lab lab0 = ff_srgb_u8_to_oklab(a);
+ const struct Lab lab1 = ff_srgb_u8_to_oklab(b);
+ const float dL = lab0.L - lab1.L;
+ const float da = lab0.a - lab1.a;
+ const float db = lab0.b - lab1.b;
+ return dL*dL + da*da + db*db;
}
static void compute_box_variance(PaletteGenContext *s, struct range_box *box)
{
- int64_t variance = 0;
+ double variance = 0.0;
for (int i = 0; i < box->len; i++) {
const struct color_ref *ref = s->refs[box->start + i];
@@ -179,7 +181,7 @@ static void compute_box_variance(PaletteGenContext *s, struct range_box *box)
static int get_next_box_id_to_split(PaletteGenContext *s)
{
int box_id, best_box_id = -1;
- int64_t max_variance = -1;
+ double max_variance = -1.0;
if (s->nb_boxes == s->max_colors - s->reserve_transparent)
return -1;
@@ -188,14 +190,14 @@ static int get_next_box_id_to_split(PaletteGenContext *s)
struct range_box *box = &s->boxes[box_id];
if (s->boxes[box_id].len >= 2) {
- if (box->variance == -1)
+ if (box->variance == -1.0)
compute_box_variance(s, box);
if (box->variance > max_variance) {
best_box_id = box_id;
max_variance = box->variance;
}
} else {
- box->variance = -1;
+ box->variance = -1.0;
}
}
return best_box_id;
@@ -245,8 +247,8 @@ static void split_box(PaletteGenContext *s, struct range_box *box, int n)
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;
+ box->variance = -1.0;
+ new_box->variance = -1.0;
}
/**
@@ -343,39 +345,39 @@ static AVFrame *get_palette_frame(AVFilterContext *ctx)
box->len = s->nb_refs;
box->sorted_by = -1;
box->color = get_avg_color(s->refs, box);
- box->variance = -1;
+ box->variance = -1.0;
s->nb_boxes = 1;
while (box && box->len > 1) {
- int i, rr, gr, br, longest;
+ int i, longest;
+ double Lr, ar, br;
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[3] = {0xff, 0xff, 0xff};
- uint8_t max[3] = {0x00, 0x00, 0x00};
+ float min[3] = {FLT_MAX, FLT_MAX, FLT_MAX};
+ float max[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
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 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]);
+ const struct Lab lab = ref->lab;
+ min[0] = FFMIN(lab.L, min[0]), max[0] = FFMAX(lab.L, max[0]);
+ min[1] = FFMIN(lab.a, min[1]), max[1] = FFMAX(lab.a, max[1]);
+ min[2] = FFMIN(lab.b, min[2]), max[2] = FFMAX(lab.b, max[2]);
box_weight += ref->count;
}
/* define the axis to sort by according to the widest range of colors */
- rr = max[0] - min[0];
- gr = max[1] - min[1];
+ Lr = max[0] - min[0];
+ ar = 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
+ longest = 0;
+ if (br >= Lr && br >= ar) longest = 2;
+ if (ar >= Lr && ar >= br) longest = 1;
+ if (Lr >= ar && Lr >= br) longest = 0;
- ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64" ranges:[%2x %2x %2x] sort by %c (already sorted:%c) ",
+ ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64" ranges:[%.3f %.3f %.3f] sort by %c (already sorted:%c) ",
box_id, box->start, box->start + box->len - 1, box->len, box_weight,
- rr, gr, br, "rgb"[longest], box->sorted_by == longest ? 'y':'n');
+ Lr, ar, br, "Lab"[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) {
@@ -449,6 +451,7 @@ static int color_inc(struct hist_node *hist, uint32_t color)
if (!e)
return AVERROR(ENOMEM);
e->color = color;
+ e->lab = ff_srgb_u8_to_oklab(color);
e->count = 1;
return 1;
}
diff --git a/tests/ref/fate/filter-palettegen-1 b/tests/ref/fate/filter-palettegen-1
index df3b714ebb..7b7ce98b76 100644
--- a/tests/ref/fate/filter-palettegen-1
+++ b/tests/ref/fate/filter-palettegen-1
@@ -3,4 +3,4 @@
#codec_id 0: rawvideo
#dimensions 0: 16x16
#sar 0: 1/1
-0, 0, 0, 1, 1024, 0x69ec37aa
+0, 0, 0, 1, 1024, 0xf1fb64c1
diff --git a/tests/ref/fate/filter-palettegen-2 b/tests/ref/fate/filter-palettegen-2
index 08320a8359..b856a79273 100644
--- a/tests/ref/fate/filter-palettegen-2
+++ b/tests/ref/fate/filter-palettegen-2
@@ -3,4 +3,4 @@
#codec_id 0: rawvideo
#dimensions 0: 16x16
#sar 0: 1/1
-0, 0, 0, 1, 1024, 0x76078b2e
+0, 0, 0, 1, 1024, 0xe84a671a
--
2.38.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH 13/15] avfilter/palettegen: use variance per-axis instead of the range
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
` (11 preceding siblings ...)
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 12/15] avfilter/palettegen: base split decision on a perceptual model Clément Bœsch
@ 2022-11-05 15:26 ` Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 14/15] avfilter/palettegen: rename longest to split_axis Clément Bœsch
` (6 subsequent siblings)
19 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-11-05 15:26 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
The split decision is now based on the per-axis variance instead of how
wide they are.
---
libavfilter/vf_palettegen.c | 61 ++++++++++++++++--------------
tests/ref/fate/filter-palettegen-1 | 2 +-
tests/ref/fate/filter-palettegen-2 | 2 +-
3 files changed, 35 insertions(+), 30 deletions(-)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index 4c2bcba7f7..2976012512 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -45,6 +45,7 @@ struct color_ref {
struct range_box {
uint32_t color; // average color
double variance; // overall variance of the box (how much the colors are spread)
+ double axis_variance[3]; // axis specific variance
int start; // index in PaletteGenContext->refs
int len; // number of referenced colors
int sorted_by; // whether range of colors is sorted by red (0), green (1) or blue (2)
@@ -136,24 +137,29 @@ static int cmp_color(const void *a, const void *b)
return FFDIFFSIGN(box1->color , box2->color);
}
-static av_always_inline float diff(const uint32_t a, const uint32_t b)
-{
- const struct Lab lab0 = ff_srgb_u8_to_oklab(a);
- const struct Lab lab1 = ff_srgb_u8_to_oklab(b);
- const float dL = lab0.L - lab1.L;
- const float da = lab0.a - lab1.a;
- const float db = lab0.b - lab1.b;
- return dL*dL + da*da + db*db;
-}
-
static void compute_box_variance(PaletteGenContext *s, struct range_box *box)
{
double variance = 0.0;
for (int i = 0; i < box->len; i++) {
const struct color_ref *ref = s->refs[box->start + i];
- variance += diff(ref->color, box->color) * ref->count;
+ const struct Lab lab0 = ff_srgb_u8_to_oklab(ref->color);
+ const struct Lab lab1 = ff_srgb_u8_to_oklab(box->color);
+ const float dL = lab0.L - lab1.L;
+ const float da = lab0.a - lab1.a;
+ const float db = lab0.b - lab1.b;
+
+ variance += (dL*dL + da*da + db*db) * ref->count;
+
+ /*
+ * No need to normalize the per-axis variances since they are compared
+ * only locally within the box and thus share the same weight.
+ */
+ box->axis_variance[0] += dL*dL * ref->count;
+ box->axis_variance[1] += da*da * ref->count;
+ box->axis_variance[2] += db*db * ref->count;
}
+
/*
* The variance is computed as a Mean Squared Error of the distance of the
* current color to the box color average, with an important difference:
@@ -198,6 +204,7 @@ static int get_next_box_id_to_split(PaletteGenContext *s)
}
} else {
box->variance = -1.0;
+ memset(box->axis_variance, 0, sizeof(box->axis_variance));
}
}
return best_box_id;
@@ -249,6 +256,8 @@ static void split_box(PaletteGenContext *s, struct range_box *box, int n)
new_box->color = get_avg_color(s->refs, new_box);
box->variance = -1.0;
new_box->variance = -1.0;
+ memset(box->axis_variance, 0, sizeof(box->axis_variance));
+ memset(new_box->axis_variance, 0, sizeof(new_box->axis_variance));
}
/**
@@ -346,38 +355,34 @@ static AVFrame *get_palette_frame(AVFilterContext *ctx)
box->sorted_by = -1;
box->color = get_avg_color(s->refs, box);
box->variance = -1.0;
+ memset(box->axis_variance, 0, sizeof(box->axis_variance));
+ compute_box_variance(s, box);
s->nb_boxes = 1;
while (box && box->len > 1) {
int i, longest;
- double Lr, ar, br;
+ double Lv, av, bv;
uint64_t median, box_weight = 0;
/* compute the box weight (sum all the weights of the colors in the
- * range) and its boundings */
- float min[3] = {FLT_MAX, FLT_MAX, FLT_MAX};
- float max[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
+ * range) */
for (i = box->start; i < box->start + box->len; i++) {
const struct color_ref *ref = s->refs[i];
- const struct Lab lab = ref->lab;
- min[0] = FFMIN(lab.L, min[0]), max[0] = FFMAX(lab.L, max[0]);
- min[1] = FFMIN(lab.a, min[1]), max[1] = FFMAX(lab.a, max[1]);
- min[2] = FFMIN(lab.b, min[2]), max[2] = FFMAX(lab.b, max[2]);
box_weight += ref->count;
}
- /* define the axis to sort by according to the widest range of colors */
- Lr = max[0] - min[0];
- ar = max[1] - min[1];
- br = max[2] - min[2];
+ /* pick the axis with the biggest variance */
+ Lv = box->axis_variance[0];
+ av = box->axis_variance[1];
+ bv = box->axis_variance[2];
longest = 0;
- if (br >= Lr && br >= ar) longest = 2;
- if (ar >= Lr && ar >= br) longest = 1;
- if (Lr >= ar && Lr >= br) longest = 0;
+ if (bv >= Lv && bv >= av) longest = 2;
+ if (av >= Lv && av >= bv) longest = 1;
+ if (Lv >= av && Lv >= bv) longest = 0;
- ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64" ranges:[%.3f %.3f %.3f] sort by %c (already sorted:%c) ",
+ ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64" var:[%.3f %.3f %.3f] sort by %c (already sorted:%c) ",
box_id, box->start, box->start + box->len - 1, box->len, box_weight,
- Lr, ar, br, "Lab"[longest], box->sorted_by == longest ? 'y':'n');
+ Lv, av, bv, "Lab"[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) {
diff --git a/tests/ref/fate/filter-palettegen-1 b/tests/ref/fate/filter-palettegen-1
index 7b7ce98b76..35730a659f 100644
--- a/tests/ref/fate/filter-palettegen-1
+++ b/tests/ref/fate/filter-palettegen-1
@@ -3,4 +3,4 @@
#codec_id 0: rawvideo
#dimensions 0: 16x16
#sar 0: 1/1
-0, 0, 0, 1, 1024, 0xf1fb64c1
+0, 0, 0, 1, 1024, 0xd8fd2c22
diff --git a/tests/ref/fate/filter-palettegen-2 b/tests/ref/fate/filter-palettegen-2
index b856a79273..548902fed0 100644
--- a/tests/ref/fate/filter-palettegen-2
+++ b/tests/ref/fate/filter-palettegen-2
@@ -3,4 +3,4 @@
#codec_id 0: rawvideo
#dimensions 0: 16x16
#sar 0: 1/1
-0, 0, 0, 1, 1024, 0xe84a671a
+0, 0, 0, 1, 1024, 0xd1f29072
--
2.38.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH 14/15] avfilter/palettegen: rename longest to split_axis
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
` (12 preceding siblings ...)
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 13/15] avfilter/palettegen: use variance per-axis instead of the range Clément Bœsch
@ 2022-11-05 15:26 ` Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 15/15] avfilter/palette{use, gen}: update Copyright after recent changes Clément Bœsch
` (5 subsequent siblings)
19 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-11-05 15:26 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
The split heuristic is not based on the length property of the axis
anymore.
---
libavfilter/vf_palettegen.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index 2976012512..9d537f8f37 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -360,7 +360,7 @@ static AVFrame *get_palette_frame(AVFilterContext *ctx)
s->nb_boxes = 1;
while (box && box->len > 1) {
- int i, longest;
+ int i, split_axis;
double Lv, av, bv;
uint64_t median, box_weight = 0;
@@ -375,20 +375,20 @@ static AVFrame *get_palette_frame(AVFilterContext *ctx)
Lv = box->axis_variance[0];
av = box->axis_variance[1];
bv = box->axis_variance[2];
- longest = 0;
- if (bv >= Lv && bv >= av) longest = 2;
- if (av >= Lv && av >= bv) longest = 1;
- if (Lv >= av && Lv >= bv) longest = 0;
+ split_axis = 0;
+ if (bv >= Lv && bv >= av) split_axis = 2;
+ if (av >= Lv && av >= bv) split_axis = 1;
+ if (Lv >= av && Lv >= bv) split_axis = 0;
ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64" var:[%.3f %.3f %.3f] sort by %c (already sorted:%c) ",
box_id, box->start, box->start + box->len - 1, box->len, box_weight,
- Lv, av, bv, "Lab"[longest], box->sorted_by == longest ? 'y':'n');
+ Lv, av, bv, "Lab"[split_axis], box->sorted_by == split_axis ? 'y':'n');
- /* sort the range by its longest axis if it's not already sorted */
- if (box->sorted_by != longest) {
- cmp_func cmpf = cmp_funcs[longest];
+ /* sort the range by its split axis if it's not already sorted */
+ if (box->sorted_by != split_axis) {
+ cmp_func cmpf = cmp_funcs[split_axis];
AV_QSORT(&s->refs[box->start], box->len, const struct color_ref *, cmpf);
- box->sorted_by = longest;
+ box->sorted_by = split_axis;
}
/* locate the median where to split */
--
2.38.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH 15/15] avfilter/palette{use, gen}: update Copyright after recent changes
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
` (13 preceding siblings ...)
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 14/15] avfilter/palettegen: rename longest to split_axis Clément Bœsch
@ 2022-11-05 15:26 ` Clément Bœsch
2022-11-05 15:44 ` [FFmpeg-devel] Rework color quantization in palette{gen,use} Paul B Mahol
` (4 subsequent siblings)
19 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-11-05 15:26 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
---
libavfilter/vf_palettegen.c | 1 +
libavfilter/vf_paletteuse.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index 9d537f8f37..cba44d1fbf 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2015 Stupeflix
+ * Copyright (c) 2022 Clément Bœsch <u pkh me>
*
* This file is part of FFmpeg.
*
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index 7c64a63722..e2b02612ca 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2015 Stupeflix
+ * Copyright (c) 2022 Clément Bœsch <u pkh me>
*
* This file is part of FFmpeg.
*
--
2.38.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] [PATCH 09/15] avfilter/palettegen: average color in linear space
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 09/15] avfilter/palettegen: average color in linear space Clément Bœsch
@ 2022-11-05 15:39 ` Paul B Mahol
2022-11-05 18:50 ` Clément Bœsch
0 siblings, 1 reply; 75+ messages in thread
From: Paul B Mahol @ 2022-11-05 15:39 UTC (permalink / raw)
To: FFmpeg development discussions and patches; +Cc: Clément Bœsch
On 11/5/22, Clément Bœsch <u@pkh.me> wrote:
> sRGB colors are gamma encoded, averaging them naively is incorrect.
> ---
> libavfilter/vf_palettegen.c | 18 ++++++++++--------
> tests/ref/fate/filter-palettegen-1 | 2 +-
> tests/ref/fate/filter-palettegen-2 | 2 +-
> 3 files changed, 12 insertions(+), 10 deletions(-)
>
Compared speed of processing of before and after?
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] Rework color quantization in palette{gen,use}
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
` (14 preceding siblings ...)
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 15/15] avfilter/palette{use, gen}: update Copyright after recent changes Clément Bœsch
@ 2022-11-05 15:44 ` Paul B Mahol
2022-11-05 18:54 ` Clément Bœsch
2022-11-05 21:52 ` Soft Works
` (3 subsequent siblings)
19 siblings, 1 reply; 75+ messages in thread
From: Paul B Mahol @ 2022-11-05 15:44 UTC (permalink / raw)
To: FFmpeg development discussions and patches
On 11/5/22, Clément Bœsch <u@pkh.me> wrote:
> Hi,
>
> This patchset essentially fixes a few core problems in these filters and
> switches to a perceptual model.
>
> I've generated a report for each key commit on this (temporary) page:
> http://big.pkh.me/pal/ (warning: heavy page, ~500M; I did try to add some
> lazy
> loading of the images but I'm not sure it's actually working as expected).
>
> It is easy for me to add specific samples and re-run the whole thing, so
> feel
> free to suggest one.
>
> A summary on a few important observed differences can be found on the page,
> but
> I'm duplicating it here for the record:
>
> - Current: current state on master
> - Paletteuse Perceptual
> + same palette but better selection: instead of rgb triplet distance,
> it uses a
> colorspace designed for uniform perceptual color differences (OkLab)
> + overall impact not that visible immediately, but it will make sure
> the
> palette is used the best way possible, meaning improvements to
> palettegen
> will be honored
> + observations (with no dither):
> * file02 (rooftops) in max_colors=8 or 16: sky pattern is more
> stable
> * file06 (parrot) in max_colors=8: better color for the parrot
> beak
> * overall seems to favor slightly brighter colors in the currently
> offered palette
> - Palettegen Linear Average
> + sRGB colors are gamma encoded, averaging them naively is incorrect,
> we
> need to do that in linear space
> + observations (with no dither):
> * file00 (colorful drawing) in max_colors=8: contrast and color
> skin
> look better
> * file07 (abstract flower) in max_color=128 or 256: this picture
> composed of 1M different colors in the source is now more
> balanced
> (better spreading of the colors)
> - Palettegen Perceptual
> + similar to the paletteuse perceptual, we use OkLab for the color
> distance
> + observations (with no dither):
> * file07 (abstract flower): in max_colors=128 or 256 we can see
> the
> picture offering a much finer grain.
> - Palettegen Variance per axis
> + When deciding on spliting a box along an axis, instead of picking
> the
> longest one, we pick the one with the most color variance
> + Not that much impact
>
> Overall, the most brutal change is probably in file07 between current and
> last,
> 256 colors no dither in particular.
>
> Finally, I do believe a lot of other color filters could at least benefit
> from
> fixing their gamma handling (I know I'm guilty of this in various other
> filters).
gamma handling depends not on pixel format but on metadata present in frame.
>
> Regards,
>
> --
> Clément B.
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] [PATCH 09/15] avfilter/palettegen: average color in linear space
2022-11-05 15:39 ` Paul B Mahol
@ 2022-11-05 18:50 ` Clément Bœsch
0 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-11-05 18:50 UTC (permalink / raw)
To: FFmpeg development discussions and patches
On Sat, Nov 05, 2022 at 04:39:26PM +0100, Paul B Mahol wrote:
> On 11/5/22, Clément Bœsch <u@pkh.me> wrote:
> > sRGB colors are gamma encoded, averaging them naively is incorrect.
> > ---
> > libavfilter/vf_palettegen.c | 18 ++++++++++--------
> > tests/ref/fate/filter-palettegen-1 | 2 +-
> > tests/ref/fate/filter-palettegen-2 | 2 +-
> > 3 files changed, 12 insertions(+), 10 deletions(-)
> >
>
> Compared speed of processing of before and after?
Definitely slower:
Before:
palettegen:
Time (mean ± σ): 2.700 s ± 0.022 s [User: 2.926 s, System: 0.080 s]
Range (min … max): 2.674 s … 2.736 s 10 runs
paletteuse:
Time (mean ± σ): 2.315 s ± 0.036 s [User: 2.566 s, System: 0.089 s]
Range (min … max): 2.281 s … 2.400 s 10 runs
After:
palettegen:
Time (mean ± σ): 3.839 s ± 0.030 s [User: 4.076 s, System: 0.070 s]
Range (min … max): 3.788 s … 3.894 s 10 runs
paletteuse:
Time (mean ± σ): 2.850 s ± 0.040 s [User: 3.094 s, System: 0.110 s]
Range (min … max): 2.805 s … 2.954 s 10 runs
--
Clément B.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] Rework color quantization in palette{gen,use}
2022-11-05 15:44 ` [FFmpeg-devel] Rework color quantization in palette{gen,use} Paul B Mahol
@ 2022-11-05 18:54 ` Clément Bœsch
2022-11-06 13:19 ` Ronald S. Bultje
0 siblings, 1 reply; 75+ messages in thread
From: Clément Bœsch @ 2022-11-05 18:54 UTC (permalink / raw)
To: FFmpeg development discussions and patches
On Sat, Nov 05, 2022 at 04:44:39PM +0100, Paul B Mahol wrote:
[...]
> > Finally, I do believe a lot of other color filters could at least benefit
> > from
> > fixing their gamma handling (I know I'm guilty of this in various other
> > filters).
>
> gamma handling depends not on pixel format but on metadata present in frame.
Right, as suggested by Ronald on IRC, maybe it would have been appropriate
to use the vf colorspace code to honor the transfer functions.
That being said, this involves quite a substantial refactoring. Is this
considered blocking?
More specifically: do we have a lot of situation in which the RGB pixel
format is not sRGB?
--
Clément B.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] [PATCH 12/15] avfilter/palettegen: base split decision on a perceptual model
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 12/15] avfilter/palettegen: base split decision on a perceptual model Clément Bœsch
@ 2022-11-05 19:07 ` Andreas Rheinhardt
2022-11-08 21:09 ` Clément Bœsch
2022-12-27 23:20 ` Clément Bœsch
0 siblings, 2 replies; 75+ messages in thread
From: Andreas Rheinhardt @ 2022-11-05 19:07 UTC (permalink / raw)
To: ffmpeg-devel
Clément Bœsch:
> Similar to the change in paletteuse, we rely on a perceptual model to
> decide how and where to split the box.
> ---
> libavfilter/Makefile | 2 +-
> libavfilter/vf_palettegen.c | 79 ++++++++++++++++--------------
> tests/ref/fate/filter-palettegen-1 | 2 +-
> tests/ref/fate/filter-palettegen-2 | 2 +-
> 4 files changed, 44 insertions(+), 41 deletions(-)
>
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index e6b6d59d2d..0a31b76c6a 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -401,7 +401,7 @@ OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER) += vf_overlay_vulkan.o vulkan.o vul
> OBJS-$(CONFIG_OWDENOISE_FILTER) += vf_owdenoise.o
> OBJS-$(CONFIG_PAD_FILTER) += vf_pad.o
> OBJS-$(CONFIG_PAD_OPENCL_FILTER) += vf_pad_opencl.o opencl.o opencl/pad.o
> -OBJS-$(CONFIG_PALETTEGEN_FILTER) += vf_palettegen.o
> +OBJS-$(CONFIG_PALETTEGEN_FILTER) += vf_palettegen.o palette.o
> OBJS-$(CONFIG_PALETTEUSE_FILTER) += vf_paletteuse.o framesync.o palette.o
> OBJS-$(CONFIG_PERMS_FILTER) += f_perms.o
> OBJS-$(CONFIG_PERSPECTIVE_FILTER) += vf_perspective.o
> diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
> index b8e4463539..4c2bcba7f7 100644
> --- a/libavfilter/vf_palettegen.c
> +++ b/libavfilter/vf_palettegen.c
> @@ -23,6 +23,8 @@
> * Generate one palette for a whole video stream.
> */
>
> +#include <float.h>
> +
> #include "libavutil/avassert.h"
> #include "libavutil/internal.h"
> #include "libavutil/opt.h"
> @@ -35,13 +37,14 @@
> /* Reference a color and how much it's used */
> struct color_ref {
> uint32_t color;
> + struct Lab lab;
> uint64_t count;
> };
>
> /* Store a range of colors */
> struct range_box {
> uint32_t color; // average color
> - int64_t variance; // overall variance of the box (how much the colors are spread)
> + double variance; // overall variance of the box (how much the colors are spread)
> int start; // index in PaletteGenContext->refs
> int len; // number of referenced colors
> int sorted_by; // whether range of colors is sorted by red (0), green (1) or blue (2)
> @@ -109,20 +112,19 @@ static int query_formats(AVFilterContext *ctx)
>
> typedef int (*cmp_func)(const void *, const void *);
>
> -#define DECLARE_CMP_FUNC(name, pos) \
> +#define DECLARE_CMP_FUNC(name) \
> 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 FFDIFFSIGN((*a)->lab.name, (*b)->lab.name); \
> }
>
> -DECLARE_CMP_FUNC(r, 0)
> -DECLARE_CMP_FUNC(g, 1)
> -DECLARE_CMP_FUNC(b, 2)
> +DECLARE_CMP_FUNC(L)
> +DECLARE_CMP_FUNC(a)
> +DECLARE_CMP_FUNC(b)
>
> -static const cmp_func cmp_funcs[] = {cmp_r, cmp_g, cmp_b};
> +static const cmp_func cmp_funcs[] = {cmp_L, cmp_a, cmp_b};
>
> /**
> * Simple color comparison for sorting the final palette
> @@ -134,19 +136,19 @@ static int cmp_color(const void *a, const void *b)
> return FFDIFFSIGN(box1->color , box2->color);
> }
>
> -static av_always_inline int diff(const uint32_t a, const uint32_t b)
> +static av_always_inline float diff(const uint32_t a, const uint32_t b)
> {
> - const uint8_t c1[] = {a >> 16 & 0xff, a >> 8 & 0xff, a & 0xff};
> - const uint8_t c2[] = {b >> 16 & 0xff, b >> 8 & 0xff, b & 0xff};
> - const int dr = c1[0] - c2[0];
> - const int dg = c1[1] - c2[1];
> - const int db = c1[2] - c2[2];
> - return dr*dr + dg*dg + db*db;
> + const struct Lab lab0 = ff_srgb_u8_to_oklab(a);
> + const struct Lab lab1 = ff_srgb_u8_to_oklab(b);
> + const float dL = lab0.L - lab1.L;
> + const float da = lab0.a - lab1.a;
> + const float db = lab0.b - lab1.b;
> + return dL*dL + da*da + db*db;
> }
>
> static void compute_box_variance(PaletteGenContext *s, struct range_box *box)
> {
> - int64_t variance = 0;
> + double variance = 0.0;
>
> for (int i = 0; i < box->len; i++) {
> const struct color_ref *ref = s->refs[box->start + i];
> @@ -179,7 +181,7 @@ static void compute_box_variance(PaletteGenContext *s, struct range_box *box)
> static int get_next_box_id_to_split(PaletteGenContext *s)
> {
> int box_id, best_box_id = -1;
> - int64_t max_variance = -1;
> + double max_variance = -1.0;
>
> if (s->nb_boxes == s->max_colors - s->reserve_transparent)
> return -1;
> @@ -188,14 +190,14 @@ static int get_next_box_id_to_split(PaletteGenContext *s)
> struct range_box *box = &s->boxes[box_id];
>
> if (s->boxes[box_id].len >= 2) {
> - if (box->variance == -1)
> + if (box->variance == -1.0)
> compute_box_variance(s, box);
> if (box->variance > max_variance) {
> best_box_id = box_id;
> max_variance = box->variance;
> }
> } else {
> - box->variance = -1;
> + box->variance = -1.0;
> }
> }
> return best_box_id;
> @@ -245,8 +247,8 @@ static void split_box(PaletteGenContext *s, struct range_box *box, int n)
>
> 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;
> + box->variance = -1.0;
> + new_box->variance = -1.0;
> }
>
> /**
> @@ -343,39 +345,39 @@ static AVFrame *get_palette_frame(AVFilterContext *ctx)
> box->len = s->nb_refs;
> box->sorted_by = -1;
> box->color = get_avg_color(s->refs, box);
> - box->variance = -1;
> + box->variance = -1.0;
> s->nb_boxes = 1;
>
> while (box && box->len > 1) {
> - int i, rr, gr, br, longest;
> + int i, longest;
> + double Lr, ar, br;
> 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[3] = {0xff, 0xff, 0xff};
> - uint8_t max[3] = {0x00, 0x00, 0x00};
> + float min[3] = {FLT_MAX, FLT_MAX, FLT_MAX};
> + float max[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
> 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 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]);
> + const struct Lab lab = ref->lab;
> + min[0] = FFMIN(lab.L, min[0]), max[0] = FFMAX(lab.L, max[0]);
> + min[1] = FFMIN(lab.a, min[1]), max[1] = FFMAX(lab.a, max[1]);
> + min[2] = FFMIN(lab.b, min[2]), max[2] = FFMAX(lab.b, max[2]);
> box_weight += ref->count;
> }
>
> /* define the axis to sort by according to the widest range of colors */
> - rr = max[0] - min[0];
> - gr = max[1] - min[1];
> + Lr = max[0] - min[0];
> + ar = 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
> + longest = 0;
> + if (br >= Lr && br >= ar) longest = 2;
> + if (ar >= Lr && ar >= br) longest = 1;
> + if (Lr >= ar && Lr >= br) longest = 0;
>
> - ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64" ranges:[%2x %2x %2x] sort by %c (already sorted:%c) ",
> + ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64" ranges:[%.3f %.3f %.3f] sort by %c (already sorted:%c) ",
> box_id, box->start, box->start + box->len - 1, box->len, box_weight,
> - rr, gr, br, "rgb"[longest], box->sorted_by == longest ? 'y':'n');
> + Lr, ar, br, "Lab"[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) {
> @@ -449,6 +451,7 @@ static int color_inc(struct hist_node *hist, uint32_t color)
> if (!e)
> return AVERROR(ENOMEM);
> e->color = color;
> + e->lab = ff_srgb_u8_to_oklab(color);
> e->count = 1;
> return 1;
> }
> diff --git a/tests/ref/fate/filter-palettegen-1 b/tests/ref/fate/filter-palettegen-1
> index df3b714ebb..7b7ce98b76 100644
> --- a/tests/ref/fate/filter-palettegen-1
> +++ b/tests/ref/fate/filter-palettegen-1
> @@ -3,4 +3,4 @@
> #codec_id 0: rawvideo
> #dimensions 0: 16x16
> #sar 0: 1/1
> -0, 0, 0, 1, 1024, 0x69ec37aa
> +0, 0, 0, 1, 1024, 0xf1fb64c1
> diff --git a/tests/ref/fate/filter-palettegen-2 b/tests/ref/fate/filter-palettegen-2
> index 08320a8359..b856a79273 100644
> --- a/tests/ref/fate/filter-palettegen-2
> +++ b/tests/ref/fate/filter-palettegen-2
> @@ -3,4 +3,4 @@
> #codec_id 0: rawvideo
> #dimensions 0: 16x16
> #sar 0: 1/1
> -0, 0, 0, 1, 1024, 0x76078b2e
> +0, 0, 0, 1, 1024, 0xe84a671a
You are adding floating point to places where there was no floating
point before (some other patches of this patchset do the same). Is this
still bitexact across all supported arches?
https://patchwork.ffmpeg.org/project/ffmpeg/patch/20221105152617.1809282-13-u@pkh.me/
makes me believe that the answer is no.
- Andreas
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] Rework color quantization in palette{gen,use}
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
` (15 preceding siblings ...)
2022-11-05 15:44 ` [FFmpeg-devel] Rework color quantization in palette{gen,use} Paul B Mahol
@ 2022-11-05 21:52 ` Soft Works
2022-11-06 17:09 ` Michael Niedermayer
` (2 subsequent siblings)
19 siblings, 0 replies; 75+ messages in thread
From: Soft Works @ 2022-11-05 21:52 UTC (permalink / raw)
To: FFmpeg development discussions and patches
> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> Clément Bœsch
> Sent: Saturday, November 5, 2022 4:26 PM
> To: ffmpeg-devel@ffmpeg.org
> Subject: [FFmpeg-devel] Rework color quantization in palette{gen,use}
>
> Hi,
>
> This patchset essentially fixes a few core problems in these filters
> and
> switches to a perceptual model.
>
> I've generated a report for each key commit on this (temporary) page:
> http://big.pkh.me/pal/ (warning: heavy page, ~500M; I did try to add
> some lazy
> loading of the images but I'm not sure it's actually working as
> expected).
>
> It is easy for me to add specific samples and re-run the whole thing,
> so feel
> free to suggest one.
The "rainbow Q" image would be nice to see in comparison.
Thanks,
softworkz
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] Rework color quantization in palette{gen,use}
2022-11-05 18:54 ` Clément Bœsch
@ 2022-11-06 13:19 ` Ronald S. Bultje
2022-11-08 21:22 ` Clément Bœsch
0 siblings, 1 reply; 75+ messages in thread
From: Ronald S. Bultje @ 2022-11-06 13:19 UTC (permalink / raw)
To: FFmpeg development discussions and patches
Hi,
On Sat, Nov 5, 2022 at 2:54 PM Clément Bœsch <u@pkh.me> wrote:
> On Sat, Nov 05, 2022 at 04:44:39PM +0100, Paul B Mahol wrote:
> [...]
> > > Finally, I do believe a lot of other color filters could at least
> benefit
> > > from
> > > fixing their gamma handling (I know I'm guilty of this in various other
> > > filters).
> >
> > gamma handling depends not on pixel format but on metadata present in
> frame.
>
> Right, as suggested by Ronald on IRC, maybe it would have been appropriate
> to use the vf colorspace code to honor the transfer functions.
>
> That being said, this involves quite a substantial refactoring. Is this
> considered blocking?
>
Not for me. I'd like a big fat FIXME and I (or you, or anyone) can look
into this at some point in the future.
Ronald
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] Rework color quantization in palette{gen,use}
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
` (16 preceding siblings ...)
2022-11-05 21:52 ` Soft Works
@ 2022-11-06 17:09 ` Michael Niedermayer
2022-11-06 17:30 ` Michael Niedermayer
2022-11-06 19:46 ` Soft Works
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
19 siblings, 1 reply; 75+ messages in thread
From: Michael Niedermayer @ 2022-11-06 17:09 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1.1: Type: text/plain, Size: 849 bytes --]
On Sat, Nov 05, 2022 at 04:26:02PM +0100, Clément Bœsch wrote:
> Hi,
>
> This patchset essentially fixes a few core problems in these filters and
> switches to a perceptual model.
>
> I've generated a report for each key commit on this (temporary) page:
> http://big.pkh.me/pal/ (warning: heavy page, ~500M; I did try to add some lazy
> loading of the images but I'm not sure it's actually working as expected).
i just looked at file00 and 16 and 64 colors with dither for it and they look
different, some areas look better before and some better afterwards
Have you done any double blind comparission ?
thx
[...]
--
Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
I have never wished to cater to the crowd; for what I know they do not
approve, and what they approve I do not know. -- Epicurus
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] Rework color quantization in palette{gen,use}
2022-11-06 17:09 ` Michael Niedermayer
@ 2022-11-06 17:30 ` Michael Niedermayer
2022-11-08 21:14 ` Clément Bœsch
2022-12-31 12:11 ` Clément Bœsch
0 siblings, 2 replies; 75+ messages in thread
From: Michael Niedermayer @ 2022-11-06 17:30 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1.1: Type: text/plain, Size: 1428 bytes --]
On Sun, Nov 06, 2022 at 06:09:41PM +0100, Michael Niedermayer wrote:
> On Sat, Nov 05, 2022 at 04:26:02PM +0100, Clément Bœsch wrote:
> > Hi,
> >
> > This patchset essentially fixes a few core problems in these filters and
> > switches to a perceptual model.
> >
> > I've generated a report for each key commit on this (temporary) page:
> > http://big.pkh.me/pal/ (warning: heavy page, ~500M; I did try to add some lazy
> > loading of the images but I'm not sure it's actually working as expected).
>
> i just looked at file00 and 16 and 64 colors with dither for it and they look
> different, some areas look better before and some better afterwards
looked at more of the 16 color cases with dither
(16 colors as i asumed fewer would magnify any issues )
file 01, IMHO current looks better than last (variance per axis)
file 02, IMHO current looks better than last (variance per axis)
file 03, IMHO VPA looks better but both really are quite off in terms of color,
thats not the color of the original image.
file 04, VPA is not good thats not the correct color
It seems th last (variance per axis) is more pale and looses color
>
> Have you done any double blind comparission ?
[...]
--
Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
If you think the mosad wants you dead since a long time then you are either
wrong or dead since a long time.
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] Rework color quantization in palette{gen,use}
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
` (17 preceding siblings ...)
2022-11-06 17:09 ` Michael Niedermayer
@ 2022-11-06 19:46 ` Soft Works
2022-11-08 21:07 ` Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
19 siblings, 1 reply; 75+ messages in thread
From: Soft Works @ 2022-11-06 19:46 UTC (permalink / raw)
To: FFmpeg development discussions and patches
> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> Clément Bœsch
> Sent: Saturday, November 5, 2022 4:26 PM
> To: ffmpeg-devel@ffmpeg.org
> Subject: [FFmpeg-devel] Rework color quantization in palette{gen,use}
>
> Hi,
>
> This patchset essentially fixes a few core problems in these filters
> and
> switches to a perceptual model.
>
> I've generated a report for each key commit on this (temporary) page:
> http://big.pkh.me/pal/ (warning: heavy page, ~500M; I did try to add
> some lazy
> loading of the images but I'm not sure it's actually working as
> expected).
Comparing the results for the known and simple "rainbow O" example reveals
that the proposed implementation seems to be even inferior to the current
code and even farther away from what is possible to achieve:
https://gist.github.com/softworkz/e310e3c84a338f98977d70b09e3e3f4f
Regards,
softworkz
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] Rework color quantization in palette{gen,use}
2022-11-06 19:46 ` Soft Works
@ 2022-11-08 21:07 ` Clément Bœsch
2022-11-08 22:37 ` Soft Works
0 siblings, 1 reply; 75+ messages in thread
From: Clément Bœsch @ 2022-11-08 21:07 UTC (permalink / raw)
To: FFmpeg development discussions and patches
On Sun, Nov 06, 2022 at 07:46:38PM +0000, Soft Works wrote:
>
>
> > -----Original Message-----
> > From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> > Clément Bœsch
> > Sent: Saturday, November 5, 2022 4:26 PM
> > To: ffmpeg-devel@ffmpeg.org
> > Subject: [FFmpeg-devel] Rework color quantization in palette{gen,use}
> >
> > Hi,
> >
> > This patchset essentially fixes a few core problems in these filters
> > and
> > switches to a perceptual model.
> >
> > I've generated a report for each key commit on this (temporary) page:
> > http://big.pkh.me/pal/ (warning: heavy page, ~500M; I did try to add
> > some lazy
> > loading of the images but I'm not sure it's actually working as
> > expected).
>
> Comparing the results for the known and simple "rainbow O" example reveals
> that the proposed implementation seems to be even inferior to the current
> code and even farther away from what is possible to achieve:
>
> https://gist.github.com/softworkz/e310e3c84a338f98977d70b09e3e3f4f
The pngquant file on this page has 373 unique colors, and the transparency
is fake (the checkerboard is opaque white & grey). I think there is a
mistake here.
WRT the regression after the patch, I confirm that there is a problem
related to the dithering. If you try with dither=none or even
dither=bayer, you'll observe that the colors are much better. I will
update the results page at some point to include that file.
Now indeed the sierra dithering (and probably the other of the same type)
are somehow spreading way too strongly, it's unclear to me yet but that
might be a bug I introduced. I'll investigate, thanks.
Regards,
--
Clément B.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] [PATCH 12/15] avfilter/palettegen: base split decision on a perceptual model
2022-11-05 19:07 ` Andreas Rheinhardt
@ 2022-11-08 21:09 ` Clément Bœsch
2022-12-27 23:20 ` Clément Bœsch
1 sibling, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-11-08 21:09 UTC (permalink / raw)
To: FFmpeg development discussions and patches
On Sat, Nov 05, 2022 at 08:07:42PM +0100, Andreas Rheinhardt wrote:
[...]
> You are adding floating point to places where there was no floating
> point before (some other patches of this patchset do the same). Is this
> still bitexact across all supported arches?
> https://patchwork.ffmpeg.org/project/ffmpeg/patch/20221105152617.1809282-13-u@pkh.me/
> makes me believe that the answer is no.
Oof I completely forgot about that, you're right. I'm working on a
bitexact version right now, we should be ready soon™. It may also address
partially the performance issue.
Thanks for pointing this out!
Regards,
--
Clément B.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] Rework color quantization in palette{gen,use}
2022-11-06 17:30 ` Michael Niedermayer
@ 2022-11-08 21:14 ` Clément Bœsch
2022-12-31 12:11 ` Clément Bœsch
1 sibling, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-11-08 21:14 UTC (permalink / raw)
To: FFmpeg development discussions and patches
On Sun, Nov 06, 2022 at 06:30:22PM +0100, Michael Niedermayer wrote:
> On Sun, Nov 06, 2022 at 06:09:41PM +0100, Michael Niedermayer wrote:
> > On Sat, Nov 05, 2022 at 04:26:02PM +0100, Clément Bœsch wrote:
> > > Hi,
> > >
> > > This patchset essentially fixes a few core problems in these filters and
> > > switches to a perceptual model.
> > >
> > > I've generated a report for each key commit on this (temporary) page:
> > > http://big.pkh.me/pal/ (warning: heavy page, ~500M; I did try to add some lazy
> > > loading of the images but I'm not sure it's actually working as expected).
> >
> > i just looked at file00 and 16 and 64 colors with dither for it and they look
> > different, some areas look better before and some better afterwards
>
> looked at more of the 16 color cases with dither
> (16 colors as i asumed fewer would magnify any issues )
> file 01, IMHO current looks better than last (variance per axis)
> file 02, IMHO current looks better than last (variance per axis)
> file 03, IMHO VPA looks better but both really are quite off in terms of color,
> thats not the color of the original image.
> file 04, VPA is not good thats not the correct color
>
> It seems th last (variance per axis) is more pale and looses color
>
You're right, the variance per axis change is not always very good, I
might dismissed it entirely.
It also makes me question the use of the variance entirely when splitting
the boxes. I need to investigate if choosing the box with a simpler
heuristic (something naive like picking the box with the highest volume)
is not actually improving things.
I'll investigate and share the results.
Thanks for looking deeply into that!
> > Have you done any double blind comparission ?
Nope, I probably should, but I'm not sure I have the energy to setup such
a thing yet.
Regards,
--
Clément B.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] Rework color quantization in palette{gen,use}
2022-11-06 13:19 ` Ronald S. Bultje
@ 2022-11-08 21:22 ` Clément Bœsch
0 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-11-08 21:22 UTC (permalink / raw)
To: FFmpeg development discussions and patches
On Sun, Nov 06, 2022 at 08:19:24AM -0500, Ronald S. Bultje wrote:
> Hi,
>
> On Sat, Nov 5, 2022 at 2:54 PM Clément Bœsch <u@pkh.me> wrote:
>
> > On Sat, Nov 05, 2022 at 04:44:39PM +0100, Paul B Mahol wrote:
> > [...]
> > > > Finally, I do believe a lot of other color filters could at least
> > benefit
> > > > from
> > > > fixing their gamma handling (I know I'm guilty of this in various other
> > > > filters).
> > >
> > > gamma handling depends not on pixel format but on metadata present in
> > frame.
> >
> > Right, as suggested by Ronald on IRC, maybe it would have been appropriate
> > to use the vf colorspace code to honor the transfer functions.
> >
> > That being said, this involves quite a substantial refactoring. Is this
> > considered blocking?
> >
>
> Not for me. I'd like a big fat FIXME and I (or you, or anyone) can look
> into this at some point in the future.
>
I will likely add a warning, or even error out if the input is not sRGB to
limit the damage.
--
Clément B.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] Rework color quantization in palette{gen,use}
2022-11-08 21:07 ` Clément Bœsch
@ 2022-11-08 22:37 ` Soft Works
2022-12-27 23:31 ` Clément Bœsch
0 siblings, 1 reply; 75+ messages in thread
From: Soft Works @ 2022-11-08 22:37 UTC (permalink / raw)
To: FFmpeg development discussions and patches; +Cc: Clément Bœsch
[-- Attachment #1: Type: text/plain, Size: 3327 bytes --]
> -----Original Message-----
> From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> Clément Bœsch
> Sent: Tuesday, November 8, 2022 10:08 PM
> To: FFmpeg development discussions and patches <ffmpeg-
> devel@ffmpeg.org>
> Subject: Re: [FFmpeg-devel] Rework color quantization in
> palette{gen,use}
>
> On Sun, Nov 06, 2022 at 07:46:38PM +0000, Soft Works wrote:
> >
> >
> > > -----Original Message-----
> > > From: ffmpeg-devel <ffmpeg-devel-bounces@ffmpeg.org> On Behalf Of
> > > Clément Bœsch
> > > Sent: Saturday, November 5, 2022 4:26 PM
> > > To: ffmpeg-devel@ffmpeg.org
> > > Subject: [FFmpeg-devel] Rework color quantization in
> palette{gen,use}
> > >
> > > Hi,
> > >
> > > This patchset essentially fixes a few core problems in these
> filters
> > > and
> > > switches to a perceptual model.
> > >
> > > I've generated a report for each key commit on this (temporary)
> page:
> > > http://big.pkh.me/pal/ (warning: heavy page, ~500M; I did try to
> add
> > > some lazy
> > > loading of the images but I'm not sure it's actually working as
> > > expected).
> >
> > Comparing the results for the known and simple "rainbow O" example
> reveals
> > that the proposed implementation seems to be even inferior to the
> current
> > code and even farther away from what is possible to achieve:
> >
> > https://gist.github.com/softworkz/e310e3c84a338f98977d70b09e3e3f4f
>
> The pngquant file on this page has 373 unique colors, and the
> transparency
> is fake (the checkerboard is opaque white & grey). I think there is a
> mistake here.
Hi Clement,
I'm sorry about the confusion. The files in both Gists were created
in the same way: Opened the result image in PhotoShop, set the view
size to 400% and then created a screenshot and pasted into the Gist.
The reason I did it that way was that GitHub seemed to do its own
image "optimization" and I wanted to rule out any such effects and
just let others see what I see.
I couldn't find the original result from pngquant, but I have attached
the result from the elbg filter which is almost of the same quality.
For completeness, I'm also including the recent comparison, but it
seems you're already on track in this regard.
> WRT the regression after the patch, I confirm that there is a problem
> related to the dithering. If you try with dither=none or even
> dither=bayer, you'll observe that the colors are much better. I will
> update the results page at some point to include that file.
That would be great. Maybe you could also find another "simple" example
like with large-scale gradients rather than being so strongly colored
like the others?
Then I'd have a question about your file07 example. Is this the
original file or did I mix something up?
http://big.pkh.me/pal/output/0-current/file07/cfg00/0-ref.png
I'm wondering because the image is full or weird artifacts at the
edges of the green (and other) leafes.
> Now indeed the sierra dithering (and probably the other of the same
> type)
> are somehow spreading way too strongly, it's unclear to me yet but
> that
> might be a bug I introduced. I'll investigate, thanks.
Yup, okay, thanks.
PS: I'd be curious what you think about the elbg image...
Thanks,
softworkz
[-- Attachment #2: out_elbg8.zip --]
[-- Type: application/x-zip-compressed, Size: 4813 bytes --]
[-- Attachment #3: out_pngs.zip --]
[-- Type: application/x-zip-compressed, Size: 9998 bytes --]
[-- Attachment #4: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] New iteration for the color quantization in palette{gen, use}
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
` (18 preceding siblings ...)
2022-11-06 19:46 ` Soft Works
@ 2022-12-27 23:17 ` Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 01/32] avfilter/palettegen: allow a minimum of 2 colors Clément Bœsch
` (32 more replies)
19 siblings, 33 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:17 UTC (permalink / raw)
To: ffmpeg-devel
Here is a new version of the palette filters patchset. A lot changed
since last iteration. Here are a few highlights:
- The previous version had a terrible typo in the transfer function,
this is now fixed
- There is now a warning when the frames are not using sRGB transfer
function (I'm assuming there is no way to automatically insert a
convert during filter negotiation yet?)
- All the code is now using integer arithmetic so it should be fully
deterministic WRT tests. For more info, see
http://blog.pkh.me/p/38-porting-oklab-colorspace-to-integer-arithmetic.html
- The license of the palette utils is now LGPL to align with FFmpeg; the
OkLab code from Björn Ottosson as well as mine in integers are
distributed under public domain and MIT so this should be compatible
- The scoring algorithms in the palette generation (heuristics for
selecting the box and axis) changed after doing an extensive study for
which you can find the results here:
https://github.com/ubitux/research/tree/main/color-quantization#results
- Output is more deterministic/stable due to a change in the color
sorting
- Overall performance is improved quite significantly from previous
patchset but overall it's still slower than master (with a rough test
1.06x slower for palettegen and 1.11x slower for paletteuse, your
mileage may vary), which is expected given the new model. I think
that's an acceptable trade-off.
- The patchset includes a lot of cleanups and drop of code, leading to
an overall net negative: 12 files changed, 856 insertions(+), 878
deletions(-)
Like before, I generated a bunch of outputs at key points in the
history which you can consult here: http://big.pkh.me/pal/ (will be
removed after patchset is merged)
Also, the branch is accessible on github/ubitux/FFmpeg branch
lavfi-palette for convenience.
And happy xmas (a bit late) for those who celebrate it
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 01/32] avfilter/palettegen: allow a minimum of 2 colors
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
@ 2022-12-27 23:17 ` Clément Bœsch
2022-12-28 21:04 ` Tomas Härdin
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 02/32] avfilter/palette{gen, use}: revert support palettes with alpha Clément Bœsch
` (31 subsequent siblings)
32 siblings, 1 reply; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:17 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
---
libavfilter/vf_palettegen.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index 27f74fd147..c03f62b942 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -82,7 +82,7 @@ typedef struct PaletteGenContext {
#define OFFSET(x) offsetof(PaletteGenContext, x)
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
static const AVOption palettegen_options[] = {
- { "max_colors", "set the maximum number of colors to use in the palette", OFFSET(max_colors), AV_OPT_TYPE_INT, {.i64=256}, 4, 256, FLAGS },
+ { "max_colors", "set the maximum number of colors to use in the palette", OFFSET(max_colors), AV_OPT_TYPE_INT, {.i64=256}, 2, 256, FLAGS },
{ "reserve_transparent", "reserve a palette entry for transparency", OFFSET(reserve_transparent), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS },
{ "transparency_color", "set a background color for transparency", OFFSET(transparency_color), AV_OPT_TYPE_COLOR, {.str="lime"}, 0, 0, FLAGS },
{ "stats_mode", "set statistics mode", OFFSET(stats_mode), AV_OPT_TYPE_INT, {.i64=STATS_MODE_ALL_FRAMES}, 0, NB_STATS_MODE-1, FLAGS, "mode" },
@@ -586,6 +586,11 @@ static int init(AVFilterContext *ctx)
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);
+ }
+
return 0;
}
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 02/32] avfilter/palette{gen, use}: revert support palettes with alpha
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 01/32] avfilter/palettegen: allow a minimum of 2 colors Clément Bœsch
@ 2022-12-27 23:17 ` Clément Bœsch
2023-01-03 19:11 ` Paul B Mahol
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 03/32] avfilter/palette{gen, use}: simplify a few alpha masks Clément Bœsch
` (30 subsequent siblings)
32 siblings, 1 reply; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:17 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
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}.
-@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}.
-
-@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@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 03/32] avfilter/palette{gen, use}: simplify a few alpha masks
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 01/32] avfilter/palettegen: allow a minimum of 2 colors Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 02/32] avfilter/palette{gen, use}: revert support palettes with alpha Clément Bœsch
@ 2022-12-27 23:17 ` Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 04/32] avfilter/palette{gen, use}: add palette utils Clément Bœsch
` (29 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:17 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
---
libavfilter/vf_paletteuse.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index cb18329bb7..f9d8a1cdfc 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -156,7 +156,7 @@ static int query_formats(AVFilterContext *ctx)
static av_always_inline uint32_t dither_color(uint32_t px, int er, int eg,
int eb, int scale, int shift)
{
- return px >> 24 << 24
+ return (px & 0xff000000)
| av_clip_uint8((px >> 16 & 0xff) + ((er * scale) / (1<<shift))) << 16
| av_clip_uint8((px >> 8 & 0xff) + ((eg * scale) / (1<<shift))) << 8
| av_clip_uint8((px & 0xff) + ((eb * scale) / (1<<shift)));
@@ -187,7 +187,7 @@ static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *pale
if (c >> 24 >= trans_thresh) { // ignore transparent entry
const uint8_t palargb[] = {
- palette[i]>>24 & 0xff,
+ palette[i]>>24,
palette[i]>>16 & 0xff,
palette[i]>> 8 & 0xff,
palette[i] & 0xff,
@@ -372,7 +372,7 @@ static av_always_inline int get_dst_color_err(PaletteUseContext *s,
uint32_t c, int *er, int *eg, int *eb,
const enum color_search_method search_method)
{
- const uint8_t a = c >> 24 & 0xff;
+ const uint8_t a = c >> 24;
const uint8_t r = c >> 16 & 0xff;
const uint8_t g = c >> 8 & 0xff;
const uint8_t b = c & 0xff;
@@ -411,7 +411,7 @@ static av_always_inline int set_frame(PaletteUseContext *s, AVFrame *out, AVFram
if (dither == DITHERING_BAYER) {
const int d = s->ordered_dither[(y & 7)<<3 | (x & 7)];
- const uint8_t a8 = src[x] >> 24 & 0xff;
+ const uint8_t a8 = src[x] >> 24;
const uint8_t r8 = src[x] >> 16 & 0xff;
const uint8_t g8 = src[x] >> 8 & 0xff;
const uint8_t b8 = src[x] & 0xff;
@@ -483,7 +483,7 @@ static av_always_inline int set_frame(PaletteUseContext *s, AVFrame *out, AVFram
if ( down) src[src_linesize + x ] = dither_color(src[src_linesize + x ], er, eg, eb, 1, 2);
} else {
- const uint8_t a = src[x] >> 24 & 0xff;
+ const uint8_t a = src[x] >> 24;
const uint8_t r = src[x] >> 16 & 0xff;
const uint8_t g = src[x] >> 8 & 0xff;
const uint8_t b = src[x] & 0xff;
@@ -630,7 +630,7 @@ static int get_next_color(const uint8_t *color_used, const uint32_t *palette,
for (i = 0; i < AVPALETTE_COUNT; i++) {
const uint32_t c = palette[i];
- const uint8_t a = c >> 24 & 0xff;
+ const uint8_t a = c >> 24;
const uint8_t r = c >> 16 & 0xff;
const uint8_t g = c >> 8 & 0xff;
const uint8_t b = c & 0xff;
@@ -700,7 +700,7 @@ static int colormap_insert(struct color_node *map,
node = &map[cur_id];
node->split = component;
node->palette_id = pal_id;
- node->val[0] = c>>24 & 0xff;
+ node->val[0] = c>>24;
node->val[1] = c>>16 & 0xff;
node->val[2] = c>> 8 & 0xff;
node->val[3] = c & 0xff;
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 04/32] avfilter/palette{gen, use}: add palette utils
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (2 preceding siblings ...)
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 03/32] avfilter/palette{gen, use}: simplify a few alpha masks Clément Bœsch
@ 2022-12-27 23:17 ` Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 05/32] avfilter/paletteuse: switch from u8[4] to u32 for color code Clément Bœsch
` (28 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:17 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
These color management helpers will be shared by palettegen and
paletteuse in the following commits.
Note that it probably makes sense to share at least the sRGB/linear
functions with other filters at some point.
More information on OkLab can be found here: https://bottosson.github.io/posts/oklab/
For the arithmetic integer version, see:
http://blog.pkh.me/p/38-porting-oklab-colorspace-to-integer-arithmetic.html
and https://github.com/ubitux/oklab-int
---
libavfilter/palette.c | 210 ++++++++++++++++++++++++++++++++++++++++++
libavfilter/palette.h | 58 ++++++++++++
2 files changed, 268 insertions(+)
create mode 100644 libavfilter/palette.c
create mode 100644 libavfilter/palette.h
diff --git a/libavfilter/palette.c b/libavfilter/palette.c
new file mode 100644
index 0000000000..03e48fc71e
--- /dev/null
+++ b/libavfilter/palette.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2020 Björn Ottosson
+ * Copyright (c) 2022 Clément Bœsch <u pkh me>
+ *
+ * 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
+ */
+
+
+#include "libavutil/common.h"
+#include "palette.h"
+
+#define K ((1 << 16) - 1)
+#define K2 ((int64_t)K*K)
+#define P ((1 << 9) - 1)
+
+/**
+ * Table mapping formula:
+ * f(x) = x < 0.04045 ? x/12.92 : ((x+0.055)/1.055)^2.4 (sRGB EOTF)
+ * Where x is the normalized index in the table and f(x) the value in the table.
+ * f(x) is remapped to [0;K] and rounded.
+ */
+static const uint16_t srgb2linear[256] = {
+ 0x0000, 0x0014, 0x0028, 0x003c, 0x0050, 0x0063, 0x0077, 0x008b,
+ 0x009f, 0x00b3, 0x00c7, 0x00db, 0x00f1, 0x0108, 0x0120, 0x0139,
+ 0x0154, 0x016f, 0x018c, 0x01ab, 0x01ca, 0x01eb, 0x020e, 0x0232,
+ 0x0257, 0x027d, 0x02a5, 0x02ce, 0x02f9, 0x0325, 0x0353, 0x0382,
+ 0x03b3, 0x03e5, 0x0418, 0x044d, 0x0484, 0x04bc, 0x04f6, 0x0532,
+ 0x056f, 0x05ad, 0x05ed, 0x062f, 0x0673, 0x06b8, 0x06fe, 0x0747,
+ 0x0791, 0x07dd, 0x082a, 0x087a, 0x08ca, 0x091d, 0x0972, 0x09c8,
+ 0x0a20, 0x0a79, 0x0ad5, 0x0b32, 0x0b91, 0x0bf2, 0x0c55, 0x0cba,
+ 0x0d20, 0x0d88, 0x0df2, 0x0e5e, 0x0ecc, 0x0f3c, 0x0fae, 0x1021,
+ 0x1097, 0x110e, 0x1188, 0x1203, 0x1280, 0x1300, 0x1381, 0x1404,
+ 0x1489, 0x1510, 0x159a, 0x1625, 0x16b2, 0x1741, 0x17d3, 0x1866,
+ 0x18fb, 0x1993, 0x1a2c, 0x1ac8, 0x1b66, 0x1c06, 0x1ca7, 0x1d4c,
+ 0x1df2, 0x1e9a, 0x1f44, 0x1ff1, 0x20a0, 0x2150, 0x2204, 0x22b9,
+ 0x2370, 0x242a, 0x24e5, 0x25a3, 0x2664, 0x2726, 0x27eb, 0x28b1,
+ 0x297b, 0x2a46, 0x2b14, 0x2be3, 0x2cb6, 0x2d8a, 0x2e61, 0x2f3a,
+ 0x3015, 0x30f2, 0x31d2, 0x32b4, 0x3399, 0x3480, 0x3569, 0x3655,
+ 0x3742, 0x3833, 0x3925, 0x3a1a, 0x3b12, 0x3c0b, 0x3d07, 0x3e06,
+ 0x3f07, 0x400a, 0x4110, 0x4218, 0x4323, 0x4430, 0x453f, 0x4651,
+ 0x4765, 0x487c, 0x4995, 0x4ab1, 0x4bcf, 0x4cf0, 0x4e13, 0x4f39,
+ 0x5061, 0x518c, 0x52b9, 0x53e9, 0x551b, 0x5650, 0x5787, 0x58c1,
+ 0x59fe, 0x5b3d, 0x5c7e, 0x5dc2, 0x5f09, 0x6052, 0x619e, 0x62ed,
+ 0x643e, 0x6591, 0x66e8, 0x6840, 0x699c, 0x6afa, 0x6c5b, 0x6dbe,
+ 0x6f24, 0x708d, 0x71f8, 0x7366, 0x74d7, 0x764a, 0x77c0, 0x7939,
+ 0x7ab4, 0x7c32, 0x7db3, 0x7f37, 0x80bd, 0x8246, 0x83d1, 0x855f,
+ 0x86f0, 0x8884, 0x8a1b, 0x8bb4, 0x8d50, 0x8eef, 0x9090, 0x9235,
+ 0x93dc, 0x9586, 0x9732, 0x98e2, 0x9a94, 0x9c49, 0x9e01, 0x9fbb,
+ 0xa179, 0xa339, 0xa4fc, 0xa6c2, 0xa88b, 0xaa56, 0xac25, 0xadf6,
+ 0xafca, 0xb1a1, 0xb37b, 0xb557, 0xb737, 0xb919, 0xbaff, 0xbce7,
+ 0xbed2, 0xc0c0, 0xc2b1, 0xc4a5, 0xc69c, 0xc895, 0xca92, 0xcc91,
+ 0xce94, 0xd099, 0xd2a1, 0xd4ad, 0xd6bb, 0xd8cc, 0xdae0, 0xdcf7,
+ 0xdf11, 0xe12e, 0xe34e, 0xe571, 0xe797, 0xe9c0, 0xebec, 0xee1b,
+ 0xf04d, 0xf282, 0xf4ba, 0xf6f5, 0xf933, 0xfb74, 0xfdb8, 0xffff,
+};
+
+/**
+ * Table mapping formula:
+ * f(x) = x < 0.0031308 ? x*12.92 : 1.055*x^(1/2.4)-0.055 (sRGB OETF)
+ * Where x is the normalized index in the table and f(x) the value in the table.
+ * f(x) is remapped to [0;0xff] and rounded.
+ *
+ * Since a 16-bit table is too large, we reduce its precision to 9-bit.
+ */
+static const uint8_t linear2srgb[P + 1] = {
+ 0x00, 0x06, 0x0d, 0x12, 0x16, 0x19, 0x1c, 0x1f, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30,
+ 0x32, 0x33, 0x35, 0x36, 0x38, 0x39, 0x3b, 0x3c, 0x3d, 0x3e, 0x40, 0x41, 0x42, 0x43, 0x45, 0x46,
+ 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56,
+ 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x5f, 0x60, 0x61, 0x62, 0x62,
+ 0x63, 0x64, 0x65, 0x65, 0x66, 0x67, 0x67, 0x68, 0x69, 0x6a, 0x6a, 0x6b, 0x6c, 0x6c, 0x6d, 0x6e,
+ 0x6e, 0x6f, 0x6f, 0x70, 0x71, 0x71, 0x72, 0x73, 0x73, 0x74, 0x74, 0x75, 0x76, 0x76, 0x77, 0x77,
+ 0x78, 0x79, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0x7f, 0x7f, 0x80, 0x80,
+ 0x81, 0x81, 0x82, 0x82, 0x83, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, 0x87, 0x87, 0x88, 0x88, 0x89,
+ 0x89, 0x8a, 0x8a, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d, 0x8e, 0x8e, 0x8f, 0x8f, 0x90, 0x90,
+ 0x91, 0x91, 0x92, 0x92, 0x93, 0x93, 0x93, 0x94, 0x94, 0x95, 0x95, 0x96, 0x96, 0x97, 0x97, 0x97,
+ 0x98, 0x98, 0x99, 0x99, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9e, 0x9e,
+ 0x9f, 0x9f, 0x9f, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5,
+ 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa7, 0xa7, 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xab,
+ 0xab, 0xab, 0xac, 0xac, 0xac, 0xad, 0xad, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0,
+ 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb6, 0xb6,
+ 0xb6, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb,
+ 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc1,
+ 0xc1, 0xc1, 0xc1, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
+ 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf,
+ 0xcf, 0xd0, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, 0xd4,
+ 0xd4, 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, 0xd6, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8,
+ 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1,
+ 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5,
+ 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9,
+ 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xed,
+ 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0,
+ 0xf1, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4,
+ 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8,
+ 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb,
+ 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff,
+};
+
+int32_t ff_srgb_u8_to_linear_int(uint8_t x)
+{
+ return (int32_t)srgb2linear[x];
+}
+
+uint8_t ff_linear_int_to_srgb_u8(int32_t x)
+{
+ if (x <= 0) {
+ return 0;
+ } else if (x >= K) {
+ return 0xff;
+ } else {
+ const int32_t xP = x * P;
+ const int32_t i = xP / K;
+ const int32_t m = xP % K;
+ const int32_t y0 = linear2srgb[i];
+ const int32_t y1 = linear2srgb[i + 1];
+ return (m * (y1 - y0) + K/2) / K + y0;
+ }
+}
+
+/* Integer cube root, working only within [0;1] */
+static int32_t cbrt01_int(int32_t x)
+{
+ int64_t u;
+
+ /* Approximation curve is for the [0;1] range */
+ if (x <= 0) return 0;
+ if (x >= K) return K;
+
+ /*
+ * Initial approximation: x³ - 2.19893x² + 2.01593x + 0.219407
+ *
+ * We are not using any rounding here since the precision is not important
+ * at this stage and it would require the more expensive rounding function
+ * that deals with negative numbers.
+ */
+ u = x*(x*(x + -144107LL) / K + 132114LL) / K + 14379LL;
+
+ /*
+ * Refine with 2 Halley iterations:
+ * uₙ₊₁ = uₙ-2f(uₙ)f'(uₙ)/(2f'(uₙ)²-f(uₙ)f"(uₙ))
+ * = uₙ(2x+uₙ³)/(x+2uₙ³)
+ *
+ * Note: u is not expected to be < 0, so we can use the (a+b/2)/b rounding.
+ */
+ for (int i = 0; i < 2; i++) {
+ const int64_t u3 = u*u*u;
+ const int64_t den = x + (2*u3 + K2/2) / K2;
+ u = (u * (2*x + (u3 + K2/2) / K2) + den/2) / den;
+ }
+
+ return u;
+}
+
+static int64_t div_round64(int64_t a, int64_t b) { return (a^b)<0 ? (a-b/2)/b : (a+b/2)/b; }
+
+struct Lab ff_srgb_u8_to_oklab_int(uint32_t srgb)
+{
+ const int32_t r = (int32_t)srgb2linear[srgb >> 16 & 0xff];
+ const int32_t g = (int32_t)srgb2linear[srgb >> 8 & 0xff];
+ const int32_t b = (int32_t)srgb2linear[srgb & 0xff];
+
+ // Note: lms can actually be slightly over K due to rounded coefficients
+ const int32_t l = (27015LL*r + 35149LL*g + 3372LL*b + K/2) / K;
+ const int32_t m = (13887LL*r + 44610LL*g + 7038LL*b + K/2) / K;
+ const int32_t s = ( 5787LL*r + 18462LL*g + 41286LL*b + K/2) / K;
+
+ const int32_t l_ = cbrt01_int(l);
+ const int32_t m_ = cbrt01_int(m);
+ const int32_t s_ = cbrt01_int(s);
+
+ const struct Lab ret = {
+ .L = div_round64( 13792LL*l_ + 52010LL*m_ - 267LL*s_, K),
+ .a = div_round64(129628LL*l_ - 159158LL*m_ + 29530LL*s_, K),
+ .b = div_round64( 1698LL*l_ + 51299LL*m_ - 52997LL*s_, K),
+ };
+
+ return ret;
+}
+
+uint32_t ff_oklab_int_to_srgb_u8(struct Lab c)
+{
+ const int64_t l_ = c.L + div_round64(25974LL * c.a, K) + div_round64(14143LL * c.b, K);
+ const int64_t m_ = c.L + div_round64(-6918LL * c.a, K) + div_round64(-4185LL * c.b, K);
+ const int64_t s_ = c.L + div_round64(-5864LL * c.a, K) + div_round64(-84638LL * c.b, K);
+
+ const int32_t l = l_*l_*l_ / K2;
+ const int32_t m = m_*m_*m_ / K2;
+ const int32_t s = s_*s_*s_ / K2;
+
+ const uint8_t r = ff_linear_int_to_srgb_u8((267169LL * l + -216771LL * m + 15137LL * s + K/2) / K);
+ const uint8_t g = ff_linear_int_to_srgb_u8((-83127LL * l + 171030LL * m + -22368LL * s + K/2) / K);
+ const uint8_t b = ff_linear_int_to_srgb_u8((-275LL * l + -46099LL * m + 111909LL * s + K/2) / K);
+
+ return r<<16 | g<<8 | b;
+}
diff --git a/libavfilter/palette.h b/libavfilter/palette.h
new file mode 100644
index 0000000000..6839bf6fc6
--- /dev/null
+++ b/libavfilter/palette.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020 Björn Ottosson
+ * Copyright (c) 2022 Clément Bœsch <u pkh me>
+ *
+ * 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
+ */
+
+#ifndef AVFILTER_PALETTE_H
+#define AVFILTER_PALETTE_H
+
+#include <math.h>
+#include <stdint.h>
+
+#include "libavutil/attributes.h"
+
+struct Lab {
+ int32_t L, a, b;
+};
+
+/**
+ * Map sRGB 8-bit color component to a 16-bit linear value (gamma
+ * expand from electrical to optical value).
+ */
+int32_t ff_srgb_u8_to_linear_int(uint8_t x);
+
+/**
+ * Map a 16-bit linear value to a sRGB 8-bit color component (gamma
+ * compressed from optical to electrical value).
+ */
+uint8_t ff_linear_int_to_srgb_u8(int32_t x);
+
+/**
+ * sRGB (non-linear) to OkLab conversion
+ * @see https://bottosson.github.io/posts/oklab/
+ */
+struct Lab ff_srgb_u8_to_oklab_int(uint32_t srgb);
+
+/**
+ * OkLab to sRGB (non-linear) conversion
+ * @see https://bottosson.github.io/posts/oklab/
+ */
+uint32_t ff_oklab_int_to_srgb_u8(struct Lab c);
+
+#endif /* AVFILTER_PALETTE_H */
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 05/32] avfilter/paletteuse: switch from u8[4] to u32 for color code
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (3 preceding siblings ...)
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 04/32] avfilter/palette{gen, use}: add palette utils Clément Bœsch
@ 2022-12-27 23:17 ` Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 06/32] avfilter/paletteuse: name target color arg consistently in colormap functions Clément Bœsch
` (27 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:17 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
This change simplifies the code quite a bit and make it consistent with
how it's done in palettegen.
---
libavfilter/vf_paletteuse.c | 98 ++++++++++++++++---------------------
1 file changed, 41 insertions(+), 57 deletions(-)
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index f9d8a1cdfc..fb4016b11c 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -57,7 +57,7 @@ enum diff_mode {
};
struct color_node {
- uint8_t val[4];
+ uint32_t val;
uint8_t palette_id;
int split;
int left_id, right_id;
@@ -162,9 +162,11 @@ 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 int trans_thresh)
+static av_always_inline int diff(const uint32_t a, const uint32_t b, const int trans_thresh)
{
// XXX: try L*a*b with CIE76 (dL*dL + da*da + db*db)
+ const uint8_t c1[] = {a >> 24, a >> 16 & 0xff, a >> 8 & 0xff, a & 0xff};
+ const uint8_t c2[] = {b >> 24, b >> 16 & 0xff, b >> 8 & 0xff, b & 0xff};
const int dr = c1[1] - c2[1];
const int dg = c1[2] - c2[2];
const int db = c1[3] - c2[3];
@@ -178,7 +180,7 @@ static av_always_inline int diff(const uint8_t *c1, const uint8_t *c2, const int
}
}
-static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *palette, const uint8_t *argb, const int trans_thresh)
+static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *palette, const uint32_t argb, const int trans_thresh)
{
int i, pal_id = -1, min_dist = INT_MAX;
@@ -186,13 +188,7 @@ static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *pale
const uint32_t c = palette[i];
if (c >> 24 >= trans_thresh) { // ignore transparent entry
- const uint8_t palargb[] = {
- palette[i]>>24,
- palette[i]>>16 & 0xff,
- palette[i]>> 8 & 0xff,
- palette[i] & 0xff,
- };
- const int d = diff(palargb, argb, trans_thresh);
+ const int d = diff(palette[i], argb, trans_thresh);
if (d < min_dist) {
pal_id = i;
min_dist = d;
@@ -210,14 +206,14 @@ struct nearest_color {
static void colormap_nearest_node(const struct color_node *map,
const int node_pos,
- const uint8_t *target,
+ const uint32_t target,
const int trans_thresh,
struct nearest_color *nearest)
{
const struct color_node *kd = map + node_pos;
- const int s = kd->split;
+ const int shift = (3 - kd->split) * 8;
int dx, nearer_kd_id, further_kd_id;
- const uint8_t *current = kd->val;
+ const uint32_t current = kd->val;
const int current_to_target = diff(target, current, trans_thresh);
if (current_to_target < nearest->dist_sqd) {
@@ -226,7 +222,7 @@ static void colormap_nearest_node(const struct color_node *map,
}
if (kd->left_id != -1 || kd->right_id != -1) {
- dx = target[s] - current[s];
+ dx = (int)(target>>shift & 0xff) - (int)(current>>shift & 0xff);
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;
@@ -239,7 +235,7 @@ static void colormap_nearest_node(const struct color_node *map,
}
}
-static av_always_inline uint8_t colormap_nearest_recursive(const struct color_node *node, const uint8_t *rgb, const int trans_thresh)
+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(node, 0, rgb, trans_thresh, &res);
@@ -251,7 +247,7 @@ struct stack_node {
int dx2;
};
-static av_always_inline uint8_t colormap_nearest_iterative(const struct color_node *root, const uint8_t *target, const int trans_thresh)
+static av_always_inline uint8_t colormap_nearest_iterative(const struct color_node *root, const uint32_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];
@@ -260,7 +256,7 @@ static av_always_inline uint8_t colormap_nearest_iterative(const struct color_no
for (;;) {
const struct color_node *kd = &root[cur_color_id];
- const uint8_t *current = kd->val;
+ const uint32_t current = kd->val;
const int current_to_target = diff(target, current, trans_thresh);
/* Compare current color node to the target and update our best node if
@@ -274,8 +270,8 @@ static av_always_inline uint8_t colormap_nearest_iterative(const struct color_no
/* Check if it's not a leaf */
if (kd->left_id != -1 || kd->right_id != -1) {
- const int split = kd->split;
- const int dx = target[split] - current[split];
+ const int shift = (3 - kd->split) * 8;
+ const int dx = (target>>shift & 0xff) - (current>>shift & 0xff);
int nearer_kd_id, further_kd_id;
/* Define which side is the most interesting. */
@@ -331,24 +327,20 @@ end:
/**
* Check if the requested color is in the cache already. If not, find it in the
* color tree and cache it.
- * Note: a, r, g, and b are the components of color, but are passed as well to avoid
- * recomputing them (they are generally computed by the caller for other uses).
*/
static av_always_inline int color_get(PaletteUseContext *s, uint32_t color,
- uint8_t a, uint8_t r, uint8_t g, uint8_t b,
const enum color_search_method search_method)
{
int i;
- const uint8_t argb_elts[] = {a, r, g, b};
- const uint8_t rhash = r & ((1<<NBITS)-1);
- const uint8_t ghash = g & ((1<<NBITS)-1);
- const uint8_t bhash = b & ((1<<NBITS)-1);
+ const uint8_t rhash = (color>>16) & ((1<<NBITS)-1);
+ const uint8_t ghash = (color>> 8) & ((1<<NBITS)-1);
+ const uint8_t bhash = color & ((1<<NBITS)-1);
const unsigned hash = rhash<<(NBITS*2) | ghash<<NBITS | bhash;
struct cache_node *node = &s->cache[hash];
struct cached_color *e;
// first, check for transparency
- if (a < s->trans_thresh && s->transparency_index >= 0) {
+ if (color>>24 < s->trans_thresh && s->transparency_index >= 0) {
return s->transparency_index;
}
@@ -363,7 +355,7 @@ 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(search_method, s->palette, s->map, argb_elts, s->trans_thresh);
+ e->pal_entry = COLORMAP_NEAREST(search_method, s->palette, s->map, color, s->trans_thresh);
return e->pal_entry;
}
@@ -372,12 +364,11 @@ static av_always_inline int get_dst_color_err(PaletteUseContext *s,
uint32_t c, int *er, int *eg, int *eb,
const enum color_search_method search_method)
{
- const uint8_t a = c >> 24;
const uint8_t r = c >> 16 & 0xff;
const uint8_t g = c >> 8 & 0xff;
const uint8_t b = c & 0xff;
uint32_t dstc;
- const int dstx = color_get(s, c, a, r, g, b, search_method);
+ const int dstx = color_get(s, c, search_method);
if (dstx < 0)
return dstx;
dstc = s->palette[dstx];
@@ -419,7 +410,7 @@ static av_always_inline int set_frame(PaletteUseContext *s, AVFrame *out, AVFram
const uint8_t g = av_clip_uint8(g8 + d);
const uint8_t b = av_clip_uint8(b8 + d);
const uint32_t color_new = (unsigned)(a8) << 24 | r << 16 | g << 8 | b;
- const int color = color_get(s, color_new, a8, r, g, b, search_method);
+ const int color = color_get(s, color_new, search_method);
if (color < 0)
return color;
@@ -483,11 +474,7 @@ static av_always_inline int set_frame(PaletteUseContext *s, AVFrame *out, AVFram
if ( down) src[src_linesize + x ] = dither_color(src[src_linesize + x ], er, eg, eb, 1, 2);
} else {
- const uint8_t a = src[x] >> 24;
- const uint8_t r = src[x] >> 16 & 0xff;
- const uint8_t g = src[x] >> 8 & 0xff;
- const uint8_t b = src[x] & 0xff;
- const int color = color_get(s, src[x], a, r, g, b, search_method);
+ const int color = color_get(s, src[x], search_method);
if (color < 0)
return color;
@@ -507,20 +494,20 @@ static void disp_node(AVBPrint *buf,
int depth)
{
const struct color_node *node = &map[node_id];
- const uint32_t fontcolor = node->val[1] > 0x50 &&
- node->val[2] > 0x50 &&
- node->val[3] > 0x50 ? 0 : 0xffffff;
+ const uint32_t fontcolor = (node->val>>16 & 0xff) > 0x50 &&
+ (node->val>> 8 & 0xff) > 0x50 &&
+ (node->val & 0xff) > 0x50 ? 0 : 0xffffff;
const int rgb_comp = node->split - 1;
av_bprintf(buf, "%*cnode%d ["
"label=\"%c%02X%c%02X%c%02X%c\" "
- "fillcolor=\"#%02x%02x%02x\" "
+ "fillcolor=\"#%06"PRIX32"\" "
"fontcolor=\"#%06"PRIX32"\"]\n",
depth*INDENT, ' ', node->palette_id,
- "[ "[rgb_comp], node->val[1],
- "][ "[rgb_comp], node->val[2],
- " ]["[rgb_comp], node->val[3],
+ "[ "[rgb_comp], node->val>>16 & 0xff,
+ "][ "[rgb_comp], node->val>> 8 & 0xff,
+ " ]["[rgb_comp], node->val & 0xff,
" ]"[rgb_comp],
- node->val[1], node->val[2], node->val[3],
+ node->val & 0xffffff,
fontcolor);
if (parent_id != -1)
av_bprintf(buf, "%*cnode%d -> node%d\n", depth*INDENT, ' ',
@@ -563,16 +550,14 @@ static int debug_accuracy(const struct color_node *node, const uint32_t *palette
for (r = 0; r < 256; r++) {
for (g = 0; g < 256; g++) {
for (b = 0; b < 256; b++) {
- const uint8_t argb[] = {0xff, r, g, b};
+ const uint32_t argb = 0xff000000 | r<<16 | g<<8 | b;
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 = 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);
+ const int d1 = diff(0xff000000 | c1, argb, trans_thresh);
+ const int d2 = diff(0xff000000 | c2, argb, trans_thresh);
if (d1 != d2) {
av_log(NULL, AV_LOG_ERROR,
"/!\\ %02X%02X%02X: %d ! %d (%06"PRIX32" ! %06"PRIX32") / dist: %d ! %d\n",
@@ -686,6 +671,7 @@ static int colormap_insert(struct color_node *map,
{
uint32_t c;
int component, cur_id;
+ uint8_t comp_value;
int node_left_id = -1, node_right_id = -1;
struct color_node *node;
struct color_rect box1, box2;
@@ -700,17 +686,15 @@ static int colormap_insert(struct color_node *map,
node = &map[cur_id];
node->split = component;
node->palette_id = pal_id;
- node->val[0] = c>>24;
- node->val[1] = c>>16 & 0xff;
- node->val[2] = c>> 8 & 0xff;
- node->val[3] = c & 0xff;
+ node->val = c;
color_used[pal_id] = 1;
/* get the two boxes this node creates */
box1 = box2 = *box;
- box1.max[component-1] = node->val[component];
- box2.min[component-1] = FFMIN(node->val[component] + 1, 255);
+ comp_value = node->val >> ((3 - component) * 8) & 0xff;
+ box1.max[component-1] = comp_value;
+ box2.min[component-1] = FFMIN(comp_value + 1, 255);
node_left_id = colormap_insert(map, color_used, nb_used, palette, trans_thresh, &box1);
@@ -787,8 +771,8 @@ static void debug_mean_error(PaletteUseContext *s, const AVFrame *in1,
for (x = 0; x < in1->width; x++) {
const uint32_t c1 = src1[x];
const uint32_t c2 = palette[src2[x]];
- 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};
+ const uint32_t argb1 = 0xff000000 | c1;
+ const uint32_t argb2 = 0xff000000 | c2;
mean_err += diff(argb1, argb2, s->trans_thresh);
}
src1 += src1_linesize;
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 06/32] avfilter/paletteuse: name target color arg consistently in colormap functions
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (4 preceding siblings ...)
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 05/32] avfilter/paletteuse: switch from u8[4] to u32 for color code Clément Bœsch
@ 2022-12-27 23:17 ` Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 07/32] avfilter/paletteuse: remove unused alpha split dimension Clément Bœsch
` (26 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:17 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
---
libavfilter/vf_paletteuse.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index fb4016b11c..f43f077454 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -180,7 +180,7 @@ static av_always_inline int diff(const uint32_t a, const uint32_t b, const int t
}
}
-static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *palette, const uint32_t argb, const int trans_thresh)
+static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *palette, const uint32_t target, const int trans_thresh)
{
int i, pal_id = -1, min_dist = INT_MAX;
@@ -188,7 +188,7 @@ static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *pale
const uint32_t c = palette[i];
if (c >> 24 >= trans_thresh) { // ignore transparent entry
- const int d = diff(palette[i], argb, trans_thresh);
+ const int d = diff(palette[i], target, trans_thresh);
if (d < min_dist) {
pal_id = i;
min_dist = d;
@@ -235,10 +235,10 @@ static void colormap_nearest_node(const struct color_node *map,
}
}
-static av_always_inline uint8_t colormap_nearest_recursive(const struct color_node *node, const uint8_t rgb, const int trans_thresh)
+static av_always_inline uint8_t colormap_nearest_recursive(const struct color_node *node, const uint8_t target, const int trans_thresh)
{
struct nearest_color res = {.dist_sqd = INT_MAX, .node_pos = -1};
- colormap_nearest_node(node, 0, rgb, trans_thresh, &res);
+ colormap_nearest_node(node, 0, target, trans_thresh, &res);
return node[res.node_pos].palette_id;
}
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 07/32] avfilter/paletteuse: remove unused alpha split dimension
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (5 preceding siblings ...)
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 06/32] avfilter/paletteuse: name target color arg consistently in colormap functions Clément Bœsch
@ 2022-12-27 23:17 ` Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 08/32] avfilter/paletteuse: remove redundant alpha condition Clément Bœsch
` (25 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:17 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
The equalities in the w{r,g,b} range checks make sure longest is never
0. Even if the alpha ended up being selected in get_next_color() it
would cause underread memory accesses in its caller (colormap_insert).
---
libavfilter/vf_paletteuse.c | 33 ++++++++++++++++-----------------
1 file changed, 16 insertions(+), 17 deletions(-)
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index f43f077454..8954a02524 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -211,7 +211,7 @@ static void colormap_nearest_node(const struct color_node *map,
struct nearest_color *nearest)
{
const struct color_node *kd = map + node_pos;
- const int shift = (3 - kd->split) * 8;
+ const int shift = (2 - kd->split) * 8;
int dx, nearer_kd_id, further_kd_id;
const uint32_t current = kd->val;
const int current_to_target = diff(target, current, trans_thresh);
@@ -270,7 +270,7 @@ static av_always_inline uint8_t colormap_nearest_iterative(const struct color_no
/* Check if it's not a leaf */
if (kd->left_id != -1 || kd->right_id != -1) {
- const int shift = (3 - kd->split) * 8;
+ const int shift = (2 - kd->split) * 8;
const int dx = (target>>shift & 0xff) - (current>>shift & 0xff);
int nearer_kd_id, further_kd_id;
@@ -497,7 +497,7 @@ static void disp_node(AVBPrint *buf,
const uint32_t fontcolor = (node->val>>16 & 0xff) > 0x50 &&
(node->val>> 8 & 0xff) > 0x50 &&
(node->val & 0xff) > 0x50 ? 0 : 0xffffff;
- const int rgb_comp = node->split - 1;
+ const int rgb_comp = node->split;
av_bprintf(buf, "%*cnode%d ["
"label=\"%c%02X%c%02X%c%02X%c\" "
"fillcolor=\"#%06"PRIX32"\" "
@@ -588,16 +588,15 @@ static int cmp_##name(const void *pa, const void *pb) \
{ \
const struct color *a = pa; \
const struct color *b = pb; \
- return (int)(a->value >> (8 * (3 - (pos))) & 0xff) \
- - (int)(b->value >> (8 * (3 - (pos))) & 0xff); \
+ return (int)(a->value >> (8 * (2 - (pos))) & 0xff) \
+ - (int)(b->value >> (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};
static int get_next_color(const uint8_t *color_used, const uint32_t *palette,
const int trans_thresh,
@@ -650,9 +649,9 @@ static int get_next_color(const uint8_t *color_used, const uint32_t *palette,
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;
+ if (wr >= wg && wr >= wb) longest = 0;
+ if (wg >= wr && wg >= wb) longest = 1;
+ if (wb >= wr && wb >= wg) longest = 2;
cmpf = cmp_funcs[longest];
*component = longest;
@@ -692,13 +691,13 @@ static int colormap_insert(struct color_node *map,
/* get the two boxes this node creates */
box1 = box2 = *box;
- comp_value = node->val >> ((3 - component) * 8) & 0xff;
- box1.max[component-1] = comp_value;
- box2.min[component-1] = FFMIN(comp_value + 1, 255);
+ comp_value = node->val >> ((2 - component) * 8) & 0xff;
+ box1.max[component] = comp_value;
+ box2.min[component] = FFMIN(comp_value + 1, 255);
node_left_id = colormap_insert(map, color_used, nb_used, palette, trans_thresh, &box1);
- if (box2.min[component-1] <= box2.max[component-1])
+ if (box2.min[component] <= box2.max[component])
node_right_id = colormap_insert(map, color_used, nb_used, palette, trans_thresh, &box2);
node->left_id = node_left_id;
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 08/32] avfilter/paletteuse: remove redundant alpha condition
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (6 preceding siblings ...)
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 07/32] avfilter/paletteuse: remove unused alpha split dimension Clément Bœsch
@ 2022-12-27 23:17 ` Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 09/32] avfilter/paletteuse: switch to a perceptual model Clément Bœsch
` (24 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:17 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
This is redundant with a != 0xff below.
---
libavfilter/vf_paletteuse.c | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index 8954a02524..0861a70a0b 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -599,7 +599,6 @@ DECLARE_CMP_FUNC(b, 2)
static const cmp_func cmp_funcs[] = {cmp_r, cmp_g, cmp_b};
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 wr, wg, wb;
@@ -619,10 +618,6 @@ static int get_next_color(const uint8_t *color_used, const uint32_t *palette,
const uint8_t g = c >> 8 & 0xff;
const uint8_t b = c & 0xff;
- if (a < trans_thresh) {
- continue;
- }
-
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])
@@ -674,7 +669,7 @@ 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, palette, trans_thresh, &component, box);
+ const int pal_id = get_next_color(color_used, palette, &component, box);
if (pal_id < 0)
return -1;
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 09/32] avfilter/paletteuse: switch to a perceptual model
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (7 preceding siblings ...)
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 08/32] avfilter/paletteuse: remove redundant alpha condition Clément Bœsch
@ 2022-12-27 23:17 ` Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 10/32] avfilter/palettegen: move box stats computation to a dedicated function Clément Bœsch
` (23 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:17 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
Now the selection of the color is based on a distance built around human
perception of color instead of the unreliable sRGB triplet one.
---
libavfilter/Makefile | 2 +-
libavfilter/vf_paletteuse.c | 181 ++++++++++----------
tests/ref/fate/filter-paletteuse-bayer | 142 +++++++--------
tests/ref/fate/filter-paletteuse-bayer0 | 142 +++++++--------
tests/ref/fate/filter-paletteuse-nodither | 142 +++++++--------
tests/ref/fate/filter-paletteuse-sierra2_4a | 142 +++++++--------
6 files changed, 378 insertions(+), 373 deletions(-)
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index cb41ccc622..c3d13e5a26 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -404,7 +404,7 @@ OBJS-$(CONFIG_OWDENOISE_FILTER) += vf_owdenoise.o
OBJS-$(CONFIG_PAD_FILTER) += vf_pad.o
OBJS-$(CONFIG_PAD_OPENCL_FILTER) += vf_pad_opencl.o opencl.o opencl/pad.o
OBJS-$(CONFIG_PALETTEGEN_FILTER) += vf_palettegen.o
-OBJS-$(CONFIG_PALETTEUSE_FILTER) += vf_paletteuse.o framesync.o
+OBJS-$(CONFIG_PALETTEUSE_FILTER) += vf_paletteuse.o framesync.o palette.o
OBJS-$(CONFIG_PERMS_FILTER) += f_perms.o
OBJS-$(CONFIG_PERSPECTIVE_FILTER) += vf_perspective.o
OBJS-$(CONFIG_PHASE_FILTER) += vf_phase.o
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index 0861a70a0b..3af121b1eb 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -32,6 +32,7 @@
#include "filters.h"
#include "framesync.h"
#include "internal.h"
+#include "palette.h"
enum dithering_mode {
DITHERING_NONE,
@@ -56,8 +57,13 @@ enum diff_mode {
NB_DIFF_MODE
};
+struct color_info {
+ uint32_t srgb;
+ int32_t lab[3];
+};
+
struct color_node {
- uint32_t val;
+ struct color_info c;
uint8_t palette_id;
int split;
int left_id, right_id;
@@ -162,25 +168,32 @@ 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 uint32_t a, const uint32_t b, const int trans_thresh)
+static av_always_inline int diff(const struct color_info *a, const struct color_info *b, const int trans_thresh)
{
- // XXX: try L*a*b with CIE76 (dL*dL + da*da + db*db)
- const uint8_t c1[] = {a >> 24, a >> 16 & 0xff, a >> 8 & 0xff, a & 0xff};
- const uint8_t c2[] = {b >> 24, b >> 16 & 0xff, b >> 8 & 0xff, b & 0xff};
- const int dr = c1[1] - c2[1];
- const int dg = c1[2] - c2[2];
- const int db = c1[3] - c2[3];
-
- if (c1[0] < trans_thresh && c2[0] < trans_thresh) {
+ const uint8_t alpha_a = a->srgb >> 24;
+ const uint8_t alpha_b = b->srgb >> 24;
+
+ if (alpha_a < trans_thresh && alpha_b < trans_thresh) {
return 0;
- } else if (c1[0] >= trans_thresh && c2[0] >= trans_thresh) {
- return dr*dr + dg*dg + db*db;
+ } else if (alpha_a >= trans_thresh && alpha_b >= trans_thresh) {
+ const int64_t dL = a->lab[0] - b->lab[0];
+ const int64_t da = a->lab[1] - b->lab[1];
+ const int64_t db = a->lab[2] - b->lab[2];
+ const int64_t ret = dL*dL + da*da + db*db;
+ return FFMIN(ret, INT32_MAX - 1);
} else {
- return 255*255 + 255*255 + 255*255;
+ return INT32_MAX - 1;
}
}
-static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *palette, const uint32_t target, const int trans_thresh)
+static struct color_info get_color_from_srgb(uint32_t srgb)
+{
+ const struct Lab lab = ff_srgb_u8_to_oklab_int(srgb);
+ struct color_info ret = {.srgb=srgb, .lab={lab.L, lab.a, lab.b}};
+ return ret;
+}
+
+static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *palette, const struct color_info *target, const int trans_thresh)
{
int i, pal_id = -1, min_dist = INT_MAX;
@@ -188,7 +201,8 @@ static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *pale
const uint32_t c = palette[i];
if (c >> 24 >= trans_thresh) { // ignore transparent entry
- const int d = diff(palette[i], target, trans_thresh);
+ const struct color_info pal_color = get_color_from_srgb(palette[i]);
+ const int d = diff(&pal_color, target, trans_thresh);
if (d < min_dist) {
pal_id = i;
min_dist = d;
@@ -206,14 +220,13 @@ struct nearest_color {
static void colormap_nearest_node(const struct color_node *map,
const int node_pos,
- const uint32_t target,
+ const struct color_info *target,
const int trans_thresh,
struct nearest_color *nearest)
{
const struct color_node *kd = map + node_pos;
- const int shift = (2 - kd->split) * 8;
- int dx, nearer_kd_id, further_kd_id;
- const uint32_t current = kd->val;
+ int nearer_kd_id, further_kd_id;
+ const struct color_info *current = &kd->c;
const int current_to_target = diff(target, current, trans_thresh);
if (current_to_target < nearest->dist_sqd) {
@@ -222,7 +235,7 @@ static void colormap_nearest_node(const struct color_node *map,
}
if (kd->left_id != -1 || kd->right_id != -1) {
- dx = (int)(target>>shift & 0xff) - (int)(current>>shift & 0xff);
+ const int dx = target->lab[kd->split] - current->lab[kd->split];
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;
@@ -235,9 +248,9 @@ static void colormap_nearest_node(const struct color_node *map,
}
}
-static av_always_inline uint8_t colormap_nearest_recursive(const struct color_node *node, const uint8_t target, const int trans_thresh)
+static av_always_inline uint8_t colormap_nearest_recursive(const struct color_node *node, const struct color_info *target, const int trans_thresh)
{
- struct nearest_color res = {.dist_sqd = INT_MAX, .node_pos = -1};
+ struct nearest_color res = {.dist_sqd = INT32_MAX, .node_pos = -1};
colormap_nearest_node(node, 0, target, trans_thresh, &res);
return node[res.node_pos].palette_id;
}
@@ -247,7 +260,7 @@ struct stack_node {
int dx2;
};
-static av_always_inline uint8_t colormap_nearest_iterative(const struct color_node *root, const uint32_t target, const int trans_thresh)
+static av_always_inline uint8_t colormap_nearest_iterative(const struct color_node *root, const struct color_info *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];
@@ -256,7 +269,7 @@ static av_always_inline uint8_t colormap_nearest_iterative(const struct color_no
for (;;) {
const struct color_node *kd = &root[cur_color_id];
- const uint32_t current = kd->val;
+ const struct color_info *current = &kd->c;
const int current_to_target = diff(target, current, trans_thresh);
/* Compare current color node to the target and update our best node if
@@ -270,8 +283,7 @@ static av_always_inline uint8_t colormap_nearest_iterative(const struct color_no
/* Check if it's not a leaf */
if (kd->left_id != -1 || kd->right_id != -1) {
- const int shift = (2 - kd->split) * 8;
- const int dx = (target>>shift & 0xff) - (current>>shift & 0xff);
+ const int dx = target->lab[kd->split] - current->lab[kd->split];
int nearer_kd_id, further_kd_id;
/* Define which side is the most interesting. */
@@ -332,6 +344,7 @@ static av_always_inline int color_get(PaletteUseContext *s, uint32_t color,
const enum color_search_method search_method)
{
int i;
+ struct color_info clrinfo;
const uint8_t rhash = (color>>16) & ((1<<NBITS)-1);
const uint8_t ghash = (color>> 8) & ((1<<NBITS)-1);
const uint8_t bhash = color & ((1<<NBITS)-1);
@@ -355,7 +368,8 @@ 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(search_method, s->palette, s->map, color, s->trans_thresh);
+ clrinfo = get_color_from_srgb(color);
+ e->pal_entry = COLORMAP_NEAREST(search_method, s->palette, s->map, &clrinfo, s->trans_thresh);
return e->pal_entry;
}
@@ -494,20 +508,18 @@ static void disp_node(AVBPrint *buf,
int depth)
{
const struct color_node *node = &map[node_id];
- const uint32_t fontcolor = (node->val>>16 & 0xff) > 0x50 &&
- (node->val>> 8 & 0xff) > 0x50 &&
- (node->val & 0xff) > 0x50 ? 0 : 0xffffff;
- const int rgb_comp = node->split;
+ const uint32_t fontcolor = node->c.lab[0] > 0x7fff ? 0 : 0xffffff;
+ const int lab_comp = node->split;
av_bprintf(buf, "%*cnode%d ["
- "label=\"%c%02X%c%02X%c%02X%c\" "
+ "label=\"%c%d%c%d%c%d%c\" "
"fillcolor=\"#%06"PRIX32"\" "
"fontcolor=\"#%06"PRIX32"\"]\n",
depth*INDENT, ' ', node->palette_id,
- "[ "[rgb_comp], node->val>>16 & 0xff,
- "][ "[rgb_comp], node->val>> 8 & 0xff,
- " ]["[rgb_comp], node->val & 0xff,
- " ]"[rgb_comp],
- node->val & 0xffffff,
+ "[ "[lab_comp], node->c.lab[0],
+ "][ "[lab_comp], node->c.lab[1],
+ " ]["[lab_comp], node->c.lab[2],
+ " ]"[lab_comp],
+ node->c.srgb & 0xffffff,
fontcolor);
if (parent_id != -1)
av_bprintf(buf, "%*cnode%d -> node%d\n", depth*INDENT, ' ',
@@ -550,18 +562,18 @@ static int debug_accuracy(const struct color_node *node, const uint32_t *palette
for (r = 0; r < 256; r++) {
for (g = 0; g < 256; g++) {
for (b = 0; b < 256; b++) {
- const uint32_t argb = 0xff000000 | r<<16 | g<<8 | b;
- const int r1 = COLORMAP_NEAREST(search_method, palette, node, argb, trans_thresh);
- const int r2 = colormap_nearest_bruteforce(palette, argb, trans_thresh);
+ const struct color_info target = get_color_from_srgb(0xff000000 | r<<16 | g<<8 | b);
+ const int r1 = COLORMAP_NEAREST(search_method, palette, node, &target, trans_thresh);
+ const int r2 = colormap_nearest_bruteforce(palette, &target, trans_thresh);
if (r1 != r2) {
- const uint32_t c1 = palette[r1];
- const uint32_t c2 = palette[r2];
- const int d1 = diff(0xff000000 | c1, argb, trans_thresh);
- const int d2 = diff(0xff000000 | c2, argb, trans_thresh);
+ const struct color_info pal_c1 = get_color_from_srgb(0xff000000 | palette[r1]);
+ const struct color_info pal_c2 = get_color_from_srgb(0xff000000 | palette[r2]);
+ const int d1 = diff(&pal_c1, &target, trans_thresh);
+ const int d2 = diff(&pal_c2, &target, trans_thresh);
if (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);
+ r, g, b, r1, r2, pal_c1.srgb & 0xffffff, pal_c2.srgb & 0xffffff, d1, d2);
ret = 1;
}
}
@@ -572,66 +584,63 @@ static int debug_accuracy(const struct color_node *node, const uint32_t *palette
}
struct color {
- uint32_t value;
+ struct Lab value;
uint8_t pal_id;
};
struct color_rect {
- uint8_t min[3];
- uint8_t max[3];
+ int min[3];
+ int max[3];
};
typedef int (*cmp_func)(const void *, const void *);
-#define DECLARE_CMP_FUNC(name, pos) \
+#define DECLARE_CMP_FUNC(name) \
static int cmp_##name(const void *pa, const void *pb) \
{ \
const struct color *a = pa; \
const struct color *b = pb; \
- return (int)(a->value >> (8 * (2 - (pos))) & 0xff) \
- - (int)(b->value >> (8 * (2 - (pos))) & 0xff); \
+ return FFDIFFSIGN(a->value.name, b->value.name); \
}
-DECLARE_CMP_FUNC(r, 0)
-DECLARE_CMP_FUNC(g, 1)
-DECLARE_CMP_FUNC(b, 2)
+DECLARE_CMP_FUNC(L)
+DECLARE_CMP_FUNC(a)
+DECLARE_CMP_FUNC(b)
-static const cmp_func cmp_funcs[] = {cmp_r, cmp_g, cmp_b};
+static const cmp_func cmp_funcs[] = {cmp_L, cmp_a, cmp_b};
static int get_next_color(const uint8_t *color_used, const uint32_t *palette,
int *component, const struct color_rect *box)
{
- int wr, wg, wb;
+ int wL, wa, 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] = 0xff;
- ranges.max[0] = ranges.max[1] = ranges.max[2] = 0x00;
+ ranges.min[0] = ranges.min[1] = ranges.min[2] = 0xffff;
+ ranges.max[0] = ranges.max[1] = ranges.max[2] = -0xffff;
for (i = 0; i < AVPALETTE_COUNT; i++) {
const uint32_t c = palette[i];
const uint8_t a = c >> 24;
- const uint8_t r = c >> 16 & 0xff;
- const uint8_t g = c >> 8 & 0xff;
- const uint8_t b = c & 0xff;
+ const struct Lab lab = ff_srgb_u8_to_oklab_int(c);
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])
+ lab.L < box->min[0] || lab.a < box->min[1] || lab.b < box->min[2] ||
+ lab.L > box->max[0] || lab.a > box->max[1] || lab.b > box->max[2])
continue;
- 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 (lab.L < ranges.min[0]) ranges.min[0] = lab.L;
+ if (lab.a < ranges.min[1]) ranges.min[1] = lab.a;
+ if (lab.b < ranges.min[2]) ranges.min[2] = lab.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;
+ if (lab.L > ranges.max[0]) ranges.max[0] = lab.L;
+ if (lab.a > ranges.max[1]) ranges.max[1] = lab.a;
+ if (lab.b > ranges.max[2]) ranges.max[2] = lab.b;
- tmp_pal[nb_color].value = c;
+ tmp_pal[nb_color].value = lab;
tmp_pal[nb_color].pal_id = i;
nb_color++;
@@ -641,12 +650,12 @@ static int get_next_color(const uint8_t *color_used, const uint32_t *palette,
return -1;
/* define longest axis that will be the split component */
- wr = ranges.max[0] - ranges.min[0];
- wg = ranges.max[1] - ranges.min[1];
+ wL = ranges.max[0] - ranges.min[0];
+ wa = ranges.max[1] - ranges.min[1];
wb = ranges.max[2] - ranges.min[2];
- if (wr >= wg && wr >= wb) longest = 0;
- if (wg >= wr && wg >= wb) longest = 1;
- if (wb >= wr && wb >= wg) longest = 2;
+ if (wb >= wL && wb >= wa) longest = 2;
+ if (wa >= wL && wa >= wb) longest = 1;
+ if (wL >= wa && wL >= wb) longest = 0;
cmpf = cmp_funcs[longest];
*component = longest;
@@ -663,9 +672,8 @@ static int colormap_insert(struct color_node *map,
const int trans_thresh,
const struct color_rect *box)
{
- uint32_t c;
int component, cur_id;
- uint8_t comp_value;
+ int comp_value;
int node_left_id = -1, node_right_id = -1;
struct color_node *node;
struct color_rect box1, box2;
@@ -676,19 +684,18 @@ static int colormap_insert(struct color_node *map,
/* create new node with that color */
cur_id = (*nb_used)++;
- c = palette[pal_id];
node = &map[cur_id];
node->split = component;
node->palette_id = pal_id;
- node->val = c;
+ node->c = get_color_from_srgb(palette[pal_id]);
color_used[pal_id] = 1;
/* get the two boxes this node creates */
box1 = box2 = *box;
- comp_value = node->val >> ((2 - component) * 8) & 0xff;
+ comp_value = node->c.lab[component];
box1.max[component] = comp_value;
- box2.min[component] = FFMIN(comp_value + 1, 255);
+ box2.min[component] = FFMIN(comp_value + 1, 0xffff);
node_left_id = colormap_insert(map, color_used, nb_used, palette, trans_thresh, &box1);
@@ -735,8 +742,8 @@ static void load_colormap(PaletteUseContext *s)
}
}
- box.min[0] = box.min[1] = box.min[2] = 0x00;
- box.max[0] = box.max[1] = box.max[2] = 0xff;
+ box.min[0] = box.min[1] = box.min[2] = -0xffff;
+ box.max[0] = box.max[1] = box.max[2] = 0xffff;
colormap_insert(s->map, color_used, &nb_used, s->palette, s->trans_thresh, &box);
@@ -763,11 +770,9 @@ static void debug_mean_error(PaletteUseContext *s, const AVFrame *in1,
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 uint32_t argb1 = 0xff000000 | c1;
- const uint32_t argb2 = 0xff000000 | c2;
- mean_err += diff(argb1, argb2, s->trans_thresh);
+ const struct color_info c1 = get_color_from_srgb(0xff000000 | src1[x]);
+ const struct color_info c2 = get_color_from_srgb(0xff000000 | palette[src2[x]]);
+ mean_err += diff(&c1, &c2, s->trans_thresh);
}
src1 += src1_linesize;
src2 += src2_linesize;
diff --git a/tests/ref/fate/filter-paletteuse-bayer b/tests/ref/fate/filter-paletteuse-bayer
index 5ca0115053..fa1778a1bb 100644
--- a/tests/ref/fate/filter-paletteuse-bayer
+++ b/tests/ref/fate/filter-paletteuse-bayer
@@ -3,74 +3,74 @@
#codec_id 0: rawvideo
#dimensions 0: 320x180
#sar 0: 1/1
-0, 0, 0, 1, 230400, 0x7b259d08
-0, 1, 1, 1, 230400, 0xf04095e0
-0, 2, 2, 1, 230400, 0x84d49cd5
-0, 3, 3, 1, 230400, 0xd7a29aaf
-0, 4, 4, 1, 230400, 0x9047947c
-0, 5, 5, 1, 230400, 0xfeb990e7
-0, 6, 6, 1, 230400, 0x51ee9295
-0, 7, 7, 1, 230400, 0x66fd4833
-0, 8, 8, 1, 230400, 0x4c0948f0
-0, 9, 9, 1, 230400, 0x632b4776
-0, 10, 10, 1, 230400, 0x7a3c87e2
-0, 11, 11, 1, 230400, 0x4a9286ba
-0, 12, 12, 1, 230400, 0x54dc8649
-0, 13, 13, 1, 230400, 0x92628944
-0, 14, 14, 1, 230400, 0x80f9899f
-0, 15, 15, 1, 230400, 0x5cd78bd8
-0, 16, 16, 1, 230400, 0x4b4ca390
-0, 17, 17, 1, 230400, 0x82cca153
-0, 18, 18, 1, 230400, 0x65f1a2d0
-0, 19, 19, 1, 230400, 0x7df6ae4c
-0, 20, 20, 1, 230400, 0x909baccc
-0, 21, 21, 1, 230400, 0x1892ac65
-0, 22, 22, 1, 230400, 0x3247bb32
-0, 23, 23, 1, 230400, 0x592fbbe5
-0, 24, 24, 1, 230400, 0x189db9d5
-0, 25, 25, 1, 230400, 0x1a38b8da
-0, 26, 26, 1, 230400, 0xccd6bd07
-0, 27, 27, 1, 230400, 0xd4a2bc53
-0, 28, 28, 1, 230400, 0x9ce3bb4e
-0, 29, 29, 1, 230400, 0x5ffdc4db
-0, 30, 30, 1, 230400, 0xc885c7c9
-0, 31, 31, 1, 230400, 0xe27b9d33
-0, 32, 32, 1, 230400, 0xac03a256
-0, 33, 33, 1, 230400, 0xa2c73929
-0, 34, 34, 1, 230400, 0x33793b73
-0, 35, 35, 1, 230400, 0x1e400add
-0, 36, 36, 1, 230400, 0x98e50c6e
-0, 37, 37, 1, 230400, 0x68ed226d
-0, 38, 38, 1, 230400, 0x569e23cb
-0, 39, 39, 1, 230400, 0x82bf3fc0
-0, 40, 40, 1, 230400, 0x2b202e86
-0, 41, 41, 1, 230400, 0x7acd2dee
-0, 42, 42, 1, 230400, 0xfe872e42
-0, 43, 43, 1, 230400, 0x026c12e5
-0, 44, 44, 1, 230400, 0x81561399
-0, 45, 45, 1, 230400, 0xa08c13b6
-0, 46, 46, 1, 230400, 0x89e712f5
-0, 47, 47, 1, 230400, 0x569011ac
-0, 48, 48, 1, 230400, 0xd4691112
-0, 49, 49, 1, 230400, 0x2e50165a
-0, 50, 50, 1, 230400, 0x0a1215b6
-0, 51, 51, 1, 230400, 0x3c5316e3
-0, 52, 52, 1, 230400, 0x079c1393
-0, 53, 53, 1, 230400, 0x39ca1c48
-0, 54, 54, 1, 230400, 0xe27f199c
-0, 55, 55, 1, 230400, 0x10ab1bab
-0, 56, 56, 1, 230400, 0xeab017c3
-0, 57, 57, 1, 230400, 0x5f701f77
-0, 58, 58, 1, 230400, 0x01371d7d
-0, 59, 59, 1, 230400, 0x22751e99
-0, 60, 60, 1, 230400, 0xaee91a97
-0, 61, 61, 1, 230400, 0x27b41f32
-0, 62, 62, 1, 230400, 0x4ff32bb1
-0, 63, 63, 1, 230400, 0x86e02864
-0, 64, 64, 1, 230400, 0x5eb52b3e
-0, 65, 65, 1, 230400, 0xd9252ba8
-0, 66, 66, 1, 230400, 0x72232d9b
-0, 67, 67, 1, 230400, 0x599a206f
-0, 68, 68, 1, 230400, 0x4d2c1ca5
-0, 69, 69, 1, 230400, 0x9166293b
-0, 70, 70, 1, 230400, 0x00992453
+0, 0, 0, 1, 230400, 0x10a99774
+0, 1, 1, 1, 230400, 0xa18b90f8
+0, 2, 2, 1, 230400, 0x837a95f9
+0, 3, 3, 1, 230400, 0xc50d948b
+0, 4, 4, 1, 230400, 0xdefc8eca
+0, 5, 5, 1, 230400, 0x00498bdb
+0, 6, 6, 1, 230400, 0x22458cdc
+0, 7, 7, 1, 230400, 0xfad1418c
+0, 8, 8, 1, 230400, 0xf09341dd
+0, 9, 9, 1, 230400, 0x5ef141ac
+0, 10, 10, 1, 230400, 0x6f2d815f
+0, 11, 11, 1, 230400, 0x960880c2
+0, 12, 12, 1, 230400, 0x4ba37f8f
+0, 13, 13, 1, 230400, 0x3e678082
+0, 14, 14, 1, 230400, 0x4f1c80da
+0, 15, 15, 1, 230400, 0x69be82fd
+0, 16, 16, 1, 230400, 0x81d29b80
+0, 17, 17, 1, 230400, 0x5fdc9af7
+0, 18, 18, 1, 230400, 0xb8969c2b
+0, 19, 19, 1, 230400, 0xdb37a691
+0, 20, 20, 1, 230400, 0xdeb6a645
+0, 21, 21, 1, 230400, 0xf5c6a606
+0, 22, 22, 1, 230400, 0x110ab482
+0, 23, 23, 1, 230400, 0x5bddb45b
+0, 24, 24, 1, 230400, 0xc18ab32a
+0, 25, 25, 1, 230400, 0x22c1b2be
+0, 26, 26, 1, 230400, 0xaa7cb5c3
+0, 27, 27, 1, 230400, 0x5e8fb50f
+0, 28, 28, 1, 230400, 0x20e1b42a
+0, 29, 29, 1, 230400, 0x0c94c158
+0, 30, 30, 1, 230400, 0x41adc2a3
+0, 31, 31, 1, 230400, 0xbc359983
+0, 32, 32, 1, 230400, 0x19bb9eea
+0, 33, 33, 1, 230400, 0xfecd2f06
+0, 34, 34, 1, 230400, 0x26ba3110
+0, 35, 35, 1, 230400, 0xfdbcff0f
+0, 36, 36, 1, 230400, 0x1f030028
+0, 37, 37, 1, 230400, 0xccca1b0b
+0, 38, 38, 1, 230400, 0x66f91b1f
+0, 39, 39, 1, 230400, 0x0ef4366b
+0, 40, 40, 1, 230400, 0x2fac271c
+0, 41, 41, 1, 230400, 0xbef026a9
+0, 42, 42, 1, 230400, 0x775726f5
+0, 43, 43, 1, 230400, 0x35210966
+0, 44, 44, 1, 230400, 0x0c36099a
+0, 45, 45, 1, 230400, 0xc4f00a24
+0, 46, 46, 1, 230400, 0xa7c409b9
+0, 47, 47, 1, 230400, 0xa92d082b
+0, 48, 48, 1, 230400, 0xfc49442e
+0, 49, 49, 1, 230400, 0xb7de4997
+0, 50, 50, 1, 230400, 0x16d24877
+0, 51, 51, 1, 230400, 0xb0954a17
+0, 52, 52, 1, 230400, 0x709f4766
+0, 53, 53, 1, 230400, 0xe0dc4e4f
+0, 54, 54, 1, 230400, 0xfa844b09
+0, 55, 55, 1, 230400, 0xa62a4cf2
+0, 56, 56, 1, 230400, 0x078d4a76
+0, 57, 57, 1, 230400, 0x64c4505c
+0, 58, 58, 1, 230400, 0xe8604f13
+0, 59, 59, 1, 230400, 0x39a3503c
+0, 60, 60, 1, 230400, 0x915a4cf1
+0, 61, 61, 1, 230400, 0x495a5176
+0, 62, 62, 1, 230400, 0xc0a75b2b
+0, 63, 63, 1, 230400, 0x1a385761
+0, 64, 64, 1, 230400, 0x9a245984
+0, 65, 65, 1, 230400, 0xbe475ad9
+0, 66, 66, 1, 230400, 0x8e275c85
+0, 67, 67, 1, 230400, 0x5ba45436
+0, 68, 68, 1, 230400, 0x80285097
+0, 69, 69, 1, 230400, 0xbb1c5bfd
+0, 70, 70, 1, 230400, 0x273a5890
diff --git a/tests/ref/fate/filter-paletteuse-bayer0 b/tests/ref/fate/filter-paletteuse-bayer0
index 85b3832f19..44cd09a6a9 100644
--- a/tests/ref/fate/filter-paletteuse-bayer0
+++ b/tests/ref/fate/filter-paletteuse-bayer0
@@ -3,74 +3,74 @@
#codec_id 0: rawvideo
#dimensions 0: 320x180
#sar 0: 1/1
-0, 0, 0, 1, 230400, 0xfb6042d2
-0, 1, 1, 1, 230400, 0x1c193c09
-0, 2, 2, 1, 230400, 0x183442f8
-0, 3, 3, 1, 230400, 0xa9634084
-0, 4, 4, 1, 230400, 0x90df3d2f
-0, 5, 5, 1, 230400, 0x59d7389f
-0, 6, 6, 1, 230400, 0xb9bd3a30
-0, 7, 7, 1, 230400, 0x9874ee38
-0, 8, 8, 1, 230400, 0xf661f01f
-0, 9, 9, 1, 230400, 0xacbcedbd
-0, 10, 10, 1, 230400, 0x05f02d59
-0, 11, 11, 1, 230400, 0xc54c2cc8
-0, 12, 12, 1, 230400, 0x19c92d61
-0, 13, 13, 1, 230400, 0x14902fb2
-0, 14, 14, 1, 230400, 0x99b62fb6
-0, 15, 15, 1, 230400, 0x3fc63293
-0, 16, 16, 1, 230400, 0x1eed4b38
-0, 17, 17, 1, 230400, 0xe9d747e0
-0, 18, 18, 1, 230400, 0x9825496f
-0, 19, 19, 1, 230400, 0x94625411
-0, 20, 20, 1, 230400, 0xed7052a3
-0, 21, 21, 1, 230400, 0x80d552dc
-0, 22, 22, 1, 230400, 0x89b360bb
-0, 23, 23, 1, 230400, 0xee9a616a
-0, 24, 24, 1, 230400, 0x30bb5f86
-0, 25, 25, 1, 230400, 0x5ec15eae
-0, 26, 26, 1, 230400, 0x0956633e
-0, 27, 27, 1, 230400, 0x72df62fa
-0, 28, 28, 1, 230400, 0xbafd61d0
-0, 29, 29, 1, 230400, 0x393f81f3
-0, 30, 30, 1, 230400, 0xba6a848c
-0, 31, 31, 1, 230400, 0x502ba0d9
-0, 32, 32, 1, 230400, 0xc81ba71d
-0, 33, 33, 1, 230400, 0x54cdf270
-0, 34, 34, 1, 230400, 0xe951f3e2
-0, 35, 35, 1, 230400, 0xbf15baa1
-0, 36, 36, 1, 230400, 0xbf96bb12
-0, 37, 37, 1, 230400, 0xcdd5cafe
-0, 38, 38, 1, 230400, 0x97b1cbb4
-0, 39, 39, 1, 230400, 0x955ae28f
-0, 40, 40, 1, 230400, 0x6a8dd28f
-0, 41, 41, 1, 230400, 0x8f02d268
-0, 42, 42, 1, 230400, 0x3075d269
-0, 43, 43, 1, 230400, 0x29e8b910
-0, 44, 44, 1, 230400, 0xb35ab888
-0, 45, 45, 1, 230400, 0xc3afb942
-0, 46, 46, 1, 230400, 0xeba8b860
-0, 47, 47, 1, 230400, 0x5de8b7ab
-0, 48, 48, 1, 230400, 0x90233679
-0, 49, 49, 1, 230400, 0x5fbc3abb
-0, 50, 50, 1, 230400, 0xeaa73b87
-0, 51, 51, 1, 230400, 0xbd0a3c4b
-0, 52, 52, 1, 230400, 0xeddb39ba
-0, 53, 53, 1, 230400, 0x269d4131
-0, 54, 54, 1, 230400, 0xae3e3e8c
-0, 55, 55, 1, 230400, 0x65f54056
-0, 56, 56, 1, 230400, 0xf2173c5b
-0, 57, 57, 1, 230400, 0xbd714477
-0, 58, 58, 1, 230400, 0xb60c42ed
-0, 59, 59, 1, 230400, 0x8def43a5
-0, 60, 60, 1, 230400, 0xe6a73f05
-0, 61, 61, 1, 230400, 0xedfe4430
-0, 62, 62, 1, 230400, 0x76c5505a
-0, 63, 63, 1, 230400, 0xf48d4d04
-0, 64, 64, 1, 230400, 0xa49950b5
-0, 65, 65, 1, 230400, 0xc64d51d8
-0, 66, 66, 1, 230400, 0xa08253ec
-0, 67, 67, 1, 230400, 0xd6ef4609
-0, 68, 68, 1, 230400, 0x27a241e7
-0, 69, 69, 1, 230400, 0xe5f74b4a
-0, 70, 70, 1, 230400, 0xb0194751
+0, 0, 0, 1, 230400, 0x9f192d87
+0, 1, 1, 1, 230400, 0x49db27f5
+0, 2, 2, 1, 230400, 0x8f8f2cb0
+0, 3, 3, 1, 230400, 0x33a82b14
+0, 4, 4, 1, 230400, 0x6f03275f
+0, 5, 5, 1, 230400, 0x1fce2453
+0, 6, 6, 1, 230400, 0x932925b3
+0, 7, 7, 1, 230400, 0x9987dba9
+0, 8, 8, 1, 230400, 0x9ba2dd04
+0, 9, 9, 1, 230400, 0x37eadc31
+0, 10, 10, 1, 230400, 0xda0518e2
+0, 11, 11, 1, 230400, 0xb96718b5
+0, 12, 12, 1, 230400, 0x0d63191e
+0, 13, 13, 1, 230400, 0xfc561af0
+0, 14, 14, 1, 230400, 0x22fd1b68
+0, 15, 15, 1, 230400, 0xef631dc5
+0, 16, 16, 1, 230400, 0xc0673745
+0, 17, 17, 1, 230400, 0x56c933f6
+0, 18, 18, 1, 230400, 0x3790359a
+0, 19, 19, 1, 230400, 0xd3293d02
+0, 20, 20, 1, 230400, 0xee513caa
+0, 21, 21, 1, 230400, 0x0fc33c17
+0, 22, 22, 1, 230400, 0x00c74991
+0, 23, 23, 1, 230400, 0xa7de49f5
+0, 24, 24, 1, 230400, 0xd99b485a
+0, 25, 25, 1, 230400, 0x6aab47d2
+0, 26, 26, 1, 230400, 0x2e434bf7
+0, 27, 27, 1, 230400, 0x46a04b1d
+0, 28, 28, 1, 230400, 0x135f49f8
+0, 29, 29, 1, 230400, 0x50566b86
+0, 30, 30, 1, 230400, 0xb0416d84
+0, 31, 31, 1, 230400, 0x8f20840c
+0, 32, 32, 1, 230400, 0xedbf8857
+0, 33, 33, 1, 230400, 0x30d6d698
+0, 34, 34, 1, 230400, 0xbc88d7a7
+0, 35, 35, 1, 230400, 0xce869fa3
+0, 36, 36, 1, 230400, 0xa973a0b1
+0, 37, 37, 1, 230400, 0x4c93b3a5
+0, 38, 38, 1, 230400, 0x3574b473
+0, 39, 39, 1, 230400, 0xa96bc936
+0, 40, 40, 1, 230400, 0x4a83b86d
+0, 41, 41, 1, 230400, 0x3b68b7cb
+0, 42, 42, 1, 230400, 0xc0feb869
+0, 43, 43, 1, 230400, 0x1a7d9ed8
+0, 44, 44, 1, 230400, 0xc32e9f12
+0, 45, 45, 1, 230400, 0x30f29fa4
+0, 46, 46, 1, 230400, 0x7b369f80
+0, 47, 47, 1, 230400, 0x28249e03
+0, 48, 48, 1, 230400, 0xf5c67eee
+0, 49, 49, 1, 230400, 0x40728404
+0, 50, 50, 1, 230400, 0xbf7383b0
+0, 51, 51, 1, 230400, 0x446d84c9
+0, 52, 52, 1, 230400, 0x5f678279
+0, 53, 53, 1, 230400, 0x4d1d891d
+0, 54, 54, 1, 230400, 0x173c866c
+0, 55, 55, 1, 230400, 0x045988a6
+0, 56, 56, 1, 230400, 0xf25f848f
+0, 57, 57, 1, 230400, 0xd61c8d71
+0, 58, 58, 1, 230400, 0x86428b1e
+0, 59, 59, 1, 230400, 0xc6268c08
+0, 60, 60, 1, 230400, 0xed9787e3
+0, 61, 61, 1, 230400, 0xe7ef8a27
+0, 62, 62, 1, 230400, 0x5dc09497
+0, 63, 63, 1, 230400, 0x8ec490b6
+0, 64, 64, 1, 230400, 0xc01d92e4
+0, 65, 65, 1, 230400, 0xbf6a941d
+0, 66, 66, 1, 230400, 0x53fd966a
+0, 67, 67, 1, 230400, 0xa2138be9
+0, 68, 68, 1, 230400, 0x2b868967
+0, 69, 69, 1, 230400, 0x4c889564
+0, 70, 70, 1, 230400, 0x6d8491ed
diff --git a/tests/ref/fate/filter-paletteuse-nodither b/tests/ref/fate/filter-paletteuse-nodither
index a2e61c3690..1535777547 100644
--- a/tests/ref/fate/filter-paletteuse-nodither
+++ b/tests/ref/fate/filter-paletteuse-nodither
@@ -3,74 +3,74 @@
#codec_id 0: rawvideo
#dimensions 0: 320x180
#sar 0: 1/1
-0, 0, 0, 1, 230400, 0x690560cb
-0, 1, 1, 1, 230400, 0x197a5a54
-0, 2, 2, 1, 230400, 0x665961db
-0, 3, 3, 1, 230400, 0xce0b5fa8
-0, 4, 4, 1, 230400, 0xa40e5cb0
-0, 5, 5, 1, 230400, 0xa5aa58da
-0, 6, 6, 1, 230400, 0x8e0259bb
-0, 7, 7, 1, 230400, 0x476d0dba
-0, 8, 8, 1, 230400, 0xfb1b0e8c
-0, 9, 9, 1, 230400, 0x50f60d3b
-0, 10, 10, 1, 230400, 0x12cd4bab
-0, 11, 11, 1, 230400, 0x4c274b13
-0, 12, 12, 1, 230400, 0xea494b0a
-0, 13, 13, 1, 230400, 0x118c4cc1
-0, 14, 14, 1, 230400, 0xd4224db7
-0, 15, 15, 1, 230400, 0xc3014f88
-0, 16, 16, 1, 230400, 0xe07a6838
-0, 17, 17, 1, 230400, 0x1b97659a
-0, 18, 18, 1, 230400, 0xf104670c
-0, 19, 19, 1, 230400, 0x7b63733d
-0, 20, 20, 1, 230400, 0x2c237200
-0, 21, 21, 1, 230400, 0x775d7248
-0, 22, 22, 1, 230400, 0xcaee7f9e
-0, 23, 23, 1, 230400, 0x4e4680a1
-0, 24, 24, 1, 230400, 0x21fb7e53
-0, 25, 25, 1, 230400, 0xf0297db6
-0, 26, 26, 1, 230400, 0x79a9829d
-0, 27, 27, 1, 230400, 0x8ccb80f7
-0, 28, 28, 1, 230400, 0xf4dd807f
-0, 29, 29, 1, 230400, 0xb6cc8696
-0, 30, 30, 1, 230400, 0x6c8a8917
-0, 31, 31, 1, 230400, 0x9e08615a
-0, 32, 32, 1, 230400, 0xc098685b
-0, 33, 33, 1, 230400, 0x5c09e710
-0, 34, 34, 1, 230400, 0xe4c4e9be
-0, 35, 35, 1, 230400, 0xac59c150
-0, 36, 36, 1, 230400, 0x6045c272
-0, 37, 37, 1, 230400, 0xf71ee6dc
-0, 38, 38, 1, 230400, 0xc82ce6f6
-0, 39, 39, 1, 230400, 0xb7ed039a
-0, 40, 40, 1, 230400, 0xda93f241
-0, 41, 41, 1, 230400, 0x194bf23b
-0, 42, 42, 1, 230400, 0xe7e6f2e2
-0, 43, 43, 1, 230400, 0xe479d834
-0, 44, 44, 1, 230400, 0xefdfd87e
-0, 45, 45, 1, 230400, 0xec66d8c0
-0, 46, 46, 1, 230400, 0x3a6bd81b
-0, 47, 47, 1, 230400, 0xb5d1d700
-0, 48, 48, 1, 230400, 0x3bc69e8b
-0, 49, 49, 1, 230400, 0x723fa455
-0, 50, 50, 1, 230400, 0x7c49a392
-0, 51, 51, 1, 230400, 0x272ea4b7
-0, 52, 52, 1, 230400, 0xebdda081
-0, 53, 53, 1, 230400, 0xfd26ab99
-0, 54, 54, 1, 230400, 0xfa02a891
-0, 55, 55, 1, 230400, 0xda2caa7f
-0, 56, 56, 1, 230400, 0x2360a611
-0, 57, 57, 1, 230400, 0xaa3baefd
-0, 58, 58, 1, 230400, 0x0961ad5c
-0, 59, 59, 1, 230400, 0x48d2ae47
-0, 60, 60, 1, 230400, 0x20eda81b
-0, 61, 61, 1, 230400, 0x8821adbb
-0, 62, 62, 1, 230400, 0x1150b810
-0, 63, 63, 1, 230400, 0x08dab596
-0, 64, 64, 1, 230400, 0x4731b7a5
-0, 65, 65, 1, 230400, 0xf382b87e
-0, 66, 66, 1, 230400, 0xdba7bac2
-0, 67, 67, 1, 230400, 0xf569acf9
-0, 68, 68, 1, 230400, 0x22d8a95d
-0, 69, 69, 1, 230400, 0xed0bb4fb
-0, 70, 70, 1, 230400, 0x2dccb218
+0, 0, 0, 1, 230400, 0xf7976830
+0, 1, 1, 1, 230400, 0xfb756340
+0, 2, 2, 1, 230400, 0x2199687e
+0, 3, 3, 1, 230400, 0xd8186657
+0, 4, 4, 1, 230400, 0xd33a6319
+0, 5, 5, 1, 230400, 0x704c603b
+0, 6, 6, 1, 230400, 0x9e6c6146
+0, 7, 7, 1, 230400, 0x63ee15f1
+0, 8, 8, 1, 230400, 0x8b201716
+0, 9, 9, 1, 230400, 0xac8e1602
+0, 10, 10, 1, 230400, 0xd97e53b6
+0, 11, 11, 1, 230400, 0x8ecc5304
+0, 12, 12, 1, 230400, 0x0ea25368
+0, 13, 13, 1, 230400, 0x78c7555e
+0, 14, 14, 1, 230400, 0x96e3562d
+0, 15, 15, 1, 230400, 0xecc75867
+0, 16, 16, 1, 230400, 0x56e26feb
+0, 17, 17, 1, 230400, 0xf65d6fac
+0, 18, 18, 1, 230400, 0x5597709e
+0, 19, 19, 1, 230400, 0xeb077c34
+0, 20, 20, 1, 230400, 0xce997afa
+0, 21, 21, 1, 230400, 0xbec37abd
+0, 22, 22, 1, 230400, 0xb01688c4
+0, 23, 23, 1, 230400, 0x0c828927
+0, 24, 24, 1, 230400, 0xa6308757
+0, 25, 25, 1, 230400, 0x90e68727
+0, 26, 26, 1, 230400, 0xe3258ae5
+0, 27, 27, 1, 230400, 0x988e8993
+0, 28, 28, 1, 230400, 0xc13688b0
+0, 29, 29, 1, 230400, 0xcc528fa1
+0, 30, 30, 1, 230400, 0x28c691a9
+0, 31, 31, 1, 230400, 0x7de96ae9
+0, 32, 32, 1, 230400, 0x47946fa5
+0, 33, 33, 1, 230400, 0x1c3efa7c
+0, 34, 34, 1, 230400, 0x7fc6fc80
+0, 35, 35, 1, 230400, 0x0509c853
+0, 36, 36, 1, 230400, 0x691bca43
+0, 37, 37, 1, 230400, 0xadb5eafd
+0, 38, 38, 1, 230400, 0x559feafa
+0, 39, 39, 1, 230400, 0xa17906da
+0, 40, 40, 1, 230400, 0x6091f838
+0, 41, 41, 1, 230400, 0x9640f6fa
+0, 42, 42, 1, 230400, 0xa73af817
+0, 43, 43, 1, 230400, 0x255fde90
+0, 44, 44, 1, 230400, 0x7284deac
+0, 45, 45, 1, 230400, 0xf603df3f
+0, 46, 46, 1, 230400, 0x435cde3a
+0, 47, 47, 1, 230400, 0x825add61
+0, 48, 48, 1, 230400, 0x13b6bc0e
+0, 49, 49, 1, 230400, 0x72e7c13d
+0, 50, 50, 1, 230400, 0x2bcdc061
+0, 51, 51, 1, 230400, 0xb2e7c189
+0, 52, 52, 1, 230400, 0xb4eabe46
+0, 53, 53, 1, 230400, 0x0108c777
+0, 54, 54, 1, 230400, 0xae5cc3b3
+0, 55, 55, 1, 230400, 0xdd8ec4da
+0, 56, 56, 1, 230400, 0x0ec5c1d0
+0, 57, 57, 1, 230400, 0x3ad7cab8
+0, 58, 58, 1, 230400, 0xa9b0c95b
+0, 59, 59, 1, 230400, 0xe98ec9a0
+0, 60, 60, 1, 230400, 0x07d9c42e
+0, 61, 61, 1, 230400, 0x23b8c94e
+0, 62, 62, 1, 230400, 0xd3a9d25d
+0, 63, 63, 1, 230400, 0x3154cf78
+0, 64, 64, 1, 230400, 0x163ad234
+0, 65, 65, 1, 230400, 0x3ce2d276
+0, 66, 66, 1, 230400, 0x6f0bd556
+0, 67, 67, 1, 230400, 0xd982cb24
+0, 68, 68, 1, 230400, 0xd04ac7ab
+0, 69, 69, 1, 230400, 0x5302d29a
+0, 70, 70, 1, 230400, 0xd989d0dc
diff --git a/tests/ref/fate/filter-paletteuse-sierra2_4a b/tests/ref/fate/filter-paletteuse-sierra2_4a
index d257820a32..75115066e1 100644
--- a/tests/ref/fate/filter-paletteuse-sierra2_4a
+++ b/tests/ref/fate/filter-paletteuse-sierra2_4a
@@ -3,74 +3,74 @@
#codec_id 0: rawvideo
#dimensions 0: 320x180
#sar 0: 1/1
-0, 0, 0, 1, 230400, 0xa4f85758
-0, 1, 1, 1, 230400, 0xbe83505c
-0, 2, 2, 1, 230400, 0x0a09584e
-0, 3, 3, 1, 230400, 0xd2065629
-0, 4, 4, 1, 230400, 0x11eb5319
-0, 5, 5, 1, 230400, 0x61024f4c
-0, 6, 6, 1, 230400, 0xd5384faa
-0, 7, 7, 1, 230400, 0xdeae0343
-0, 8, 8, 1, 230400, 0xcb640541
-0, 9, 9, 1, 230400, 0xea2602c3
-0, 10, 10, 1, 230400, 0xa7974293
-0, 11, 11, 1, 230400, 0x67cd4287
-0, 12, 12, 1, 230400, 0x83fa437a
-0, 13, 13, 1, 230400, 0x852b42bf
-0, 14, 14, 1, 230400, 0x6d2d434c
-0, 15, 15, 1, 230400, 0x20c44629
-0, 16, 16, 1, 230400, 0xf2a35f57
-0, 17, 17, 1, 230400, 0x232959ec
-0, 18, 18, 1, 230400, 0x1f8e5c48
-0, 19, 19, 1, 230400, 0x88dc69bd
-0, 20, 20, 1, 230400, 0x4b6866f3
-0, 21, 21, 1, 230400, 0xe8f966dc
-0, 22, 22, 1, 230400, 0xe0877466
-0, 23, 23, 1, 230400, 0x8799748c
-0, 24, 24, 1, 230400, 0xcab871bc
-0, 25, 25, 1, 230400, 0x2e0372b4
-0, 26, 26, 1, 230400, 0x15fb77d5
-0, 27, 27, 1, 230400, 0xbadf75fc
-0, 28, 28, 1, 230400, 0xa4977626
-0, 29, 29, 1, 230400, 0x5b987943
-0, 30, 30, 1, 230400, 0x9ed57c09
-0, 31, 31, 1, 230400, 0x565d5105
-0, 32, 32, 1, 230400, 0x901b5a07
-0, 33, 33, 1, 230400, 0x8dc4e9a8
-0, 34, 34, 1, 230400, 0x0b9cee1c
-0, 35, 35, 1, 230400, 0x2bcdbe37
-0, 36, 36, 1, 230400, 0xf3e2bf71
-0, 37, 37, 1, 230400, 0xb718da67
-0, 38, 38, 1, 230400, 0x8f59da64
-0, 39, 39, 1, 230400, 0x8812f9aa
-0, 40, 40, 1, 230400, 0xe0dae6a3
-0, 41, 41, 1, 230400, 0xd2c7e5b7
-0, 42, 42, 1, 230400, 0xea2ae5d2
-0, 43, 43, 1, 230400, 0x2d66ca25
-0, 44, 44, 1, 230400, 0xf0d3cac6
-0, 45, 45, 1, 230400, 0xb9acccac
-0, 46, 46, 1, 230400, 0x8523ca4a
-0, 47, 47, 1, 230400, 0x92b9c9ef
-0, 48, 48, 1, 230400, 0x0a88946e
-0, 49, 49, 1, 230400, 0xe33699b8
-0, 50, 50, 1, 230400, 0x5e7b9917
-0, 51, 51, 1, 230400, 0xdac99998
-0, 52, 52, 1, 230400, 0xb5c995fc
-0, 53, 53, 1, 230400, 0x908b9f50
-0, 54, 54, 1, 230400, 0x60d59ced
-0, 55, 55, 1, 230400, 0x212e9f55
-0, 56, 56, 1, 230400, 0x95e69b2a
-0, 57, 57, 1, 230400, 0x6c38a34a
-0, 58, 58, 1, 230400, 0xeb32a103
-0, 59, 59, 1, 230400, 0x0131a1b7
-0, 60, 60, 1, 230400, 0xd59b9c4e
-0, 61, 61, 1, 230400, 0x2fc0a13f
-0, 62, 62, 1, 230400, 0x7a40adf9
-0, 63, 63, 1, 230400, 0x5cdbab2f
-0, 64, 64, 1, 230400, 0xcdc0ada8
-0, 65, 65, 1, 230400, 0x2f5faf32
-0, 66, 66, 1, 230400, 0xd463b224
-0, 67, 67, 1, 230400, 0xe337a2d5
-0, 68, 68, 1, 230400, 0xe775a0c1
-0, 69, 69, 1, 230400, 0x726aab49
-0, 70, 70, 1, 230400, 0x74dda81e
+0, 0, 0, 1, 230400, 0xa40645e7
+0, 1, 1, 1, 230400, 0x72b63e5e
+0, 2, 2, 1, 230400, 0x030344b2
+0, 3, 3, 1, 230400, 0xab8c42b8
+0, 4, 4, 1, 230400, 0x1fcd3f00
+0, 5, 5, 1, 230400, 0x371f3c27
+0, 6, 6, 1, 230400, 0x0cfe3dff
+0, 7, 7, 1, 230400, 0x0c64f102
+0, 8, 8, 1, 230400, 0xbca2f2f7
+0, 9, 9, 1, 230400, 0x5198f134
+0, 10, 10, 1, 230400, 0xee02305e
+0, 11, 11, 1, 230400, 0x22592ff1
+0, 12, 12, 1, 230400, 0xa230311d
+0, 13, 13, 1, 230400, 0x66453278
+0, 14, 14, 1, 230400, 0x68c63165
+0, 15, 15, 1, 230400, 0xded434ae
+0, 16, 16, 1, 230400, 0xff1a4e51
+0, 17, 17, 1, 230400, 0x31064c7b
+0, 18, 18, 1, 230400, 0x3d374e74
+0, 19, 19, 1, 230400, 0x58ec59d5
+0, 20, 20, 1, 230400, 0x8c02570f
+0, 21, 21, 1, 230400, 0x5f6b56ac
+0, 22, 22, 1, 230400, 0x781f6408
+0, 23, 23, 1, 230400, 0x1c0165d2
+0, 24, 24, 1, 230400, 0xc6e66311
+0, 25, 25, 1, 230400, 0x0375635d
+0, 26, 26, 1, 230400, 0x00756822
+0, 27, 27, 1, 230400, 0xb4276753
+0, 28, 28, 1, 230400, 0x8b826638
+0, 29, 29, 1, 230400, 0x201066e2
+0, 30, 30, 1, 230400, 0x4acc6ab8
+0, 31, 31, 1, 230400, 0xa78741fe
+0, 32, 32, 1, 230400, 0xfe85481e
+0, 33, 33, 1, 230400, 0x7153dae0
+0, 34, 34, 1, 230400, 0x9b7ede62
+0, 35, 35, 1, 230400, 0x785cad21
+0, 36, 36, 1, 230400, 0x4c81ac20
+0, 37, 37, 1, 230400, 0x2e9cc57c
+0, 38, 38, 1, 230400, 0x0043c629
+0, 39, 39, 1, 230400, 0xccb1e72d
+0, 40, 40, 1, 230400, 0xf800d4d9
+0, 41, 41, 1, 230400, 0xb40ad374
+0, 42, 42, 1, 230400, 0xa94bd3eb
+0, 43, 43, 1, 230400, 0xefa8b85f
+0, 44, 44, 1, 230400, 0xa32ab85d
+0, 45, 45, 1, 230400, 0xbb89b941
+0, 46, 46, 1, 230400, 0x6556b8f0
+0, 47, 47, 1, 230400, 0x3d5ab7ab
+0, 48, 48, 1, 230400, 0x7b68afd9
+0, 49, 49, 1, 230400, 0x7518b560
+0, 50, 50, 1, 230400, 0x4d6bb43f
+0, 51, 51, 1, 230400, 0xafe7b5a0
+0, 52, 52, 1, 230400, 0x5211b1c0
+0, 53, 53, 1, 230400, 0x4ababa33
+0, 54, 54, 1, 230400, 0x90f7b7fc
+0, 55, 55, 1, 230400, 0x0b0dba13
+0, 56, 56, 1, 230400, 0xc9b6b5b4
+0, 57, 57, 1, 230400, 0xa1c5bfcd
+0, 58, 58, 1, 230400, 0xde30bdaf
+0, 59, 59, 1, 230400, 0x6ae5bc07
+0, 60, 60, 1, 230400, 0x9845b936
+0, 61, 61, 1, 230400, 0xcad4bf41
+0, 62, 62, 1, 230400, 0x90abca33
+0, 63, 63, 1, 230400, 0x2c12c614
+0, 64, 64, 1, 230400, 0x2d65c7ea
+0, 65, 65, 1, 230400, 0x1949c8f3
+0, 66, 66, 1, 230400, 0xe5adcb22
+0, 67, 67, 1, 230400, 0xa87bc06c
+0, 68, 68, 1, 230400, 0x4c43baeb
+0, 69, 69, 1, 230400, 0xbf66c7f9
+0, 70, 70, 1, 230400, 0x9883c62d
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 10/32] avfilter/palettegen: move box stats computation to a dedicated function
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (8 preceding siblings ...)
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 09/32] avfilter/paletteuse: switch to a perceptual model Clément Bœsch
@ 2022-12-27 23:17 ` Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 11/32] avfilter/palettegen: define the best axis to cut using the squared error Clément Bœsch
` (22 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:17 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
---
libavfilter/vf_palettegen.c | 64 ++++++++++++++++++++++---------------
1 file changed, 38 insertions(+), 26 deletions(-)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index bea3292796..a047c75599 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -40,6 +40,8 @@ struct color_ref {
/* Store a range of colors */
struct range_box {
uint32_t color; // average color
+ int major_axis; // best axis candidate for cutting the box
+ uint64_t weight; // sum of all the weights of the colors
int64_t variance; // overall variance of the box (how much the colors are spread)
int start; // index in PaletteGenContext->refs
int len; // number of referenced colors
@@ -143,6 +145,35 @@ static av_always_inline int diff(const uint32_t a, const uint32_t b)
return dr*dr + dg*dg + db*db;
}
+static void compute_box_stats(PaletteGenContext *s, struct range_box *box)
+{
+ int rr, gr, br;
+
+ /* compute the box weight (sum all the weights of the colors in the
+ * range) and its boundings */
+ uint8_t min[3] = {0xff, 0xff, 0xff};
+ uint8_t max[3] = {0x00, 0x00, 0x00};
+ box->weight = 0;
+ for (int 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 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 */
+ rr = max[0] - min[0];
+ gr = max[1] - min[1];
+ br = max[2] - min[2];
+ box->major_axis = 1; // pick green by default (the color the eye is the most sensitive to)
+ if (br >= rr && br >= gr) box->major_axis = 2;
+ if (rr >= gr && rr >= br) box->major_axis = 0;
+ if (gr >= rr && gr >= br) box->major_axis = 1; // prefer green again
+}
+
/**
* Find the next box to split: pick the one with the highest variance
*/
@@ -324,35 +355,16 @@ static AVFrame *get_palette_frame(AVFilterContext *ctx)
s->nb_boxes = 1;
while (box && box->len > 1) {
- 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[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 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;
- }
+ int i, longest;
+ uint64_t median, box_weight;
- /* define the axis to sort by according to the widest range of colors */
- 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
+ compute_box_stats(s, box);
+ longest = box->major_axis;
+ box_weight = box->weight;
- ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64" ranges:[%2x %2x %2x] sort by %c (already sorted:%c) ",
+ ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64" sort by %c (already sorted:%c) ",
box_id, box->start, box->start + box->len - 1, box->len, box_weight,
- rr, gr, br, "rgb"[longest], box->sorted_by == longest ? 'y':'n');
+ "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) {
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 11/32] avfilter/palettegen: define the best axis to cut using the squared error
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (9 preceding siblings ...)
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 10/32] avfilter/palettegen: move box stats computation to a dedicated function Clément Bœsch
@ 2022-12-27 23:17 ` Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 12/32] avfilter/palettegen: use box->major_axis without intermediate variable Clément Bœsch
` (21 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:17 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
This is following the results from personal research¹.
¹: https://github.com/ubitux/research/tree/main/color-quantization#results
---
libavfilter/vf_palettegen.c | 42 ++++++++++++++++++------------
tests/ref/fate/filter-palettegen-1 | 2 +-
tests/ref/fate/filter-palettegen-2 | 2 +-
3 files changed, 27 insertions(+), 19 deletions(-)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index a047c75599..ed1448755c 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -147,31 +147,39 @@ static av_always_inline int diff(const uint32_t a, const uint32_t b)
static void compute_box_stats(PaletteGenContext *s, struct range_box *box)
{
- int rr, gr, br;
+ int avg[3];
+ int64_t er2[3] = {0};
- /* compute the box weight (sum all the weights of the colors in the
- * range) and its boundings */
- uint8_t min[3] = {0xff, 0xff, 0xff};
- uint8_t max[3] = {0x00, 0x00, 0x00};
+ /* Compute average color */
+ uint64_t sr = 0, sg = 0, sb = 0;
box->weight = 0;
for (int 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 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]);
+ sr += (ref->color >> 16 & 0xff) * ref->count;
+ sg += (ref->color >> 8 & 0xff) * ref->count;
+ sb += (ref->color & 0xff) * ref->count;
box->weight += ref->count;
}
+ avg[0] = sr / box->weight;
+ avg[1] = sg / box->weight;
+ avg[2] = sb / box->weight;
- /* define the axis to sort by according to the widest range of colors */
- rr = max[0] - min[0];
- gr = max[1] - min[1];
- br = max[2] - min[2];
+ /* Compute squared error of each color channel */
+ for (int i = box->start; i < box->start + box->len; i++) {
+ const struct color_ref *ref = s->refs[i];
+ const int64_t dr = (int)(ref->color >> 16 & 0xff) - avg[0];
+ const int64_t dg = (int)(ref->color >> 8 & 0xff) - avg[1];
+ const int64_t db = (int)(ref->color & 0xff) - avg[2];
+ er2[0] += dr * dr * ref->count;
+ er2[1] += dg * dg * ref->count;
+ er2[2] += db * db * ref->count;
+ }
+
+ /* Define the best axis candidate for cutting the box */
box->major_axis = 1; // pick green by default (the color the eye is the most sensitive to)
- if (br >= rr && br >= gr) box->major_axis = 2;
- if (rr >= gr && rr >= br) box->major_axis = 0;
- if (gr >= rr && gr >= br) box->major_axis = 1; // prefer green again
+ if (er2[2] >= er2[0] && er2[2] >= er2[1]) box->major_axis = 2;
+ if (er2[0] >= er2[1] && er2[0] >= er2[2]) box->major_axis = 0;
+ if (er2[1] >= er2[0] && er2[1] >= er2[2]) box->major_axis = 1; // prefer green again
}
/**
diff --git a/tests/ref/fate/filter-palettegen-1 b/tests/ref/fate/filter-palettegen-1
index bebfd24e19..278d831846 100644
--- a/tests/ref/fate/filter-palettegen-1
+++ b/tests/ref/fate/filter-palettegen-1
@@ -3,4 +3,4 @@
#codec_id 0: rawvideo
#dimensions 0: 16x16
#sar 0: 1/1
-0, 0, 0, 1, 1024, 0x3395ef5a
+0, 0, 0, 1, 1024, 0x394ee723
diff --git a/tests/ref/fate/filter-palettegen-2 b/tests/ref/fate/filter-palettegen-2
index 9abec0fe8e..e9bc635c81 100644
--- a/tests/ref/fate/filter-palettegen-2
+++ b/tests/ref/fate/filter-palettegen-2
@@ -3,4 +3,4 @@
#codec_id 0: rawvideo
#dimensions 0: 16x16
#sar 0: 1/1
-0, 0, 0, 1, 1024, 0x23e072c8
+0, 0, 0, 1, 1024, 0xc54d773d
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 12/32] avfilter/palettegen: use box->major_axis without intermediate variable
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (10 preceding siblings ...)
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 11/32] avfilter/palettegen: define the best axis to cut using the squared error Clément Bœsch
@ 2022-12-27 23:17 ` Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 13/32] avfilter/palettegen: always compute the box variance Clément Bœsch
` (20 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:17 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
---
libavfilter/vf_palettegen.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index ed1448755c..aa0c8fdc5b 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -363,22 +363,21 @@ static AVFrame *get_palette_frame(AVFilterContext *ctx)
s->nb_boxes = 1;
while (box && box->len > 1) {
- int i, longest;
+ int i;
uint64_t median, box_weight;
compute_box_stats(s, box);
- longest = box->major_axis;
box_weight = box->weight;
ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64" sort by %c (already sorted:%c) ",
box_id, box->start, box->start + box->len - 1, box->len, box_weight,
- "rgb"[longest], box->sorted_by == longest ? 'y':'n');
+ "rgb"[box->major_axis], box->sorted_by == box->major_axis ? 'y':'n');
- /* sort the range by its longest axis if it's not already sorted */
- if (box->sorted_by != longest) {
- cmp_func cmpf = cmp_funcs[longest];
+ /* sort the range by its major axis if it's not already sorted */
+ if (box->sorted_by != box->major_axis) {
+ cmp_func cmpf = cmp_funcs[box->major_axis];
AV_QSORT(&s->refs[box->start], box->len, const struct color_ref *, cmpf);
- box->sorted_by = longest;
+ box->sorted_by = box->major_axis;
}
/* locate the median where to split */
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 13/32] avfilter/palettegen: always compute the box variance
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (11 preceding siblings ...)
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 12/32] avfilter/palettegen: use box->major_axis without intermediate variable Clément Bœsch
@ 2022-12-27 23:17 ` Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 14/32] avfilter/palettegen: rename variance to cut_score Clément Bœsch
` (19 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:17 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
The variance computation is simple enough now (since we can use the axis
squared errors) that it doesn't need to have a complex lazy computation
logic.
---
libavfilter/vf_palettegen.c | 42 ++++++++-----------------------------
1 file changed, 9 insertions(+), 33 deletions(-)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index aa0c8fdc5b..ca1e02444c 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -135,16 +135,6 @@ static int cmp_color(const void *a, const void *b)
return FFDIFFSIGN(box1->color , box2->color);
}
-static av_always_inline int diff(const uint32_t a, const uint32_t b)
-{
- const uint8_t c1[] = {a >> 16 & 0xff, a >> 8 & 0xff, a & 0xff};
- const uint8_t c2[] = {b >> 16 & 0xff, b >> 8 & 0xff, b & 0xff};
- const int dr = c1[0] - c2[0];
- const int dg = c1[1] - c2[1];
- const int db = c1[2] - c2[2];
- return dr*dr + dg*dg + db*db;
-}
-
static void compute_box_stats(PaletteGenContext *s, struct range_box *box)
{
int avg[3];
@@ -180,6 +170,8 @@ static void compute_box_stats(PaletteGenContext *s, struct range_box *box)
if (er2[2] >= er2[0] && er2[2] >= er2[1]) box->major_axis = 2;
if (er2[0] >= er2[1] && er2[0] >= er2[2]) box->major_axis = 0;
if (er2[1] >= er2[0] && er2[1] >= er2[2]) box->major_axis = 1; // prefer green again
+
+ box->variance = er2[0] + er2[1] + er2[2];
}
/**
@@ -187,7 +179,7 @@ static void compute_box_stats(PaletteGenContext *s, struct range_box *box)
*/
static int get_next_box_id_to_split(PaletteGenContext *s)
{
- int box_id, i, best_box_id = -1;
+ int box_id, best_box_id = -1;
int64_t max_variance = -1;
if (s->nb_boxes == s->max_colors - s->reserve_transparent)
@@ -195,24 +187,9 @@ static int get_next_box_id_to_split(PaletteGenContext *s)
for (box_id = 0; box_id < s->nb_boxes; box_id++) {
struct range_box *box = &s->boxes[box_id];
-
- if (s->boxes[box_id].len >= 2) {
-
- if (box->variance == -1) {
- int64_t variance = 0;
-
- 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;
- }
- box->variance = variance;
- }
- if (box->variance > max_variance) {
- best_box_id = box_id;
- max_variance = box->variance;
- }
- } else {
- box->variance = -1;
+ if (s->boxes[box_id].len >= 2 && box->variance > max_variance) {
+ best_box_id = box_id;
+ max_variance = box->variance;
}
}
return best_box_id;
@@ -261,8 +238,8 @@ static void split_box(PaletteGenContext *s, struct range_box *box, int n)
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;
+ compute_box_stats(s, box);
+ compute_box_stats(s, new_box);
}
/**
@@ -359,14 +336,13 @@ static AVFrame *get_palette_frame(AVFilterContext *ctx)
box->len = s->nb_refs;
box->sorted_by = -1;
box->color = get_avg_color(s->refs, box);
- box->variance = -1;
+ compute_box_stats(s, box);
s->nb_boxes = 1;
while (box && box->len > 1) {
int i;
uint64_t median, box_weight;
- compute_box_stats(s, box);
box_weight = box->weight;
ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64" sort by %c (already sorted:%c) ",
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 14/32] avfilter/palettegen: rename variance to cut_score
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (12 preceding siblings ...)
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 13/32] avfilter/palettegen: always compute the box variance Clément Bœsch
@ 2022-12-27 23:17 ` Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 15/32] avfilter/palettegen: change cut score from ∑e² to max e² Clément Bœsch
` (18 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:17 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
"Variance" wasn't exactly the correct word; "cut score" is more
agnostic, which will be useful when changing the algorithm in the next
commit.
---
libavfilter/vf_palettegen.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index ca1e02444c..7ecb1211ba 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -42,7 +42,7 @@ struct range_box {
uint32_t color; // average color
int major_axis; // best axis candidate for cutting the box
uint64_t weight; // sum of all the weights of the colors
- int64_t variance; // overall variance of the box (how much the colors are spread)
+ int64_t cut_score; // how likely the box is to be cut down (higher implying more likely)
int start; // index in PaletteGenContext->refs
int len; // number of referenced colors
int sorted_by; // whether range of colors is sorted by red (0), green (1) or blue (2)
@@ -171,25 +171,25 @@ static void compute_box_stats(PaletteGenContext *s, struct range_box *box)
if (er2[0] >= er2[1] && er2[0] >= er2[2]) box->major_axis = 0;
if (er2[1] >= er2[0] && er2[1] >= er2[2]) box->major_axis = 1; // prefer green again
- box->variance = er2[0] + er2[1] + er2[2];
+ box->cut_score = er2[0] + er2[1] + er2[2];
}
/**
- * Find the next box to split: pick the one with the highest variance
+ * Find the next box to split: pick the one with the highest cut score
*/
static int get_next_box_id_to_split(PaletteGenContext *s)
{
int box_id, best_box_id = -1;
- int64_t max_variance = -1;
+ int64_t max_score = -1;
if (s->nb_boxes == s->max_colors - s->reserve_transparent)
return -1;
for (box_id = 0; box_id < s->nb_boxes; box_id++) {
struct range_box *box = &s->boxes[box_id];
- if (s->boxes[box_id].len >= 2 && box->variance > max_variance) {
+ if (s->boxes[box_id].len >= 2 && box->cut_score > max_score) {
best_box_id = box_id;
- max_variance = box->variance;
+ max_score = box->cut_score;
}
}
return best_box_id;
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 15/32] avfilter/palettegen: change cut score from ∑e² to max e²
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (13 preceding siblings ...)
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 14/32] avfilter/palettegen: rename variance to cut_score Clément Bœsch
@ 2022-12-27 23:17 ` Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 16/32] avfilter/palettegen: compute average color within compute_box_stats() Clément Bœsch
` (17 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:17 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
This is following the results from personal research¹.
¹: https://github.com/ubitux/research/tree/main/color-quantization#results
---
libavfilter/vf_palettegen.c | 3 ++-
tests/ref/fate/filter-palettegen-1 | 2 +-
tests/ref/fate/filter-palettegen-2 | 2 +-
3 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index 7ecb1211ba..00b5f88c49 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -171,7 +171,8 @@ static void compute_box_stats(PaletteGenContext *s, struct range_box *box)
if (er2[0] >= er2[1] && er2[0] >= er2[2]) box->major_axis = 0;
if (er2[1] >= er2[0] && er2[1] >= er2[2]) box->major_axis = 1; // prefer green again
- box->cut_score = er2[0] + er2[1] + er2[2];
+ /* The box that has the axis with the biggest error amongst all boxes will but cut down */
+ box->cut_score = FFMAX3(er2[0], er2[1], er2[2]);
}
/**
diff --git a/tests/ref/fate/filter-palettegen-1 b/tests/ref/fate/filter-palettegen-1
index 278d831846..57be338b42 100644
--- a/tests/ref/fate/filter-palettegen-1
+++ b/tests/ref/fate/filter-palettegen-1
@@ -3,4 +3,4 @@
#codec_id 0: rawvideo
#dimensions 0: 16x16
#sar 0: 1/1
-0, 0, 0, 1, 1024, 0x394ee723
+0, 0, 0, 1, 1024, 0x21c6e6c4
diff --git a/tests/ref/fate/filter-palettegen-2 b/tests/ref/fate/filter-palettegen-2
index e9bc635c81..bcdf54af95 100644
--- a/tests/ref/fate/filter-palettegen-2
+++ b/tests/ref/fate/filter-palettegen-2
@@ -3,4 +3,4 @@
#codec_id 0: rawvideo
#dimensions 0: 16x16
#sar 0: 1/1
-0, 0, 0, 1, 1024, 0xc54d773d
+0, 0, 0, 1, 1024, 0x630d76b1
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 16/32] avfilter/palettegen: compute average color within compute_box_stats()
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (14 preceding siblings ...)
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 15/32] avfilter/palettegen: change cut score from ∑e² to max e² Clément Bœsch
@ 2022-12-27 23:17 ` Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 17/32] avfilter/palettegen: misc cosmetics Clément Bœsch
` (16 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:17 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
---
libavfilter/vf_palettegen.c | 30 +-----------------------------
1 file changed, 1 insertion(+), 29 deletions(-)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index 00b5f88c49..36f0a976d9 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -153,6 +153,7 @@ static void compute_box_stats(PaletteGenContext *s, struct range_box *box)
avg[0] = sr / box->weight;
avg[1] = sg / box->weight;
avg[2] = sb / box->weight;
+ box->color = 0xffU<<24 | avg[0]<<16 | avg[1]<<8 | avg[2];
/* Compute squared error of each color channel */
for (int i = box->start; i < box->start + box->len; i++) {
@@ -196,32 +197,6 @@ static int get_next_box_id_to_split(PaletteGenContext *s)
return best_box_id;
}
-/**
- * Get the 32-bit average color for the range of RGB colors enclosed in the
- * 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 i;
- const int n = box->len;
- 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];
- r += (ref->color >> 16 & 0xff) * ref->count;
- g += (ref->color >> 8 & 0xff) * ref->count;
- b += (ref->color & 0xff) * ref->count;
- div += ref->count;
- }
-
- r = r / div;
- g = g / div;
- b = b / div;
-
- return 0xffU<<24 | r<<16 | g<<8 | b;
-}
-
/**
* Split given box in two at position n. The original box becomes the left part
* of the split, and the new index box is the right part.
@@ -237,8 +212,6 @@ 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);
compute_box_stats(s, box);
compute_box_stats(s, new_box);
}
@@ -336,7 +309,6 @@ 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);
compute_box_stats(s, box);
s->nb_boxes = 1;
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 17/32] avfilter/palettegen: misc cosmetics
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (15 preceding siblings ...)
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 16/32] avfilter/palettegen: compute average color within compute_box_stats() Clément Bœsch
@ 2022-12-27 23:17 ` Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 18/32] avfilter/palettegen: rename local variable box_weight to weight Clément Bœsch
` (15 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:17 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
---
libavfilter/vf_palettegen.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index 36f0a976d9..ad21882df3 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -132,7 +132,7 @@ static int cmp_color(const void *a, const void *b)
{
const struct range_box *box1 = a;
const struct range_box *box2 = b;
- return FFDIFFSIGN(box1->color , box2->color);
+ return FFDIFFSIGN(box1->color, box2->color);
}
static void compute_box_stats(PaletteGenContext *s, struct range_box *box)
@@ -188,7 +188,7 @@ static int get_next_box_id_to_split(PaletteGenContext *s)
return -1;
for (box_id = 0; box_id < s->nb_boxes; box_id++) {
- struct range_box *box = &s->boxes[box_id];
+ const struct range_box *box = &s->boxes[box_id];
if (s->boxes[box_id].len >= 2 && box->cut_score > max_score) {
best_box_id = box_id;
max_score = box->cut_score;
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 18/32] avfilter/palettegen: rename local variable box_weight to weight
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (16 preceding siblings ...)
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 17/32] avfilter/palettegen: misc cosmetics Clément Bœsch
@ 2022-12-27 23:18 ` Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 19/32] avfilter/palettegen: switch to signed arithmetic Clément Bœsch
` (14 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:18 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
This variable is used only for the running weight (used to reach the
target median). The places where we actually need the box weight are
changed to use box->weight.
---
libavfilter/vf_palettegen.c | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index ad21882df3..b8db234fef 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -314,12 +314,10 @@ static AVFrame *get_palette_frame(AVFilterContext *ctx)
while (box && box->len > 1) {
int i;
- uint64_t median, box_weight;
-
- box_weight = box->weight;
+ uint64_t median, weight;
ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64" sort by %c (already sorted:%c) ",
- box_id, box->start, box->start + box->len - 1, box->len, box_weight,
+ box_id, box->start, box->start + box->len - 1, box->len, box->weight,
"rgb"[box->major_axis], box->sorted_by == box->major_axis ? 'y':'n');
/* sort the range by its major axis if it's not already sorted */
@@ -330,16 +328,16 @@ static AVFrame *get_palette_frame(AVFilterContext *ctx)
}
/* locate the median where to split */
- median = (box_weight + 1) >> 1;
- box_weight = 0;
+ median = (box->weight + 1) >> 1;
+ weight = 0;
/* if you have 2 boxes, the maximum is actually #0: you must have at
* least 1 color on each side of the split, hence the -2 */
for (i = box->start; i < box->start + box->len - 2; i++) {
- box_weight += s->refs[i]->count;
- if (box_weight > median)
+ weight += s->refs[i]->count;
+ if (weight > median)
break;
}
- ff_dlog(ctx, "split @ i=%-6d with w=%-6"PRIu64" (target=%6"PRIu64")\n", i, box_weight, median);
+ ff_dlog(ctx, "split @ i=%-6d with w=%-6"PRIu64" (target=%6"PRIu64")\n", i, weight, median);
split_box(s, box, i);
box_id = get_next_box_id_to_split(s);
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 19/32] avfilter/palettegen: switch to signed arithmetic
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (17 preceding siblings ...)
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 18/32] avfilter/palettegen: rename local variable box_weight to weight Clément Bœsch
@ 2022-12-27 23:18 ` Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 20/32] avfilter/palettegen: base box split decision on a perceptual model Clément Bœsch
` (13 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:18 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
This prevents mixed sign arithmetic (typically because we have signed
color channel differences), which has nasty side effects in C.
---
libavfilter/vf_palettegen.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index b8db234fef..99e4512e52 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -34,14 +34,14 @@
/* Reference a color and how much it's used */
struct color_ref {
uint32_t color;
- uint64_t count;
+ int64_t count;
};
/* Store a range of colors */
struct range_box {
uint32_t color; // average color
int major_axis; // best axis candidate for cutting the box
- uint64_t weight; // sum of all the weights of the colors
+ int64_t weight; // sum of all the weights of the colors
int64_t cut_score; // how likely the box is to be cut down (higher implying more likely)
int start; // index in PaletteGenContext->refs
int len; // number of referenced colors
@@ -141,7 +141,7 @@ static void compute_box_stats(PaletteGenContext *s, struct range_box *box)
int64_t er2[3] = {0};
/* Compute average color */
- uint64_t sr = 0, sg = 0, sb = 0;
+ int64_t sr = 0, sg = 0, sb = 0;
box->weight = 0;
for (int i = box->start; i < box->start + box->len; i++) {
const struct color_ref *ref = s->refs[i];
@@ -314,7 +314,7 @@ static AVFrame *get_palette_frame(AVFilterContext *ctx)
while (box && box->len > 1) {
int i;
- uint64_t median, weight;
+ int64_t median, weight;
ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64" sort by %c (already sorted:%c) ",
box_id, box->start, box->start + box->len - 1, box->len, box->weight,
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 20/32] avfilter/palettegen: base box split decision on a perceptual model
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (18 preceding siblings ...)
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 19/32] avfilter/palettegen: switch to signed arithmetic Clément Bœsch
@ 2022-12-27 23:18 ` Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 21/32] avfilter/palettegen: add a warning about supporting only sRGB Clément Bœsch
` (12 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:18 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
Similar to the change in paletteuse, we rely on a perceptual model to
decide how and where to split the box.
---
libavfilter/Makefile | 2 +-
libavfilter/vf_palettegen.c | 48 ++++++++++++++++--------------
tests/ref/fate/filter-palettegen-1 | 2 +-
tests/ref/fate/filter-palettegen-2 | 2 +-
4 files changed, 29 insertions(+), 25 deletions(-)
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index c3d13e5a26..5783be281d 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -403,7 +403,7 @@ OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER) += vf_overlay_vulkan.o vulkan.o vul
OBJS-$(CONFIG_OWDENOISE_FILTER) += vf_owdenoise.o
OBJS-$(CONFIG_PAD_FILTER) += vf_pad.o
OBJS-$(CONFIG_PAD_OPENCL_FILTER) += vf_pad_opencl.o opencl.o opencl/pad.o
-OBJS-$(CONFIG_PALETTEGEN_FILTER) += vf_palettegen.o
+OBJS-$(CONFIG_PALETTEGEN_FILTER) += vf_palettegen.o palette.o
OBJS-$(CONFIG_PALETTEUSE_FILTER) += vf_paletteuse.o framesync.o palette.o
OBJS-$(CONFIG_PERMS_FILTER) += f_perms.o
OBJS-$(CONFIG_PERSPECTIVE_FILTER) += vf_perspective.o
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index 99e4512e52..3178c43ab9 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -30,16 +30,19 @@
#include "libavutil/intreadwrite.h"
#include "avfilter.h"
#include "internal.h"
+#include "palette.h"
/* Reference a color and how much it's used */
struct color_ref {
uint32_t color;
+ struct Lab lab;
int64_t count;
};
/* Store a range of colors */
struct range_box {
uint32_t color; // average color
+ struct Lab avg; // average color in perceptual OkLab space
int major_axis; // best axis candidate for cutting the box
int64_t weight; // sum of all the weights of the colors
int64_t cut_score; // how likely the box is to be cut down (higher implying more likely)
@@ -115,15 +118,14 @@ 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 FFDIFFSIGN((*a)->lab.name, (*b)->lab.name); \
}
-DECLARE_CMP_FUNC(r, 0)
-DECLARE_CMP_FUNC(g, 1)
+DECLARE_CMP_FUNC(L, 0)
+DECLARE_CMP_FUNC(a, 1)
DECLARE_CMP_FUNC(b, 2)
-static const cmp_func cmp_funcs[] = {cmp_r, cmp_g, cmp_b};
+static const cmp_func cmp_funcs[] = {cmp_L, cmp_a, cmp_b};
/**
* Simple color comparison for sorting the final palette
@@ -137,40 +139,38 @@ static int cmp_color(const void *a, const void *b)
static void compute_box_stats(PaletteGenContext *s, struct range_box *box)
{
- int avg[3];
int64_t er2[3] = {0};
/* Compute average color */
- int64_t sr = 0, sg = 0, sb = 0;
+ int64_t sL = 0, sa = 0, sb = 0;
box->weight = 0;
for (int i = box->start; i < box->start + box->len; i++) {
const struct color_ref *ref = s->refs[i];
- sr += (ref->color >> 16 & 0xff) * ref->count;
- sg += (ref->color >> 8 & 0xff) * ref->count;
- sb += (ref->color & 0xff) * ref->count;
+ sL += ref->lab.L * ref->count;
+ sa += ref->lab.a * ref->count;
+ sb += ref->lab.b * ref->count;
box->weight += ref->count;
}
- avg[0] = sr / box->weight;
- avg[1] = sg / box->weight;
- avg[2] = sb / box->weight;
- box->color = 0xffU<<24 | avg[0]<<16 | avg[1]<<8 | avg[2];
+ box->avg.L = sL / box->weight;
+ box->avg.a = sa / box->weight;
+ box->avg.b = sb / box->weight;
/* Compute squared error of each color channel */
for (int i = box->start; i < box->start + box->len; i++) {
const struct color_ref *ref = s->refs[i];
- const int64_t dr = (int)(ref->color >> 16 & 0xff) - avg[0];
- const int64_t dg = (int)(ref->color >> 8 & 0xff) - avg[1];
- const int64_t db = (int)(ref->color & 0xff) - avg[2];
- er2[0] += dr * dr * ref->count;
- er2[1] += dg * dg * ref->count;
+ const int64_t dL = ref->lab.L - box->avg.L;
+ const int64_t da = ref->lab.a - box->avg.a;
+ const int64_t db = ref->lab.b - box->avg.b;
+ er2[0] += dL * dL * ref->count;
+ er2[1] += da * da * ref->count;
er2[2] += db * db * ref->count;
}
/* Define the best axis candidate for cutting the box */
- box->major_axis = 1; // pick green by default (the color the eye is the most sensitive to)
+ box->major_axis = 0;
if (er2[2] >= er2[0] && er2[2] >= er2[1]) box->major_axis = 2;
+ if (er2[1] >= er2[0] && er2[1] >= er2[2]) box->major_axis = 1;
if (er2[0] >= er2[1] && er2[0] >= er2[2]) box->major_axis = 0;
- if (er2[1] >= er2[0] && er2[1] >= er2[2]) box->major_axis = 1; // prefer green again
/* The box that has the axis with the biggest error amongst all boxes will but cut down */
box->cut_score = FFMAX3(er2[0], er2[1], er2[2]);
@@ -318,7 +318,7 @@ static AVFrame *get_palette_frame(AVFilterContext *ctx)
ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64" sort by %c (already sorted:%c) ",
box_id, box->start, box->start + box->len - 1, box->len, box->weight,
- "rgb"[box->major_axis], box->sorted_by == box->major_axis ? 'y':'n');
+ "Lab"[box->major_axis], box->sorted_by == box->major_axis ? 'y':'n');
/* sort the range by its major axis if it's not already sorted */
if (box->sorted_by != box->major_axis) {
@@ -348,6 +348,9 @@ static AVFrame *get_palette_frame(AVFilterContext *ctx)
av_log(ctx, AV_LOG_INFO, "%d%s colors generated out of %d colors; ratio=%f\n",
s->nb_boxes, s->reserve_transparent ? "(+1)" : "", s->nb_refs, ratio);
+ for (int i = 0; i < s->nb_boxes; i++)
+ s->boxes[i].color = 0xffU<<24 | ff_oklab_int_to_srgb_u8(s->boxes[i].avg);
+
qsort(s->boxes, s->nb_boxes, sizeof(*s->boxes), cmp_color);
write_palette(ctx, out);
@@ -392,6 +395,7 @@ static int color_inc(struct hist_node *hist, uint32_t color)
if (!e)
return AVERROR(ENOMEM);
e->color = color;
+ e->lab = ff_srgb_u8_to_oklab_int(color);
e->count = 1;
return 1;
}
diff --git a/tests/ref/fate/filter-palettegen-1 b/tests/ref/fate/filter-palettegen-1
index 57be338b42..bae6b7064b 100644
--- a/tests/ref/fate/filter-palettegen-1
+++ b/tests/ref/fate/filter-palettegen-1
@@ -3,4 +3,4 @@
#codec_id 0: rawvideo
#dimensions 0: 16x16
#sar 0: 1/1
-0, 0, 0, 1, 1024, 0x21c6e6c4
+0, 0, 0, 1, 1024, 0xbb5cde01
diff --git a/tests/ref/fate/filter-palettegen-2 b/tests/ref/fate/filter-palettegen-2
index bcdf54af95..7217de3a92 100644
--- a/tests/ref/fate/filter-palettegen-2
+++ b/tests/ref/fate/filter-palettegen-2
@@ -3,4 +3,4 @@
#codec_id 0: rawvideo
#dimensions 0: 16x16
#sar 0: 1/1
-0, 0, 0, 1, 1024, 0x630d76b1
+0, 0, 0, 1, 1024, 0xfbf66e70
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 21/32] avfilter/palettegen: add a warning about supporting only sRGB
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (19 preceding siblings ...)
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 20/32] avfilter/palettegen: base box split decision on a perceptual model Clément Bœsch
@ 2022-12-27 23:18 ` Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 22/32] avfilter/palettegen: make refs order deterministic Clément Bœsch
` (11 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:18 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
---
libavfilter/vf_palettegen.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index 3178c43ab9..ba81739d27 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -451,9 +451,13 @@ 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)
- : update_histogram_frame(s->histogram, in);
+ int ret;
+
+ if (in->color_trc != AVCOL_TRC_UNSPECIFIED && in->color_trc != AVCOL_TRC_IEC61966_2_1)
+ av_log(ctx, AV_LOG_WARNING, "The input frame is not in sRGB, colors may be off\n");
+ 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;
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 22/32] avfilter/palettegen: make refs order deterministic
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (20 preceding siblings ...)
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 21/32] avfilter/palettegen: add a warning about supporting only sRGB Clément Bœsch
@ 2022-12-27 23:18 ` Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 23/32] avfilter/palettegen: use libc qsort Clément Bœsch
` (10 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:18 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
Currently, in case of equality on the first color channel, the order of
the ref colors is defined by the hashing function. This commit makes the
sorting deterministic and improve the hierarchical ordering.
---
libavfilter/vf_palettegen.c | 61 ++++++++++++++++++++++--------
tests/ref/fate/filter-palettegen-1 | 2 +-
tests/ref/fate/filter-palettegen-2 | 2 +-
3 files changed, 47 insertions(+), 18 deletions(-)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index ba81739d27..784e81b875 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -113,19 +113,51 @@ static int query_formats(AVFilterContext *ctx)
typedef int (*cmp_func)(const void *, const void *);
-#define DECLARE_CMP_FUNC(name, pos) \
-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 FFDIFFSIGN((*a)->lab.name, (*b)->lab.name); \
+#define DECLARE_CMP_FUNC(k0, k1, k2) \
+static int cmp_##k0##k1##k2(const void *pa, const void *pb) \
+{ \
+ const struct color_ref * const *a = pa; \
+ const struct color_ref * const *b = pb; \
+ const int c0 = FFDIFFSIGN((*a)->lab.k0, (*b)->lab.k0); \
+ const int c1 = FFDIFFSIGN((*a)->lab.k1, (*b)->lab.k1); \
+ const int c2 = FFDIFFSIGN((*a)->lab.k2, (*b)->lab.k2); \
+ return c0 ? c0 : c1 ? c1 : c2; \
}
-DECLARE_CMP_FUNC(L, 0)
-DECLARE_CMP_FUNC(a, 1)
-DECLARE_CMP_FUNC(b, 2)
+DECLARE_CMP_FUNC(L, a, b)
+DECLARE_CMP_FUNC(L, b, a)
+DECLARE_CMP_FUNC(a, L, b)
+DECLARE_CMP_FUNC(a, b, L)
+DECLARE_CMP_FUNC(b, L, a)
+DECLARE_CMP_FUNC(b, a, L)
+
+enum { ID_XYZ, ID_XZY, ID_ZXY, ID_YXZ, ID_ZYX, ID_YZX };
+static const char * const sortstr[] = { "Lab", "Lba", "bLa", "aLb", "baL", "abL" };
+
+static const cmp_func cmp_funcs[] = {
+ [ID_XYZ] = cmp_Lab,
+ [ID_XZY] = cmp_Lba,
+ [ID_ZXY] = cmp_bLa,
+ [ID_YXZ] = cmp_aLb,
+ [ID_ZYX] = cmp_baL,
+ [ID_YZX] = cmp_abL,
+};
-static const cmp_func cmp_funcs[] = {cmp_L, cmp_a, cmp_b};
+/*
+ * Return an identifier for the order of x, y, z (from higher to lower),
+ * preferring x over y and y over z in case of equality.
+ */
+static int sort3id(int64_t x, int64_t y, int64_t z)
+{
+ if (x >= y) {
+ if (y >= z) return ID_XYZ;
+ if (x >= z) return ID_XZY;
+ return ID_ZXY;
+ }
+ if (x >= z) return ID_YXZ;
+ if (y >= z) return ID_YZX;
+ return ID_ZYX;
+}
/**
* Simple color comparison for sorting the final palette
@@ -167,10 +199,7 @@ static void compute_box_stats(PaletteGenContext *s, struct range_box *box)
}
/* Define the best axis candidate for cutting the box */
- box->major_axis = 0;
- if (er2[2] >= er2[0] && er2[2] >= er2[1]) box->major_axis = 2;
- if (er2[1] >= er2[0] && er2[1] >= er2[2]) box->major_axis = 1;
- if (er2[0] >= er2[1] && er2[0] >= er2[2]) box->major_axis = 0;
+ box->major_axis = sort3id(er2[0], er2[1], er2[2]);
/* The box that has the axis with the biggest error amongst all boxes will but cut down */
box->cut_score = FFMAX3(er2[0], er2[1], er2[2]);
@@ -316,9 +345,9 @@ static AVFrame *get_palette_frame(AVFilterContext *ctx)
int i;
int64_t median, weight;
- ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64" sort by %c (already sorted:%c) ",
+ ff_dlog(ctx, "box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64" sort by %s (already sorted:%c) ",
box_id, box->start, box->start + box->len - 1, box->len, box->weight,
- "Lab"[box->major_axis], box->sorted_by == box->major_axis ? 'y':'n');
+ sortstr[box->major_axis], box->sorted_by == box->major_axis ? 'y':'n');
/* sort the range by its major axis if it's not already sorted */
if (box->sorted_by != box->major_axis) {
diff --git a/tests/ref/fate/filter-palettegen-1 b/tests/ref/fate/filter-palettegen-1
index bae6b7064b..1e5c9ee002 100644
--- a/tests/ref/fate/filter-palettegen-1
+++ b/tests/ref/fate/filter-palettegen-1
@@ -3,4 +3,4 @@
#codec_id 0: rawvideo
#dimensions 0: 16x16
#sar 0: 1/1
-0, 0, 0, 1, 1024, 0xbb5cde01
+0, 0, 0, 1, 1024, 0xa285dd77
diff --git a/tests/ref/fate/filter-palettegen-2 b/tests/ref/fate/filter-palettegen-2
index 7217de3a92..c1fc64e13d 100644
--- a/tests/ref/fate/filter-palettegen-2
+++ b/tests/ref/fate/filter-palettegen-2
@@ -3,4 +3,4 @@
#codec_id 0: rawvideo
#dimensions 0: 16x16
#sar 0: 1/1
-0, 0, 0, 1, 1024, 0xfbf66e70
+0, 0, 0, 1, 1024, 0xf2286e18
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 23/32] avfilter/palettegen: use libc qsort
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (21 preceding siblings ...)
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 22/32] avfilter/palettegen: make refs order deterministic Clément Bœsch
@ 2022-12-27 23:18 ` Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 24/32] avfilter/palette{gen, use}: update Copyright after recent changes Clément Bœsch
` (9 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:18 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
Now that the sort function is deterministic, we can rely on the libc
sorting function.
---
libavfilter/vf_palettegen.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index 784e81b875..507690fa82 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -26,7 +26,6 @@
#include "libavutil/avassert.h"
#include "libavutil/internal.h"
#include "libavutil/opt.h"
-#include "libavutil/qsort.h"
#include "libavutil/intreadwrite.h"
#include "avfilter.h"
#include "internal.h"
@@ -352,7 +351,7 @@ static AVFrame *get_palette_frame(AVFilterContext *ctx)
/* sort the range by its major axis if it's not already sorted */
if (box->sorted_by != box->major_axis) {
cmp_func cmpf = cmp_funcs[box->major_axis];
- AV_QSORT(&s->refs[box->start], box->len, const struct color_ref *, cmpf);
+ qsort(&s->refs[box->start], box->len, sizeof(struct color_ref *), cmpf);
box->sorted_by = box->major_axis;
}
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 24/32] avfilter/palette{gen, use}: update Copyright after recent changes
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (22 preceding siblings ...)
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 23/32] avfilter/palettegen: use libc qsort Clément Bœsch
@ 2022-12-27 23:18 ` Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 25/32] avfilter/palette: add lowbias32 hashing Clément Bœsch
` (8 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:18 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
---
libavfilter/vf_palettegen.c | 1 +
libavfilter/vf_paletteuse.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index 507690fa82..6301cf6358 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2015 Stupeflix
+ * Copyright (c) 2022 Clément Bœsch <u pkh me>
*
* This file is part of FFmpeg.
*
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index 3af121b1eb..c32b73ab9a 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2015 Stupeflix
+ * Copyright (c) 2022 Clément Bœsch <u pkh me>
*
* This file is part of FFmpeg.
*
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 25/32] avfilter/palette: add lowbias32 hashing
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (23 preceding siblings ...)
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 24/32] avfilter/palette{gen, use}: update Copyright after recent changes Clément Bœsch
@ 2022-12-27 23:18 ` Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 26/32] avfilter/palettegen: use lowbias32 for color hashing Clément Bœsch
` (7 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:18 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
---
libavfilter/palette.c | 10 ++++++++++
libavfilter/palette.h | 5 +++++
2 files changed, 15 insertions(+)
diff --git a/libavfilter/palette.c b/libavfilter/palette.c
index 03e48fc71e..e21ab6ff4d 100644
--- a/libavfilter/palette.c
+++ b/libavfilter/palette.c
@@ -208,3 +208,13 @@ uint32_t ff_oklab_int_to_srgb_u8(struct Lab c)
return r<<16 | g<<8 | b;
}
+
+uint32_t ff_lowbias32(uint32_t x)
+{
+ x ^= x >> 16;
+ x *= 0x7feb352d;
+ x ^= x >> 15;
+ x *= 0x846ca68b;
+ x ^= x >> 16;
+ return x;
+}
diff --git a/libavfilter/palette.h b/libavfilter/palette.h
index 6839bf6fc6..d3acc854ba 100644
--- a/libavfilter/palette.h
+++ b/libavfilter/palette.h
@@ -55,4 +55,9 @@ struct Lab ff_srgb_u8_to_oklab_int(uint32_t srgb);
*/
uint32_t ff_oklab_int_to_srgb_u8(struct Lab c);
+/*
+ * lowbias32 hashing from https://nullprogram.com/blog/2018/07/31/
+ */
+uint32_t ff_lowbias32(uint32_t x);
+
#endif /* AVFILTER_PALETTE_H */
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 26/32] avfilter/palettegen: use lowbias32 for color hashing
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (24 preceding siblings ...)
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 25/32] avfilter/palette: add lowbias32 hashing Clément Bœsch
@ 2022-12-27 23:18 ` Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 27/32] avfilter/paletteuse: " Clément Bœsch
` (6 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:18 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
1.12x faster overall in palettegen on my machine.
---
libavfilter/vf_palettegen.c | 19 ++-----------------
1 file changed, 2 insertions(+), 17 deletions(-)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index 6301cf6358..97e12f7274 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -63,8 +63,7 @@ enum {
NB_STATS_MODE
};
-#define NBITS 5
-#define HIST_SIZE (1<<(3*NBITS))
+#define HIST_SIZE (1<<15)
typedef struct PaletteGenContext {
const AVClass *class;
@@ -387,27 +386,13 @@ static AVFrame *get_palette_frame(AVFilterContext *ctx)
return out;
}
-/**
- * Hashing function for the color.
- * 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)
-{
- 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);
-
- 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 i;
- const unsigned hash = color_hash(color);
+ const uint32_t hash = ff_lowbias32(color) & (HIST_SIZE - 1);
struct hist_node *node = &hist[hash];
struct color_ref *e;
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 27/32] avfilter/paletteuse: use lowbias32 for color hashing
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (25 preceding siblings ...)
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 26/32] avfilter/palettegen: use lowbias32 for color hashing Clément Bœsch
@ 2022-12-27 23:18 ` Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 28/32] avfilter/paletteuse: switch to recursive method Clément Bœsch
` (5 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:18 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
Impact is more negligible than previous commit but still faster (1.02x).
---
libavfilter/vf_paletteuse.c | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index c32b73ab9a..c2d6333662 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -70,8 +70,7 @@ struct color_node {
int left_id, right_id;
};
-#define NBITS 5
-#define CACHE_SIZE (1<<(3*NBITS))
+#define CACHE_SIZE (1<<15)
struct cached_color {
uint32_t color;
@@ -346,10 +345,7 @@ static av_always_inline int color_get(PaletteUseContext *s, uint32_t color,
{
int i;
struct color_info clrinfo;
- const uint8_t rhash = (color>>16) & ((1<<NBITS)-1);
- const uint8_t ghash = (color>> 8) & ((1<<NBITS)-1);
- const uint8_t bhash = color & ((1<<NBITS)-1);
- const unsigned hash = rhash<<(NBITS*2) | ghash<<NBITS | bhash;
+ const uint32_t hash = ff_lowbias32(color) & (CACHE_SIZE - 1);
struct cache_node *node = &s->cache[hash];
struct cached_color *e;
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 28/32] avfilter/paletteuse: switch to recursive method
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (26 preceding siblings ...)
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 27/32] avfilter/paletteuse: " Clément Bœsch
@ 2022-12-27 23:18 ` Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 29/32] avfilter/paletteuse: remove alternative search methods Clément Bœsch
` (4 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:18 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
It appears faster than the iterative method on my machine (1.06x
faster), so I'm guessing compilers improved over time (the iterative
version was slightly faster in the past).
---
libavfilter/vf_paletteuse.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index c2d6333662..0342002b15 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -130,7 +130,7 @@ static const AVOption paletteuse_options[] = {
/* 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 },
- { "color_search", "set reverse colormap color search method", OFFSET(color_search_method), AV_OPT_TYPE_INT, {.i64=COLOR_SEARCH_NNS_ITERATIVE}, 0, NB_COLOR_SEARCHES-1, FLAGS, "search" },
+ { "color_search", "set reverse colormap color search method", OFFSET(color_search_method), AV_OPT_TYPE_INT, {.i64=COLOR_SEARCH_NNS_RECURSIVE}, 0, NB_COLOR_SEARCHES-1, FLAGS, "search" },
{ "nns_iterative", "iterative search", 0, AV_OPT_TYPE_CONST, {.i64=COLOR_SEARCH_NNS_ITERATIVE}, INT_MIN, INT_MAX, FLAGS, "search" },
{ "nns_recursive", "recursive search", 0, AV_OPT_TYPE_CONST, {.i64=COLOR_SEARCH_NNS_RECURSIVE}, INT_MIN, INT_MAX, FLAGS, "search" },
{ "bruteforce", "brute-force into the palette", 0, AV_OPT_TYPE_CONST, {.i64=COLOR_SEARCH_BRUTEFORCE}, INT_MIN, INT_MAX, FLAGS, "search" },
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 29/32] avfilter/paletteuse: remove alternative search methods
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (27 preceding siblings ...)
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 28/32] avfilter/paletteuse: switch to recursive method Clément Bœsch
@ 2022-12-27 23:18 ` Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 30/32] avfilter/paletteuse: remove mean error tool Clément Bœsch
` (3 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:18 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
This is a maintenance pain more than anything. It appears to make the
code slightly faster as a side effect.
---
libavfilter/vf_paletteuse.c | 220 +++++-------------------------------
1 file changed, 31 insertions(+), 189 deletions(-)
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index 0342002b15..690422a842 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -45,13 +45,6 @@ enum dithering_mode {
NB_DITHERING
};
-enum color_search_method {
- COLOR_SEARCH_NNS_ITERATIVE,
- COLOR_SEARCH_NNS_RECURSIVE,
- COLOR_SEARCH_BRUTEFORCE,
- NB_COLOR_SEARCHES
-};
-
enum diff_mode {
DIFF_MODE_NONE,
DIFF_MODE_RECTANGLE,
@@ -107,10 +100,8 @@ typedef struct PaletteUseContext {
/* debug options */
char *dot_filename;
- int color_search_method;
int calc_mean_err;
uint64_t total_mean_err;
- int debug_accuracy;
} PaletteUseContext;
#define OFFSET(x) offsetof(PaletteUseContext, x)
@@ -130,12 +121,7 @@ static const AVOption paletteuse_options[] = {
/* 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 },
- { "color_search", "set reverse colormap color search method", OFFSET(color_search_method), AV_OPT_TYPE_INT, {.i64=COLOR_SEARCH_NNS_RECURSIVE}, 0, NB_COLOR_SEARCHES-1, FLAGS, "search" },
- { "nns_iterative", "iterative search", 0, AV_OPT_TYPE_CONST, {.i64=COLOR_SEARCH_NNS_ITERATIVE}, INT_MIN, INT_MAX, FLAGS, "search" },
- { "nns_recursive", "recursive search", 0, AV_OPT_TYPE_CONST, {.i64=COLOR_SEARCH_NNS_RECURSIVE}, INT_MIN, INT_MAX, FLAGS, "search" },
- { "bruteforce", "brute-force into the palette", 0, AV_OPT_TYPE_CONST, {.i64=COLOR_SEARCH_BRUTEFORCE}, INT_MIN, INT_MAX, FLAGS, "search" },
{ "mean_err", "compute and print mean error", OFFSET(calc_mean_err), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
- { "debug_accuracy", "test color search accuracy", OFFSET(debug_accuracy), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
{ NULL }
};
@@ -193,26 +179,6 @@ static struct color_info get_color_from_srgb(uint32_t srgb)
return ret;
}
-static av_always_inline uint8_t colormap_nearest_bruteforce(const uint32_t *palette, const struct color_info *target, const int trans_thresh)
-{
- int i, pal_id = -1, min_dist = INT_MAX;
-
- for (i = 0; i < AVPALETTE_COUNT; i++) {
- const uint32_t c = palette[i];
-
- if (c >> 24 >= trans_thresh) { // ignore transparent entry
- const struct color_info pal_color = get_color_from_srgb(palette[i]);
- const int d = diff(&pal_color, target, trans_thresh);
- if (d < min_dist) {
- pal_id = i;
- min_dist = d;
- }
- }
- }
- return pal_id;
-}
-
-/* Recursive form, simpler but a bit slower. Kept for reference. */
struct nearest_color {
int node_pos;
int dist_sqd;
@@ -248,7 +214,7 @@ static void colormap_nearest_node(const struct color_node *map,
}
}
-static av_always_inline uint8_t colormap_nearest_recursive(const struct color_node *node, const struct color_info *target, const int trans_thresh)
+static av_always_inline uint8_t colormap_nearest(const struct color_node *node, const struct color_info *target, const int trans_thresh)
{
struct nearest_color res = {.dist_sqd = INT32_MAX, .node_pos = -1};
colormap_nearest_node(node, 0, target, trans_thresh, &res);
@@ -260,88 +226,11 @@ struct stack_node {
int dx2;
};
-static av_always_inline uint8_t colormap_nearest_iterative(const struct color_node *root, const struct color_info *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];
- struct stack_node *node = &nodes[0];
-
- for (;;) {
-
- const struct color_node *kd = &root[cur_color_id];
- const struct color_info *current = &kd->c;
- 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. */
- if (current_to_target < best_dist) {
- best_node_id = cur_color_id;
- if (!current_to_target)
- goto end; // exact match, we can return immediately
- best_dist = current_to_target;
- }
-
- /* Check if it's not a leaf */
- if (kd->left_id != -1 || kd->right_id != -1) {
- const int dx = target->lab[kd->split] - current->lab[kd->split];
- int nearer_kd_id, further_kd_id;
-
- /* Define which side is the most interesting. */
- 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) {
- if (further_kd_id != -1) {
- /* Here, both paths are defined, so we push a state for
- * when we are going back. */
- node->color_id = further_kd_id;
- node->dx2 = dx*dx;
- pos++;
- node++;
- }
- /* We can now update current color with the most probable path
- * (no need to create a state since there is nothing to save
- * anymore). */
- cur_color_id = nearer_kd_id;
- continue;
- } else if (dx*dx < best_dist) {
- /* The nearest path isn't available, so there is only one path
- * possible and it's the least probable. We enter it only if the
- * distance from the current point to the hyper rectangle is
- * less than our best distance. */
- cur_color_id = further_kd_id;
- continue;
- }
- }
-
- /* Unstack as much as we can, typically as long as the least probable
- * branch aren't actually probable. */
- do {
- if (--pos < 0)
- goto end;
- node--;
- } while (node->dx2 >= best_dist);
-
- /* We got a node where the least probable branch might actually contain
- * a relevant color. */
- cur_color_id = node->color_id;
- }
-
-end:
- return root[best_node_id].palette_id;
-}
-
-#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
* color tree and cache it.
*/
-static av_always_inline int color_get(PaletteUseContext *s, uint32_t color,
- const enum color_search_method search_method)
+static av_always_inline int color_get(PaletteUseContext *s, uint32_t color)
{
int i;
struct color_info clrinfo;
@@ -366,20 +255,19 @@ static av_always_inline int color_get(PaletteUseContext *s, uint32_t color,
return AVERROR(ENOMEM);
e->color = color;
clrinfo = get_color_from_srgb(color);
- e->pal_entry = COLORMAP_NEAREST(search_method, s->palette, s->map, &clrinfo, s->trans_thresh);
+ e->pal_entry = colormap_nearest(s->map, &clrinfo, s->trans_thresh);
return e->pal_entry;
}
static av_always_inline int get_dst_color_err(PaletteUseContext *s,
- uint32_t c, int *er, int *eg, int *eb,
- const enum color_search_method search_method)
+ uint32_t c, int *er, int *eg, int *eb)
{
const uint8_t r = c >> 16 & 0xff;
const uint8_t g = c >> 8 & 0xff;
const uint8_t b = c & 0xff;
uint32_t dstc;
- const int dstx = color_get(s, c, search_method);
+ const int dstx = color_get(s, c);
if (dstx < 0)
return dstx;
dstc = s->palette[dstx];
@@ -395,8 +283,7 @@ static av_always_inline int get_dst_color_err(PaletteUseContext *s,
static av_always_inline int set_frame(PaletteUseContext *s, AVFrame *out, AVFrame *in,
int x_start, int y_start, int w, int h,
- enum dithering_mode dither,
- const enum color_search_method search_method)
+ enum dithering_mode dither)
{
int x, y;
const int src_linesize = in ->linesize[0] >> 2;
@@ -421,7 +308,7 @@ static av_always_inline int set_frame(PaletteUseContext *s, AVFrame *out, AVFram
const uint8_t g = av_clip_uint8(g8 + d);
const uint8_t b = av_clip_uint8(b8 + d);
const uint32_t color_new = (unsigned)(a8) << 24 | r << 16 | g << 8 | b;
- const int color = color_get(s, color_new, search_method);
+ const int color = color_get(s, color_new);
if (color < 0)
return color;
@@ -429,7 +316,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], &er, &eg, &eb, search_method);
+ const int color = get_dst_color_err(s, src[x], &er, &eg, &eb);
if (color < 0)
return color;
@@ -441,7 +328,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], &er, &eg, &eb, search_method);
+ const int color = get_dst_color_err(s, src[x], &er, &eg, &eb);
if (color < 0)
return color;
@@ -455,7 +342,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], &er, &eg, &eb, search_method);
+ const int color = get_dst_color_err(s, src[x], &er, &eg, &eb);
if (color < 0)
return color;
@@ -474,7 +361,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], &er, &eg, &eb, search_method);
+ const int color = get_dst_color_err(s, src[x], &er, &eg, &eb);
if (color < 0)
return color;
@@ -485,7 +372,7 @@ static av_always_inline int set_frame(PaletteUseContext *s, AVFrame *out, AVFram
if ( down) src[src_linesize + x ] = dither_color(src[src_linesize + x ], er, eg, eb, 1, 2);
} else {
- const int color = color_get(s, src[x], search_method);
+ const int color = color_get(s, src[x]);
if (color < 0)
return color;
@@ -551,35 +438,6 @@ static int disp_tree(const struct color_node *node, const char *fname)
return 0;
}
-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;
-
- for (r = 0; r < 256; r++) {
- for (g = 0; g < 256; g++) {
- for (b = 0; b < 256; b++) {
- const struct color_info target = get_color_from_srgb(0xff000000 | r<<16 | g<<8 | b);
- const int r1 = COLORMAP_NEAREST(search_method, palette, node, &target, trans_thresh);
- const int r2 = colormap_nearest_bruteforce(palette, &target, trans_thresh);
- if (r1 != r2) {
- const struct color_info pal_c1 = get_color_from_srgb(0xff000000 | palette[r1]);
- const struct color_info pal_c2 = get_color_from_srgb(0xff000000 | palette[r2]);
- const int d1 = diff(&pal_c1, &target, trans_thresh);
- const int d2 = diff(&pal_c2, &target, trans_thresh);
- if (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, pal_c1.srgb & 0xffffff, pal_c2.srgb & 0xffffff, d1, d2);
- ret = 1;
- }
- }
- }
- }
- }
- return ret;
-}
-
struct color {
struct Lab value;
uint8_t pal_id;
@@ -746,11 +604,6 @@ static void load_colormap(PaletteUseContext *s)
if (s->dot_filename)
disp_tree(s->map, s->dot_filename);
-
- if (s->debug_accuracy) {
- if (!debug_accuracy(s->map, s->palette, s->trans_thresh, s->color_search_method))
- av_log(NULL, AV_LOG_INFO, "Accuracy check passed\n");
- }
}
static void debug_mean_error(PaletteUseContext *s, const AVFrame *in1,
@@ -1009,38 +862,27 @@ static int load_apply_palette(FFFrameSync *fs)
return ff_filter_frame(ctx->outputs[0], out);
}
-#define DEFINE_SET_FRAME(color_search, name, value) \
+#define DEFINE_SET_FRAME(name, value) \
static int set_frame_##name(PaletteUseContext *s, AVFrame *out, AVFrame *in, \
int x_start, int y_start, int w, int h) \
{ \
- return set_frame(s, out, in, x_start, y_start, w, h, value, color_search); \
-}
-
-#define DEFINE_SET_FRAME_COLOR_SEARCH(color_search, color_search_macro) \
- DEFINE_SET_FRAME(color_search_macro, color_search##_##none, DITHERING_NONE) \
- DEFINE_SET_FRAME(color_search_macro, color_search##_##bayer, DITHERING_BAYER) \
- DEFINE_SET_FRAME(color_search_macro, color_search##_##heckbert, DITHERING_HECKBERT) \
- DEFINE_SET_FRAME(color_search_macro, color_search##_##floyd_steinberg, DITHERING_FLOYD_STEINBERG) \
- DEFINE_SET_FRAME(color_search_macro, color_search##_##sierra2, DITHERING_SIERRA2) \
- DEFINE_SET_FRAME(color_search_macro, color_search##_##sierra2_4a, DITHERING_SIERRA2_4A) \
-
-DEFINE_SET_FRAME_COLOR_SEARCH(nns_iterative, COLOR_SEARCH_NNS_ITERATIVE)
-DEFINE_SET_FRAME_COLOR_SEARCH(nns_recursive, COLOR_SEARCH_NNS_RECURSIVE)
-DEFINE_SET_FRAME_COLOR_SEARCH(bruteforce, COLOR_SEARCH_BRUTEFORCE)
-
-#define DITHERING_ENTRIES(color_search) { \
- set_frame_##color_search##_none, \
- set_frame_##color_search##_bayer, \
- set_frame_##color_search##_heckbert, \
- set_frame_##color_search##_floyd_steinberg, \
- set_frame_##color_search##_sierra2, \
- set_frame_##color_search##_sierra2_4a, \
-}
-
-static const set_frame_func set_frame_lut[NB_COLOR_SEARCHES][NB_DITHERING] = {
- DITHERING_ENTRIES(nns_iterative),
- DITHERING_ENTRIES(nns_recursive),
- DITHERING_ENTRIES(bruteforce),
+ return set_frame(s, out, in, x_start, y_start, w, h, value); \
+}
+
+DEFINE_SET_FRAME(none, DITHERING_NONE)
+DEFINE_SET_FRAME(bayer, DITHERING_BAYER)
+DEFINE_SET_FRAME(heckbert, DITHERING_HECKBERT)
+DEFINE_SET_FRAME(floyd_steinberg, DITHERING_FLOYD_STEINBERG)
+DEFINE_SET_FRAME(sierra2, DITHERING_SIERRA2)
+DEFINE_SET_FRAME(sierra2_4a, DITHERING_SIERRA2_4A)
+
+static const set_frame_func set_frame_lut[NB_DITHERING] = {
+ set_frame_none,
+ set_frame_bayer,
+ set_frame_heckbert,
+ set_frame_floyd_steinberg,
+ set_frame_sierra2,
+ set_frame_sierra2_4a,
};
static int dither_value(int p)
@@ -1060,7 +902,7 @@ static av_cold int init(AVFilterContext *ctx)
if (!s->last_in || !s->last_out)
return AVERROR(ENOMEM);
- s->set_frame = set_frame_lut[s->color_search_method][s->dither];
+ s->set_frame = set_frame_lut[s->dither];
if (s->dither == DITHERING_BAYER) {
int i;
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 30/32] avfilter/paletteuse: remove mean error tool
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (28 preceding siblings ...)
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 29/32] avfilter/paletteuse: remove alternative search methods Clément Bœsch
@ 2022-12-27 23:18 ` Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 31/32] avfilter/paletteuse: move r, g, b computation in a more local scope Clément Bœsch
` (2 subsequent siblings)
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:18 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
This belongs in another filter.
---
libavfilter/vf_paletteuse.c | 31 -------------------------------
1 file changed, 31 deletions(-)
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index 690422a842..33b8e70293 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -121,7 +121,6 @@ static const AVOption paletteuse_options[] = {
/* 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 },
- { "mean_err", "compute and print mean error", OFFSET(calc_mean_err), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS },
{ NULL }
};
@@ -606,34 +605,6 @@ static void load_colormap(PaletteUseContext *s)
disp_tree(s->map, s->dot_filename);
}
-static void debug_mean_error(PaletteUseContext *s, const AVFrame *in1,
- const AVFrame *in2, int frame_count)
-{
- int x, y;
- const uint32_t *palette = s->palette;
- uint32_t *src1 = (uint32_t *)in1->data[0];
- 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 * 3;
- unsigned mean_err = 0;
-
- for (y = 0; y < in1->height; y++) {
- for (x = 0; x < in1->width; x++) {
- const struct color_info c1 = get_color_from_srgb(0xff000000 | src1[x]);
- const struct color_info c2 = get_color_from_srgb(0xff000000 | palette[src2[x]]);
- mean_err += diff(&c1, &c2, s->trans_thresh);
- }
- src1 += src1_linesize;
- src2 += src2_linesize;
- }
-
- s->total_mean_err += mean_err;
-
- av_log(NULL, AV_LOG_INFO, "MEP:%.3f TotalMEP:%.3f\n",
- mean_err / div, s->total_mean_err / (div * frame_count));
-}
-
static void set_processing_window(enum diff_mode diff_mode,
const AVFrame *prv_src, const AVFrame *cur_src,
const AVFrame *prv_dst, AVFrame *cur_dst,
@@ -759,8 +730,6 @@ static int apply_palette(AVFilterLink *inlink, AVFrame *in, AVFrame **outf)
return ret;
}
memcpy(out->data[1], s->palette, AVPALETTE_SIZE);
- if (s->calc_mean_err)
- debug_mean_error(s, in, out, inlink->frame_count_out);
*outf = out;
return 0;
}
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 31/32] avfilter/paletteuse: move r, g, b computation in a more local scope
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (29 preceding siblings ...)
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 30/32] avfilter/paletteuse: remove mean error tool Clément Bœsch
@ 2022-12-27 23:18 ` Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 32/32] avfilter/palette{gen, use}: misc for-loop cosmetics Clément Bœsch
2023-01-03 16:28 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:18 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
---
libavfilter/vf_paletteuse.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index 33b8e70293..e3462b4abb 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -262,9 +262,6 @@ static av_always_inline int color_get(PaletteUseContext *s, uint32_t color)
static av_always_inline int get_dst_color_err(PaletteUseContext *s,
uint32_t c, int *er, int *eg, int *eb)
{
- const uint8_t r = c >> 16 & 0xff;
- const uint8_t g = c >> 8 & 0xff;
- const uint8_t b = c & 0xff;
uint32_t dstc;
const int dstx = color_get(s, c);
if (dstx < 0)
@@ -273,6 +270,9 @@ static av_always_inline int get_dst_color_err(PaletteUseContext *s,
if (dstx == s->transparency_index) {
*er = *eg = *eb = 0;
} else {
+ const uint8_t r = c >> 16 & 0xff;
+ const uint8_t g = c >> 8 & 0xff;
+ const uint8_t b = c & 0xff;
*er = (int)r - (int)(dstc >> 16 & 0xff);
*eg = (int)g - (int)(dstc >> 8 & 0xff);
*eb = (int)b - (int)(dstc & 0xff);
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* [FFmpeg-devel] [PATCH v2 32/32] avfilter/palette{gen, use}: misc for-loop cosmetics
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (30 preceding siblings ...)
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 31/32] avfilter/paletteuse: move r, g, b computation in a more local scope Clément Bœsch
@ 2022-12-27 23:18 ` Clément Bœsch
2023-01-03 16:28 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:18 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Clément Bœsch
---
libavfilter/vf_palettegen.c | 19 +++++++++----------
libavfilter/vf_paletteuse.c | 22 +++++++++-------------
2 files changed, 18 insertions(+), 23 deletions(-)
diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c
index 97e12f7274..4b69d3c63b 100644
--- a/libavfilter/vf_palettegen.c
+++ b/libavfilter/vf_palettegen.c
@@ -209,13 +209,13 @@ static void compute_box_stats(PaletteGenContext *s, struct range_box *box)
*/
static int get_next_box_id_to_split(PaletteGenContext *s)
{
- int box_id, best_box_id = -1;
+ int best_box_id = -1;
int64_t max_score = -1;
if (s->nb_boxes == s->max_colors - s->reserve_transparent)
return -1;
- for (box_id = 0; box_id < s->nb_boxes; box_id++) {
+ for (int box_id = 0; box_id < s->nb_boxes; box_id++) {
const struct range_box *box = &s->boxes[box_id];
if (s->boxes[box_id].len >= 2 && box->cut_score > max_score) {
best_box_id = box_id;
@@ -250,13 +250,13 @@ static void split_box(PaletteGenContext *s, struct range_box *box, int n)
static void write_palette(AVFilterContext *ctx, AVFrame *out)
{
const PaletteGenContext *s = ctx->priv;
- int x, y, box_id = 0;
+ int box_id = 0;
uint32_t *pal = (uint32_t *)out->data[0];
const int pal_linesize = out->linesize[0] >> 2;
uint32_t last_color = 0;
- for (y = 0; y < out->height; y++) {
- for (x = 0; x < out->width; x++) {
+ for (int y = 0; y < out->height; y++) {
+ for (int x = 0; x < out->width; x++) {
if (box_id < s->nb_boxes) {
pal[x] = s->boxes[box_id++].color;
if ((x || y) && pal[x] == last_color)
@@ -282,16 +282,16 @@ static void write_palette(AVFilterContext *ctx, AVFrame *out)
*/
static struct color_ref **load_color_refs(const struct hist_node *hist, int nb_refs)
{
- int i, j, k = 0;
+ int k = 0;
struct color_ref **refs = av_malloc_array(nb_refs, sizeof(*refs));
if (!refs)
return NULL;
- for (j = 0; j < HIST_SIZE; j++) {
+ for (int j = 0; j < HIST_SIZE; j++) {
const struct hist_node *node = &hist[j];
- for (i = 0; i < node->nb_entries; i++)
+ for (int i = 0; i < node->nb_entries; i++)
refs[k++] = &node->entries[i];
}
@@ -391,12 +391,11 @@ static AVFrame *get_palette_frame(AVFilterContext *ctx)
*/
static int color_inc(struct hist_node *hist, uint32_t color)
{
- int i;
const uint32_t hash = ff_lowbias32(color) & (HIST_SIZE - 1);
struct hist_node *node = &hist[hash];
struct color_ref *e;
- for (i = 0; i < node->nb_entries; i++) {
+ for (int i = 0; i < node->nb_entries; i++) {
e = &node->entries[i];
if (e->color == color) {
e->count++;
diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c
index e3462b4abb..33d0b0e722 100644
--- a/libavfilter/vf_paletteuse.c
+++ b/libavfilter/vf_paletteuse.c
@@ -231,7 +231,6 @@ struct stack_node {
*/
static av_always_inline int color_get(PaletteUseContext *s, uint32_t color)
{
- int i;
struct color_info clrinfo;
const uint32_t hash = ff_lowbias32(color) & (CACHE_SIZE - 1);
struct cache_node *node = &s->cache[hash];
@@ -242,7 +241,7 @@ static av_always_inline int color_get(PaletteUseContext *s, uint32_t color)
return s->transparency_index;
}
- for (i = 0; i < node->nb_entries; i++) {
+ for (int i = 0; i < node->nb_entries; i++) {
e = &node->entries[i];
if (e->color == color)
return e->pal_entry;
@@ -284,7 +283,6 @@ static av_always_inline int set_frame(PaletteUseContext *s, AVFrame *out, AVFram
int x_start, int y_start, int w, int h,
enum dithering_mode dither)
{
- int x, y;
const int src_linesize = in ->linesize[0] >> 2;
const int dst_linesize = out->linesize[0];
uint32_t *src = ((uint32_t *)in ->data[0]) + y_start*src_linesize;
@@ -293,8 +291,8 @@ static av_always_inline int set_frame(PaletteUseContext *s, AVFrame *out, AVFram
w += x_start;
h += y_start;
- for (y = y_start; y < h; y++) {
- for (x = x_start; x < w; x++) {
+ for (int y = y_start; y < h; y++) {
+ for (int x = x_start; x < w; x++) {
int er, eg, eb;
if (dither == DITHERING_BAYER) {
@@ -467,7 +465,7 @@ static int get_next_color(const uint8_t *color_used, const uint32_t *palette,
int *component, const struct color_rect *box)
{
int wL, wa, wb;
- int i, longest = 0;
+ int longest = 0;
unsigned nb_color = 0;
struct color_rect ranges;
struct color tmp_pal[256];
@@ -476,7 +474,7 @@ static int get_next_color(const uint8_t *color_used, const uint32_t *palette,
ranges.min[0] = ranges.min[1] = ranges.min[2] = 0xffff;
ranges.max[0] = ranges.max[1] = ranges.max[2] = -0xffff;
- for (i = 0; i < AVPALETTE_COUNT; i++) {
+ for (int i = 0; i < AVPALETTE_COUNT; i++) {
const uint32_t c = palette[i];
const uint8_t a = c >> 24;
const struct Lab lab = ff_srgb_u8_to_oklab_int(c);
@@ -571,7 +569,7 @@ static int cmp_pal_entry(const void *a, const void *b)
static void load_colormap(PaletteUseContext *s)
{
- int i, nb_used = 0;
+ int nb_used = 0;
uint8_t color_used[AVPALETTE_COUNT] = {0};
uint32_t last_color = 0;
struct color_rect box;
@@ -583,7 +581,7 @@ static void load_colormap(PaletteUseContext *s)
/* disable transparent colors and dups */
qsort(s->palette, AVPALETTE_COUNT-(s->transparency_index >= 0), sizeof(*s->palette), cmp_pal_entry);
- for (i = 0; i < AVPALETTE_COUNT; i++) {
+ for (int i = 0; i < AVPALETTE_COUNT; i++) {
const uint32_t c = s->palette[i];
if (i != 0 && c == last_color) {
color_used[i] = 1;
@@ -874,10 +872,9 @@ static av_cold int init(AVFilterContext *ctx)
s->set_frame = set_frame_lut[s->dither];
if (s->dither == DITHERING_BAYER) {
- int i;
const int delta = 1 << (5 - s->bayer_scale); // to avoid too much luma
- for (i = 0; i < FF_ARRAY_ELEMS(s->ordered_dither); i++)
+ for (int i = 0; i < FF_ARRAY_ELEMS(s->ordered_dither); i++)
s->ordered_dither[i] = (dither_value(i) >> s->bayer_scale) - delta;
}
@@ -892,11 +889,10 @@ static int activate(AVFilterContext *ctx)
static av_cold void uninit(AVFilterContext *ctx)
{
- int i;
PaletteUseContext *s = ctx->priv;
ff_framesync_uninit(&s->fs);
- for (i = 0; i < CACHE_SIZE; i++)
+ for (int i = 0; i < CACHE_SIZE; i++)
av_freep(&s->cache[i].entries);
av_frame_free(&s->last_in);
av_frame_free(&s->last_out);
--
2.39.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] [PATCH 12/15] avfilter/palettegen: base split decision on a perceptual model
2022-11-05 19:07 ` Andreas Rheinhardt
2022-11-08 21:09 ` Clément Bœsch
@ 2022-12-27 23:20 ` Clément Bœsch
1 sibling, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:20 UTC (permalink / raw)
To: FFmpeg development discussions and patches
On Sat, Nov 05, 2022 at 08:07:42PM +0100, Andreas Rheinhardt wrote:
[...]
> You are adding floating point to places where there was no floating
> point before (some other patches of this patchset do the same). Is this
> still bitexact across all supported arches?
It should be good with the last iteration I just submitted.
Regards,
--
Clément B.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] Rework color quantization in palette{gen,use}
2022-11-08 22:37 ` Soft Works
@ 2022-12-27 23:31 ` Clément Bœsch
0 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2022-12-27 23:31 UTC (permalink / raw)
To: FFmpeg development discussions and patches
On Tue, Nov 08, 2022 at 10:37:59PM +0000, Soft Works wrote:
[...]
> For completeness, I'm also including the recent comparison, but it
> seems you're already on track in this regard.
If you remove the alpha from the input image you'll see that it performs
pretty much as good. You can check with the last results.
Please note though that it's expected to have elbg or even pngquant to get
better results, because they're relying on clustering algorithms afaik,
which are slower (but more efficient). It's a trade-off.
[...]
> Then I'd have a question about your file07 example. Is this the
> original file or did I mix something up?
>
> http://big.pkh.me/pal/output/0-current/file07/cfg00/0-ref.png
>
> I'm wondering because the image is full or weird artifacts at the
> edges of the green (and other) leafes.
>
I'm assuming this was the image with 1M+ colors; sorry I removed the file,
but yeah given the URL it was the reference file with weird artifacts
(it's a synthetic sample after all).
[...]
> PS: I'd be curious what you think about the elbg image...
If you want to look at elbg vs palette filters, make sure you disable
dithering (elbg doesn't have any), and make sure to use the sample without
transparency. You'll see that they perform mostly the same.
--
Clément B.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] [PATCH v2 01/32] avfilter/palettegen: allow a minimum of 2 colors
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 01/32] avfilter/palettegen: allow a minimum of 2 colors Clément Bœsch
@ 2022-12-28 21:04 ` Tomas Härdin
2022-12-28 21:23 ` Clément Bœsch
0 siblings, 1 reply; 75+ messages in thread
From: Tomas Härdin @ 2022-12-28 21:04 UTC (permalink / raw)
To: FFmpeg development discussions and patches; +Cc: Clément Bœsch
ons 2022-12-28 klockan 00:17 +0100 skrev Clément Bœsch:
> ---
> libavfilter/vf_palettegen.c | 7 ++++++-
> 1 file changed, 6 insertions(+), 1 deletion(-)
>
> diff --git a/libavfilter/vf_palettegen.c
> b/libavfilter/vf_palettegen.c
> index 27f74fd147..c03f62b942 100644
> --- a/libavfilter/vf_palettegen.c
> +++ b/libavfilter/vf_palettegen.c
> @@ -82,7 +82,7 @@ typedef struct PaletteGenContext {
> #define OFFSET(x) offsetof(PaletteGenContext, x)
> #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
> static const AVOption palettegen_options[] = {
> - { "max_colors", "set the maximum number of colors to use in the
> palette", OFFSET(max_colors), AV_OPT_TYPE_INT, {.i64=256}, 4, 256,
> FLAGS },
> + { "max_colors", "set the maximum number of colors to use in the
> palette", OFFSET(max_colors), AV_OPT_TYPE_INT, {.i64=256}, 2, 256,
> FLAGS },
> { "reserve_transparent", "reserve a palette entry for
> transparency", OFFSET(reserve_transparent), AV_OPT_TYPE_BOOL,
> {.i64=1}, 0, 1, FLAGS },
> { "transparency_color", "set a background color for
> transparency", OFFSET(transparency_color), AV_OPT_TYPE_COLOR,
> {.str="lime"}, 0, 0, FLAGS },
> { "stats_mode", "set statistics mode", OFFSET(stats_mode),
> AV_OPT_TYPE_INT, {.i64=STATS_MODE_ALL_FRAMES}, 0, NB_STATS_MODE-1,
> FLAGS, "mode" },
> @@ -586,6 +586,11 @@ static int init(AVFilterContext *ctx)
> 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");
Does this mean all-black with transparent is disallowed? Shouldn't it
be max_colors + reserve < 2?
/Tomas
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] [PATCH v2 01/32] avfilter/palettegen: allow a minimum of 2 colors
2022-12-28 21:04 ` Tomas Härdin
@ 2022-12-28 21:23 ` Clément Bœsch
2023-01-03 18:59 ` Tomas Härdin
0 siblings, 1 reply; 75+ messages in thread
From: Clément Bœsch @ 2022-12-28 21:23 UTC (permalink / raw)
To: FFmpeg development discussions and patches
On Wed, Dec 28, 2022 at 10:04:26PM +0100, Tomas Härdin wrote:
[...]
> Does this mean all-black with transparent is disallowed?
Yes a single color with or without an extra transparency slot makes no
sense.
> Shouldn't it be max_colors + reserve < 2?
max_colors accounts for the transparent color as well. So if you specify
256 colors with reserve_transparent (default settings), you'll get 255
color slots and 1 for transparency.
It also means that if want to go as low as 2 colors in your palette you
can do max_colors=3 with reserve_transparent=1, or max_colors=2 with
reserve_transparent=0
3-1 → 2
2-0 → 2
These are the minimums.
PS: sorry for the double answer Tomas!
--
Clément B.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] Rework color quantization in palette{gen,use}
2022-11-06 17:30 ` Michael Niedermayer
2022-11-08 21:14 ` Clément Bœsch
@ 2022-12-31 12:11 ` Clément Bœsch
2023-01-02 21:57 ` Michael Niedermayer
1 sibling, 1 reply; 75+ messages in thread
From: Clément Bœsch @ 2022-12-31 12:11 UTC (permalink / raw)
To: FFmpeg development discussions and patches
On Sun, Nov 06, 2022 at 06:30:22PM +0100, Michael Niedermayer wrote:
> On Sun, Nov 06, 2022 at 06:09:41PM +0100, Michael Niedermayer wrote:
> > On Sat, Nov 05, 2022 at 04:26:02PM +0100, Clément Bœsch wrote:
> > > Hi,
> > >
> > > This patchset essentially fixes a few core problems in these filters and
> > > switches to a perceptual model.
> > >
> > > I've generated a report for each key commit on this (temporary) page:
> > > http://big.pkh.me/pal/ (warning: heavy page, ~500M; I did try to add some lazy
> > > loading of the images but I'm not sure it's actually working as expected).
> >
> > i just looked at file00 and 16 and 64 colors with dither for it and they look
> > different, some areas look better before and some better afterwards
>
> looked at more of the 16 color cases with dither
> (16 colors as i asumed fewer would magnify any issues )
> file 01, IMHO current looks better than last (variance per axis)
> file 02, IMHO current looks better than last (variance per axis)
> file 03, IMHO VPA looks better but both really are quite off in terms of color,
> thats not the color of the original image.
> file 04, VPA is not good thats not the correct color
>
> It seems th last (variance per axis) is more pale and looses color
So I did a lot of experiments, and the explanation for the desaturated
output at low number of colors can be found at the end of this article:
http://blog.pkh.me/p/39-improving-color-quantization-heuristics.html
I still think it's acceptable to lean toward desaturated colors when
reducing the number of colors, but you may disagree.
Regards,
--
Clément B.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] Rework color quantization in palette{gen,use}
2022-12-31 12:11 ` Clément Bœsch
@ 2023-01-02 21:57 ` Michael Niedermayer
2023-01-02 23:05 ` Clément Bœsch
0 siblings, 1 reply; 75+ messages in thread
From: Michael Niedermayer @ 2023-01-02 21:57 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1.1: Type: text/plain, Size: 4738 bytes --]
Hi
On Sat, Dec 31, 2022 at 01:11:54PM +0100, Clément Bœsch wrote:
> On Sun, Nov 06, 2022 at 06:30:22PM +0100, Michael Niedermayer wrote:
> > On Sun, Nov 06, 2022 at 06:09:41PM +0100, Michael Niedermayer wrote:
> > > On Sat, Nov 05, 2022 at 04:26:02PM +0100, Clément Bœsch wrote:
> > > > Hi,
> > > >
> > > > This patchset essentially fixes a few core problems in these filters and
> > > > switches to a perceptual model.
> > > >
> > > > I've generated a report for each key commit on this (temporary) page:
> > > > http://big.pkh.me/pal/ (warning: heavy page, ~500M; I did try to add some lazy
> > > > loading of the images but I'm not sure it's actually working as expected).
> > >
> > > i just looked at file00 and 16 and 64 colors with dither for it and they look
> > > different, some areas look better before and some better afterwards
> >
> > looked at more of the 16 color cases with dither
> > (16 colors as i asumed fewer would magnify any issues )
> > file 01, IMHO current looks better than last (variance per axis)
> > file 02, IMHO current looks better than last (variance per axis)
> > file 03, IMHO VPA looks better but both really are quite off in terms of color,
> > thats not the color of the original image.
> > file 04, VPA is not good thats not the correct color
> >
> > It seems th last (variance per axis) is more pale and looses color
>
> So I did a lot of experiments, and the explanation for the desaturated
> output at low number of colors can be found at the end of this article:
> http://blog.pkh.me/p/39-improving-color-quantization-heuristics.html
interresting and its impressive how much reseacrh you did here
i hope this will get applied also i hape a bit that it will get
extended to include clustering as in ELBG cuz it seems a bit odd
to have this sort of alternative filters neither does all ....
>
> I still think it's acceptable to lean toward desaturated colors when
> reducing the number of colors, but you may disagree.
I think a key aspect of desaturation has not been mentioned.
That is mixing, i mean dithering is some sort of mixing, in the sense of
an artist mixing several pigment/dyes/colors.
If you have black and white a 50% mixture gives 50% gray. other ratios
would give us all values between white and black though with dithering
some ratios work better like 50% looks good while ratios very close to
0 and 100% but not exacty 0 and 100 look bad with few highly vissible
black or white pixels in a see of the opposing color.
Now this results in 2 things at least.
1. We should be able to improve color quantization by this.
If we have colors A and B the (A+B)/2 point is basically free, its dither
pattern looks good on any high resolution display and if we consider such
points there are more of course like maybe (A+B+C+D)/4 we can cover more
output colors with a smaller palette.
2. desaturation happens in dithered images because colors are simply not
representable, the same way a artist cant paint 100% white if the brightest
color she has is 80% white. She cant mix that with anything to make it
brighter. An algorithm which would ensure that the colors from the palette
form a convex hull around all the colors of the input would ensure all
colors are representable and no desaturation should happen. it of course
may look bad, i dont know, A convex hull likely is not the global optimum
from a perceptual POV. But one only needs 8 colors to gurantee all colors
are representable with dithering
Another way to maybe see this is that if you have 1 color the best place
is teh one where it minimizes the distance to all. But as more points are
added average points between them become usable in a dithered image so
the thing starts filling up while the perimeter and outside is harder
to represent
One could also say that with 2 colors all points on the line joining
them can be represented and so distance to that line could be minimized
but as not really all points on that line form pleasing dither patterns
iam hesitant about this representation but it can be extended to a triangle
and so forth with more points
Now i hope i have not given any ideas that make you spend more months on
this if you dont enjoy it :) But i find the whole myself a bit interresting
[...]
thx
--
Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
Any man who breaks a law that conscience tells him is unjust and willingly
accepts the penalty by staying in jail in order to arouse the conscience of
the community on the injustice of the law is at that moment expressing the
very highest respect for law. - Martin Luther King Jr
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] Rework color quantization in palette{gen,use}
2023-01-02 21:57 ` Michael Niedermayer
@ 2023-01-02 23:05 ` Clément Bœsch
2023-01-03 18:50 ` Michael Niedermayer
0 siblings, 1 reply; 75+ messages in thread
From: Clément Bœsch @ 2023-01-02 23:05 UTC (permalink / raw)
To: FFmpeg development discussions and patches
On Mon, Jan 02, 2023 at 10:57:33PM +0100, Michael Niedermayer wrote:
[...]
> > So I did a lot of experiments, and the explanation for the desaturated
> > output at low number of colors can be found at the end of this article:
> > http://blog.pkh.me/p/39-improving-color-quantization-heuristics.html
>
> interresting and its impressive how much reseacrh you did here
> i hope this will get applied
Thanks. I was actually planning to push in the next 12 hours or so, unless
there is an objection.
> also i hape a bit that it will get
> extended to include clustering as in ELBG cuz it seems a bit odd
> to have this sort of alternative filters neither does all ....
Yeah at some point we probably want to group the clustering and vector
quantization logics in a common module. But there are lot of questions API
wise wrt its relationship with perceptual and other color systems.
> > I still think it's acceptable to lean toward desaturated colors when
> > reducing the number of colors, but you may disagree.
>
> I think a key aspect of desaturation has not been mentioned.
> That is mixing, i mean dithering is some sort of mixing, in the sense of
> an artist mixing several pigment/dyes/colors.
> If you have black and white a 50% mixture gives 50% gray. other ratios
> would give us all values between white and black though with dithering
> some ratios work better like 50% looks good while ratios very close to
> 0 and 100% but not exacty 0 and 100 look bad with few highly vissible
> black or white pixels in a see of the opposing color.
>
> Now this results in 2 things at least.
> 1. We should be able to improve color quantization by this.
> If we have colors A and B the (A+B)/2 point is basically free, its dither
> pattern looks good on any high resolution display and if we consider such
> points there are more of course like maybe (A+B+C+D)/4 we can cover more
> output colors with a smaller palette.
That's interesting. Basically you'd free certain slots of the palette if
you detect that this particular color is at the mid point of two others in
the palette? And so you could use that slot for another tint…
Yeah I don't know what to do with this information, it looks not trivial
to implement.
> 2. desaturation happens in dithered images because colors are simply not
> representable, the same way a artist cant paint 100% white if the brightest
> color she has is 80% white. She cant mix that with anything to make it
> brighter. An algorithm which would ensure that the colors from the palette
> form a convex hull around all the colors of the input would ensure all
> colors are representable and no desaturation should happen. it of course
> may look bad, i dont know, A convex hull likely is not the global optimum
> from a perceptual POV. But one only needs 8 colors to gurantee all colors
> are representable with dithering
I feel like a cheap hack would be to create a filter such as
"palettesource" which generates a palette using OkLCh (same as OkLab but
circular space, the hue is an angle) to design such palette. That's easy
to do and you could immediately test it by feeding it to paletteuse.
> Another way to maybe see this is that if you have 1 color the best place
> is teh one where it minimizes the distance to all. But as more points are
> added average points between them become usable in a dithered image so
> the thing starts filling up while the perimeter and outside is harder
> to represent
> One could also say that with 2 colors all points on the line joining
> them can be represented and so distance to that line could be minimized
> but as not really all points on that line form pleasing dither patterns
> iam hesitant about this representation but it can be extended to a triangle
> and so forth with more points
>
> Now i hope i have not given any ideas that make you spend more months on
> this if you dont enjoy it :) But i find the whole myself a bit interresting
Heh, yeah I'm already onto another crazy topic currently so you'll have to
do it on your own :)
BTW it was rightfully pointed out to me that in addition to the box and
the axis selections, there is a 3rd aspect to study: the median cut
itself.
There is likely something better to do here that would use the values
themselves instead of just a cut at the median of the set, specifically if
there are large gaps in the values. For example [1,2,3,6,7,8,231,255]
(assuming weights of 1) would be cut [1,2,3,6] [7,8,231,255] when
[1,2,3,6,7,8] [231,255] would probably be much more appropriate.
It might help addressing the bias toward L* for low number of colors
where these irregularities are particularly common (and tend to smooth out
over cuts because typically [7,8,231,255] is likely to be cut again soon
due to its variance).
I feel like it might not be that hard to actually improve the low color
count by trying out some alternatives. But there are many cut solutions
approaches which need to be measured.
I'm happy to provide guidance in how to try them out in my research
repository. It should be pretty easy to hack.
Regards,
--
Clément B.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] New iteration for the color quantization in palette{gen, use}
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
` (31 preceding siblings ...)
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 32/32] avfilter/palette{gen, use}: misc for-loop cosmetics Clément Bœsch
@ 2023-01-03 16:28 ` Clément Bœsch
32 siblings, 0 replies; 75+ messages in thread
From: Clément Bœsch @ 2023-01-03 16:28 UTC (permalink / raw)
To: FFmpeg development discussions and patches
On Wed, Dec 28, 2022 at 12:17:42AM +0100, Clément Bœsch wrote:
[...]
Patchset applied
--
Clément B.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] Rework color quantization in palette{gen,use}
2023-01-02 23:05 ` Clément Bœsch
@ 2023-01-03 18:50 ` Michael Niedermayer
0 siblings, 0 replies; 75+ messages in thread
From: Michael Niedermayer @ 2023-01-03 18:50 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1.1: Type: text/plain, Size: 5166 bytes --]
On Tue, Jan 03, 2023 at 12:05:22AM +0100, Clément Bœsch wrote:
> On Mon, Jan 02, 2023 at 10:57:33PM +0100, Michael Niedermayer wrote:
> [...]
> > > So I did a lot of experiments, and the explanation for the desaturated
> > > output at low number of colors can be found at the end of this article:
> > > http://blog.pkh.me/p/39-improving-color-quantization-heuristics.html
> >
> > interresting and its impressive how much reseacrh you did here
> > i hope this will get applied
>
> Thanks. I was actually planning to push in the next 12 hours or so, unless
> there is an objection.
>
> > also i hape a bit that it will get
> > extended to include clustering as in ELBG cuz it seems a bit odd
> > to have this sort of alternative filters neither does all ....
>
> Yeah at some point we probably want to group the clustering and vector
> quantization logics in a common module. But there are lot of questions API
> wise wrt its relationship with perceptual and other color systems.
>
> > > I still think it's acceptable to lean toward desaturated colors when
> > > reducing the number of colors, but you may disagree.
> >
> > I think a key aspect of desaturation has not been mentioned.
> > That is mixing, i mean dithering is some sort of mixing, in the sense of
> > an artist mixing several pigment/dyes/colors.
> > If you have black and white a 50% mixture gives 50% gray. other ratios
> > would give us all values between white and black though with dithering
> > some ratios work better like 50% looks good while ratios very close to
> > 0 and 100% but not exacty 0 and 100 look bad with few highly vissible
> > black or white pixels in a see of the opposing color.
> >
> > Now this results in 2 things at least.
> > 1. We should be able to improve color quantization by this.
> > If we have colors A and B the (A+B)/2 point is basically free, its dither
> > pattern looks good on any high resolution display and if we consider such
> > points there are more of course like maybe (A+B+C+D)/4 we can cover more
> > output colors with a smaller palette.
>
> That's interesting. Basically you'd free certain slots of the palette if
> you detect that this particular color is at the mid point of two others in
> the palette? And so you could use that slot for another tint…
>
> Yeah I don't know what to do with this information, it looks not trivial
> to implement.
If we simplify the problem a bit and instead of considering 3d we just look at 1d
then for example to represent all colors between 0 and 128 either as a
solid color or by 2 colors 50:50% then we need only 19 colors of these values
{0,2,6,8,10,16,28,40,52,64,76,88,100,112,118,120,122,126,128}
if we have fewer colors in 1d we can cover these
Full n= 4 cover=9 avg=2.666667 {0,2,6,8}
Full n= 5 cover=13 avg=3.769231 {0,2,6,10,12}
Full n= 6 cover=17 avg=4.823529 {0,2,6,10,14,16}
Full n= 7 cover=21 avg=5.857143 {0,2,4,10,16,18,20}
Full n= 8 cover=27 avg=6.888889 {0,2,6,8,18,20,24,26}
Full n= 9 cover=33 avg= 9.969697 {0,2,4,10,16,22,28,30,32}
Full n=10 cover=41 avg=10.878049 {0,2,6,8,18,22,32,34,38,40}
Full n=11 cover=45 avg=10.644444 {0,2,6,8,18,22,26,36,38,42,44}
Full n=12 cover=55 avg=14.727273 {0,2,6,8,18,22,32,36,46,48,52,54}
Full n=13 cover=65 avg=18.415385 {0,2,6,8,18,22,32,42,46,56,58,62,64}
Full n=14 cover=73 avg=18.931507 {0,2,6,8,18,22,32,40,50,54,64,66,70,72}
Full n=15 cover=81 avg=25.172840 {0,2,6,8,10,16,28,40,52,64,70,72,74,78,80}
Full n=16 cover=93 avg=30.193548 {0,2,6,8,10,16,28,40,52,64,76,82,84,86,90,92}
Full n=17 cover=105 avg=35.209524 {0,2,6,8,10,16,28,40,52,64,76,88,94,96,98,102,104}
Full n=18 cover=111 avg=33.261261 {0,2,6,10,14,18,20,42,44,66,68,90,92,96,100,104,108,110}
so what about 3d ?
if we have a 3d cube and we create a palette with the 1d list from above for
6 colors we end up with a 6x6x6 palette of 216 colors where with the 50:50 mixes
we cover the full 17x17x17=4913 cube, all points of it that is. same for the 6x6x7
(17x17x21=6069) cases. this might be an interresting alternative to the 332 palette
or maybe it could be used as start point for some optimized palette, remocing unused
colors and adding new by some clustering
I didnt try any of this on an actual image. Its not as simple as using that palette
as the dither algorithm also needs to be redesigned to actually use the right
color pairs. Dither would generally use the closest color and thats not true here
for many pairs
also gamma needs to be handled correctly for all this because mixing white and
black pixels will only look like 50% gray when gamma is handled correctly
As i didnt try it, i have 0 idea how bad it would look. I was primarly interrested
in the nummeric/math aspect behind this which is why i played a bit with these
numbers
thx
[...]
--
Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
The day soldiers stop bringing you their problems is the day you have stopped
leading them. They have either lost confidence that you can help or concluded
you do not care. Either case is a failure of leadership. - Colin Powell
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] [PATCH v2 01/32] avfilter/palettegen: allow a minimum of 2 colors
2022-12-28 21:23 ` Clément Bœsch
@ 2023-01-03 18:59 ` Tomas Härdin
0 siblings, 0 replies; 75+ messages in thread
From: Tomas Härdin @ 2023-01-03 18:59 UTC (permalink / raw)
To: FFmpeg development discussions and patches
ons 2022-12-28 klockan 22:23 +0100 skrev Clément Bœsch:
> On Wed, Dec 28, 2022 at 10:04:26PM +0100, Tomas Härdin wrote:
> [...]
> > Does this mean all-black with transparent is disallowed?
>
> Yes a single color with or without an extra transparency slot makes
> no
> sense.
>
> > Shouldn't it be max_colors + reserve < 2?
>
> max_colors accounts for the transparent color as well. So if you
> specify
> 256 colors with reserve_transparent (default settings), you'll get
> 255
> color slots and 1 for transparency.
>
> It also means that if want to go as low as 2 colors in your palette
> you
> can do max_colors=3 with reserve_transparent=1, or max_colors=2 with
> reserve_transparent=0
>
> 3-1 → 2
> 2-0 → 2
>
> These are the minimums.
Right, because there's no "0bpp" pixel format with 1bpp alpha.. yet!
Something similar actually exists with GoldSrc .spr files, especially
the indexalpha mode. It is effectively "0bpp" with 8-bit alpha:
https://the303.org/tutorials/gold_sprite_p3.htm
>
> PS: sorry for the double answer Tomas!
No problem :)
/Tomas
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
* Re: [FFmpeg-devel] [PATCH v2 02/32] avfilter/palette{gen, use}: revert support palettes with alpha
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 02/32] avfilter/palette{gen, use}: revert support palettes with alpha Clément Bœsch
@ 2023-01-03 19:11 ` Paul B Mahol
0 siblings, 0 replies; 75+ messages in thread
From: Paul B Mahol @ 2023-01-03 19:11 UTC (permalink / raw)
To: FFmpeg development discussions and patches; +Cc: Clément Bœsch
Please add alpha support back.
On Wed, Dec 28, 2022 at 12:18 AM Clément Bœsch <u@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}.
> -@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}.
> -
> -@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@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 75+ messages in thread
end of thread, other threads:[~2023-01-03 19:06 UTC | newest]
Thread overview: 75+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-11-05 15:26 [FFmpeg-devel] Rework color quantization in palette{gen,use} Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 01/15] Revert "avfilter/vf_palette(gen|use): support palettes with alpha" Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 02/15] avfilter: add palette utils Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 03/15] avfilter/palette{use, gen}: simplify a few alpha masks Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 04/15] avfilter/paletteuse: switch from u8[4] to u32 for color code Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 05/15] avfilter/paletteuse: name target color arg consistently in colormap functions Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 06/15] avfilter/paletteuse: remove unused alpha split dimension Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 07/15] avfilter/paletteuse: remove redundant alpha condition Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 08/15] avfilter/paletteuse: switch to a perceptual model Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 09/15] avfilter/palettegen: average color in linear space Clément Bœsch
2022-11-05 15:39 ` Paul B Mahol
2022-11-05 18:50 ` Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 10/15] avfilter/palettegen: move box variance computation in a dedicated function Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 11/15] avfilter/palettegen: comment on the unnormalized variance Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 12/15] avfilter/palettegen: base split decision on a perceptual model Clément Bœsch
2022-11-05 19:07 ` Andreas Rheinhardt
2022-11-08 21:09 ` Clément Bœsch
2022-12-27 23:20 ` Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 13/15] avfilter/palettegen: use variance per-axis instead of the range Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 14/15] avfilter/palettegen: rename longest to split_axis Clément Bœsch
2022-11-05 15:26 ` [FFmpeg-devel] [PATCH 15/15] avfilter/palette{use, gen}: update Copyright after recent changes Clément Bœsch
2022-11-05 15:44 ` [FFmpeg-devel] Rework color quantization in palette{gen,use} Paul B Mahol
2022-11-05 18:54 ` Clément Bœsch
2022-11-06 13:19 ` Ronald S. Bultje
2022-11-08 21:22 ` Clément Bœsch
2022-11-05 21:52 ` Soft Works
2022-11-06 17:09 ` Michael Niedermayer
2022-11-06 17:30 ` Michael Niedermayer
2022-11-08 21:14 ` Clément Bœsch
2022-12-31 12:11 ` Clément Bœsch
2023-01-02 21:57 ` Michael Niedermayer
2023-01-02 23:05 ` Clément Bœsch
2023-01-03 18:50 ` Michael Niedermayer
2022-11-06 19:46 ` Soft Works
2022-11-08 21:07 ` Clément Bœsch
2022-11-08 22:37 ` Soft Works
2022-12-27 23:31 ` Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 01/32] avfilter/palettegen: allow a minimum of 2 colors Clément Bœsch
2022-12-28 21:04 ` Tomas Härdin
2022-12-28 21:23 ` Clément Bœsch
2023-01-03 18:59 ` Tomas Härdin
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 02/32] avfilter/palette{gen, use}: revert support palettes with alpha Clément Bœsch
2023-01-03 19:11 ` Paul B Mahol
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 03/32] avfilter/palette{gen, use}: simplify a few alpha masks Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 04/32] avfilter/palette{gen, use}: add palette utils Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 05/32] avfilter/paletteuse: switch from u8[4] to u32 for color code Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 06/32] avfilter/paletteuse: name target color arg consistently in colormap functions Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 07/32] avfilter/paletteuse: remove unused alpha split dimension Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 08/32] avfilter/paletteuse: remove redundant alpha condition Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 09/32] avfilter/paletteuse: switch to a perceptual model Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 10/32] avfilter/palettegen: move box stats computation to a dedicated function Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 11/32] avfilter/palettegen: define the best axis to cut using the squared error Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 12/32] avfilter/palettegen: use box->major_axis without intermediate variable Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 13/32] avfilter/palettegen: always compute the box variance Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 14/32] avfilter/palettegen: rename variance to cut_score Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 15/32] avfilter/palettegen: change cut score from ∑e² to max e² Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 16/32] avfilter/palettegen: compute average color within compute_box_stats() Clément Bœsch
2022-12-27 23:17 ` [FFmpeg-devel] [PATCH v2 17/32] avfilter/palettegen: misc cosmetics Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 18/32] avfilter/palettegen: rename local variable box_weight to weight Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 19/32] avfilter/palettegen: switch to signed arithmetic Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 20/32] avfilter/palettegen: base box split decision on a perceptual model Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 21/32] avfilter/palettegen: add a warning about supporting only sRGB Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 22/32] avfilter/palettegen: make refs order deterministic Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 23/32] avfilter/palettegen: use libc qsort Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 24/32] avfilter/palette{gen, use}: update Copyright after recent changes Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 25/32] avfilter/palette: add lowbias32 hashing Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 26/32] avfilter/palettegen: use lowbias32 for color hashing Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 27/32] avfilter/paletteuse: " Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 28/32] avfilter/paletteuse: switch to recursive method Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 29/32] avfilter/paletteuse: remove alternative search methods Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 30/32] avfilter/paletteuse: remove mean error tool Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 31/32] avfilter/paletteuse: move r, g, b computation in a more local scope Clément Bœsch
2022-12-27 23:18 ` [FFmpeg-devel] [PATCH v2 32/32] avfilter/palette{gen, use}: misc for-loop cosmetics Clément Bœsch
2023-01-03 16:28 ` [FFmpeg-devel] New iteration for the color quantization in palette{gen, use} Clément Bœsch
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git