1 /*
2 * Copyright (c) 2015 Stupeflix
3 * Copyright (c) 2022 Clément Bœsch <u pkh me>
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /**
23 * @file
24 * Generate one palette for a whole video stream.
25 */
26
37
38 /* Reference a color and how much it's used */
43 };
44
45 /* Store a range of colors */
47 uint32_t
color;
// average color
48 struct Lab avg;
// average color in perceptual OkLab space
49 int major_axis;
// best axis candidate for cutting the box
51 int64_t cut_score;
// how likely the box is to be cut down (higher implying more likely)
52 int start;
// index in PaletteGenContext->refs
53 int len;
// number of referenced colors
54 int sorted_by;
// whether range of colors is sorted by red (0), green (1) or blue (2)
55 };
56
60 };
61
62 enum {
67 };
68
69 #define HIST_SIZE (1<<15)
70
73
77
80 struct color_ref **
refs;
// references of all the colors used in the stream
81 int nb_refs;
// number of color references (or number of different colors)
82 struct range_box boxes[256];
// define the segmentation of the colorspace (the final palette)
83 int nb_boxes;
// number of boxes (increase will segmenting them)
84 int palette_pushed;
// if the palette frame is pushed into the outlink or not
87
88 #define OFFSET(x) offsetof(PaletteGenContext, x)
89 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
91 {
"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 },
92 {
"reserve_transparent",
"reserve a palette entry for transparency",
OFFSET(reserve_transparent),
AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1,
FLAGS },
93 {
"transparency_color",
"set a background color for transparency",
OFFSET(transparency_color),
AV_OPT_TYPE_COLOR, {.str=
"lime"}, 0, 0,
FLAGS },
99 };
100
102
106 {
110
115 return 0;
116 }
117
118 typedef int (*
cmp_func)(
const void *,
const void *);
119
120 #define DECLARE_CMP_FUNC(k0, k1, k2) \
121 static int cmp_##k0##k1##k2(const void *pa, const void *pb) \
122 { \
123 const struct color_ref * const *a = pa; \
124 const struct color_ref * const *b = pb; \
125 const int c0 = FFDIFFSIGN((*a)->lab.k0, (*b)->lab.k0); \
126 const int c1 = FFDIFFSIGN((*a)->lab.k1, (*b)->lab.k1); \
127 const int c2 = FFDIFFSIGN((*a)->lab.k2, (*b)->lab.k2); \
128 return c0 ? c0 : c1 ? c1 : c2; \
129 }
130
137
139 static const char *
const sortstr[] = {
"Lab",
"Lba",
"bLa",
"aLb",
"baL",
"abL" };
140
148 };
149
150 /*
151 * Return an identifier for the order of x, y, z (from higher to lower),
152 * preferring x over y and y over z in case of equality.
153 */
155 {
156 if (x >= y) {
157 if (y >= z)
return ID_XYZ;
158 if (x >= z)
return ID_XZY;
160 }
161 if (x >= z)
return ID_YXZ;
162 if (y >= z)
return ID_YZX;
164 }
165
166 /**
167 * Simple color comparison for sorting the final palette
168 */
170 {
174 }
175
177 {
179
180 /* Compute average color */
181 int64_t sL = 0, sa = 0, sb = 0;
185 sL +=
ref->lab.L *
ref->count;
186 sa +=
ref->lab.a *
ref->count;
187 sb +=
ref->lab.b *
ref->count;
189 }
193
194 /* Compute squared error of each color channel */
195 for (
int i = box->
start; i < box->start + box->
len;
i++) {
200 er2[0] += dL * dL *
ref->count;
201 er2[1] += da * da *
ref->count;
202 er2[2] += db * db *
ref->count;
203 }
204
205 /* Define the best axis candidate for cutting the box */
207
208 /* The box that has the axis with the biggest error amongst all boxes will but cut down */
210 }
211
212 /**
213 * Find the next box to split: pick the one with the highest cut score
214 */
216 {
217 int best_box_id = -1;
219
220 if (
s->nb_boxes ==
s->max_colors -
s->reserve_transparent)
221 return -1;
222
223 for (
int box_id = 0; box_id <
s->nb_boxes; box_id++) {
224 const struct range_box *box = &
s->boxes[box_id];
225 if (
s->boxes[box_id].len >= 2 && box->
cut_score > max_score) {
226 best_box_id = box_id;
228 }
229 }
230 return best_box_id;
231 }
232
233 /**
234 * Split given box in two at position n. The original box becomes the left part
235 * of the split, and the new index box is the right part.
236 */
238 {
239 struct range_box *new_box = &
s->boxes[
s->nb_boxes++];
240 new_box->
start = n + 1;
244
247
250 }
251
252 /**
253 * Write the palette into the output frame.
254 */
256 {
258 int box_id = 0;
259 uint32_t *pal = (uint32_t *)
out->data[0];
260 const int pal_linesize =
out->linesize[0] >> 2;
261 uint32_t last_color = 0;
262
263 for (
int y = 0; y <
out->height; y++) {
264 for (
int x = 0; x <
out->width; x++) {
265 if (box_id < s->nb_boxes) {
266 pal[x] =
s->boxes[box_id++].color;
267 if ((x || y) && pal[x] == last_color)
269 last_color = pal[x];
270 } else {
271 pal[x] = last_color; // pad with last color
272 }
273 }
274 pal += pal_linesize;
275 }
276
277 if (
s->reserve_transparent) {
279 pal[
out->width - pal_linesize - 1] =
AV_RB32(&
s->transparency_color) >> 8;
280 }
281 }
282
283 /**
284 * Crawl the histogram to get all the defined colors, and create a linear list
285 * of them (each color reference entry is a pointer to the value in the
286 * histogram/hash table).
287 */
289 {
290 int k = 0;
292
293 if (!refs)
295
298
301 }
302
303 return refs;
304 }
305
307 {
308 char buf[32];
309 const double ratio = (
double)nb_out / nb_in;
310 snprintf(buf,
sizeof(buf),
"%f", ratio);
312 return ratio;
313 }
314
315 /**
316 * Main function implementing the Median Cut Algorithm defined by Paul Heckbert
317 * in Color Image Quantization for Frame Buffer Display (1982)
318 */
320 {
324 double ratio;
325 int box_id = 0;
327
328 /* reference only the used colors from histogram */
333 }
334
335 /* create the palette frame */
340
341 /* set first box for 0..nb_refs */
342 box = &
s->boxes[box_id];
343 box->
len =
s->nb_refs;
347
348 while (box && box->
len > 1) {
351
352 ff_dlog(
ctx,
"box #%02X [%6d..%-6d] (%6d) w:%-6"PRIu64
" sort by %s (already sorted:%c) ",
355
356 /* sort the range by its major axis if it's not already sorted */
361 }
362
363 /* locate the median where to split */
364 median = (box->
weight + 1) >> 1;
366 /* if you have 2 boxes, the maximum is actually #0: you must have at
367 * least 1 color on each side of the split, hence the -2 */
371 break;
372 }
373 ff_dlog(
ctx,
"split @ i=%-6d with w=%-6"PRIu64
" (target=%6"PRIu64
")\n",
i,
weight, median);
375
377 box = box_id >= 0 ? &
s->boxes[box_id] :
NULL;
378 }
379
382 s->nb_boxes,
s->reserve_transparent ?
"(+1)" :
"",
s->nb_refs, ratio);
383
384 for (
int i = 0;
i <
s->nb_boxes;
i++)
386
387 qsort(
s->boxes,
s->nb_boxes,
sizeof(*
s->boxes),
cmp_color);
388
390
392 }
393
394 /**
395 * Locate the color in the hash table and increment its counter.
396 */
398 {
402
407 return 0;
408 }
409 }
410
413 if (!e)
418 return 1;
419 }
420
421 /**
422 * Update histogram when pixels differ from previous frame.
423 */
426 {
427 int x, y,
ret, nb_diff_colors = 0;
428
429 for (y = 0; y < f1->
height; y++) {
430 const uint32_t *
p = (
const uint32_t *)(f1->
data[0] + y*f1->
linesize[0]);
431 const uint32_t *q = (
const uint32_t *)(f2->
data[0] + y*f2->
linesize[0]);
432
433 for (x = 0; x < f1->
width; x++) {
435 continue;
439 nb_diff_colors +=
ret;
440 }
441 }
442 return nb_diff_colors;
443 }
444
445 /**
446 * Simple histogram of the frame.
447 */
449 {
450 int x, y,
ret, nb_diff_colors = 0;
451
452 for (y = 0; y <
f->height; y++) {
453 const uint32_t *
p = (
const uint32_t *)(
f->data[0] + y*
f->linesize[0]);
454
455 for (x = 0; x <
f->width; x++) {
459 nb_diff_colors +=
ret;
460 }
461 }
462 return nb_diff_colors;
463 }
464
465 /**
466 * Update the histogram for each passing frame. No frame will be pushed here.
467 */
469 {
473
476
481
488
498 memset(
s->boxes, 0,
sizeof(
s->boxes));
499 memset(
s->histogram, 0,
sizeof(
s->histogram));
500 } else {
502 }
503
505 }
506
507 /**
508 * Returns only one frame at the end containing the full palette.
509 */
511 {
516
520 s->palette_pushed = 1;
522 }
524 }
525
526 /**
527 * The output is one simple 16x16 squared-pixels palette.
528 */
530 {
531 outlink->
w = outlink->
h = 16;
533 return 0;
534 }
535
537 {
539
540 if (
s->max_colors -
s->reserve_transparent < 2) {
541 av_log(
ctx,
AV_LOG_ERROR,
"max_colors=2 is only allowed without reserving a transparent color slot\n");
543 }
544
545 return 0;
546 }
547
549 {
552
557 }
558
560 {
564 },
565 };
566
568 {
573 },
574 };
575
577 .
p.
name =
"palettegen",
579 .p.priv_class = &palettegen_class,
586 };