|
|
|
@ -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) { |
|
|
|
|