FFmpeg
vf_colorlevels.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013 Paul B Mahol
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include "libavutil/opt.h"
22 #include "libavutil/pixdesc.h"
23 #include "avfilter.h"
24 #include "drawutils.h"
25 #include "internal.h"
26 #include "video.h"
27 #include "preserve_color.h"
28 
29 #define R 0
30 #define G 1
31 #define B 2
32 #define A 3
33 
34 typedef struct Range {
35  double in_min, in_max;
36  double out_min, out_max;
37 } Range;
38 
39 typedef struct ColorLevelsContext {
40  const AVClass *class;
43 
44  int nb_comp;
45  int depth;
46  int max;
47  int planar;
48  int bpp;
49  int step;
50  uint8_t rgba_map[4];
51  int linesize;
52 
53  int (*colorlevels_slice[2])(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs);
55 
56 #define OFFSET(x) offsetof(ColorLevelsContext, x)
57 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
58 static const AVOption colorlevels_options[] = {
59  { "rimin", "set input red black point", OFFSET(range[R].in_min), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS },
60  { "gimin", "set input green black point", OFFSET(range[G].in_min), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS },
61  { "bimin", "set input blue black point", OFFSET(range[B].in_min), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS },
62  { "aimin", "set input alpha black point", OFFSET(range[A].in_min), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, FLAGS },
63  { "rimax", "set input red white point", OFFSET(range[R].in_max), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -1, 1, FLAGS },
64  { "gimax", "set input green white point", OFFSET(range[G].in_max), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -1, 1, FLAGS },
65  { "bimax", "set input blue white point", OFFSET(range[B].in_max), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -1, 1, FLAGS },
66  { "aimax", "set input alpha white point", OFFSET(range[A].in_max), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -1, 1, FLAGS },
67  { "romin", "set output red black point", OFFSET(range[R].out_min), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 1, FLAGS },
68  { "gomin", "set output green black point", OFFSET(range[G].out_min), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 1, FLAGS },
69  { "bomin", "set output blue black point", OFFSET(range[B].out_min), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 1, FLAGS },
70  { "aomin", "set output alpha black point", OFFSET(range[A].out_min), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 1, FLAGS },
71  { "romax", "set output red white point", OFFSET(range[R].out_max), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },
72  { "gomax", "set output green white point", OFFSET(range[G].out_max), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },
73  { "bomax", "set output blue white point", OFFSET(range[B].out_max), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },
74  { "aomax", "set output alpha white point", OFFSET(range[A].out_max), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS },
75  { "preserve", "set preserve color mode", OFFSET(preserve_color), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_PRESERVE-1, FLAGS, .unit = "preserve" },
76  { "none", "disabled", 0, AV_OPT_TYPE_CONST, {.i64=P_NONE}, 0, 0, FLAGS, .unit = "preserve" },
77  { "lum", "luminance", 0, AV_OPT_TYPE_CONST, {.i64=P_LUM}, 0, 0, FLAGS, .unit = "preserve" },
78  { "max", "max", 0, AV_OPT_TYPE_CONST, {.i64=P_MAX}, 0, 0, FLAGS, .unit = "preserve" },
79  { "avg", "average", 0, AV_OPT_TYPE_CONST, {.i64=P_AVG}, 0, 0, FLAGS, .unit = "preserve" },
80  { "sum", "sum", 0, AV_OPT_TYPE_CONST, {.i64=P_SUM}, 0, 0, FLAGS, .unit = "preserve" },
81  { "nrm", "norm", 0, AV_OPT_TYPE_CONST, {.i64=P_NRM}, 0, 0, FLAGS, .unit = "preserve" },
82  { "pwr", "power", 0, AV_OPT_TYPE_CONST, {.i64=P_PWR}, 0, 0, FLAGS, .unit = "preserve" },
83  { NULL }
84 };
85 
86 AVFILTER_DEFINE_CLASS(colorlevels);
87 
88 typedef struct ThreadData {
89  const uint8_t *srcrow[4];
90  uint8_t *dstrow[4];
92  int src_linesize;
93 
94  float coeff[4];
95 
96  int h;
97 
98  float fimin[4];
99  float fomin[4];
100  int imin[4];
101  int omin[4];
102 } ThreadData;
103 
104 #define DO_COMMON(type, ptype, clip, preserve, planar) \
105  const ThreadData *td = arg; \
106  const int linesize = s->linesize; \
107  const int step = s->step; \
108  const int process_h = td->h; \
109  const int slice_start = (process_h * jobnr ) / nb_jobs; \
110  const int slice_end = (process_h * (jobnr+1)) / nb_jobs; \
111  const int src_linesize = td->src_linesize / sizeof(type); \
112  const int dst_linesize = td->dst_linesize / sizeof(type); \
113  const type *src_r = (const type *)(td->srcrow[R]) + src_linesize * slice_start; \
114  const type *src_g = (const type *)(td->srcrow[G]) + src_linesize * slice_start; \
115  const type *src_b = (const type *)(td->srcrow[B]) + src_linesize * slice_start; \
116  const type *src_a = (const type *)(td->srcrow[A]) + src_linesize * slice_start; \
117  type *dst_r = (type *)(td->dstrow[R]) + src_linesize * slice_start; \
118  type *dst_g = (type *)(td->dstrow[G]) + src_linesize * slice_start; \
119  type *dst_b = (type *)(td->dstrow[B]) + src_linesize * slice_start; \
120  type *dst_a = (type *)(td->dstrow[A]) + src_linesize * slice_start; \
121  const ptype imin_r = s->depth == 32 ? td->fimin[R] : td->imin[R]; \
122  const ptype imin_g = s->depth == 32 ? td->fimin[G] : td->imin[G]; \
123  const ptype imin_b = s->depth == 32 ? td->fimin[B] : td->imin[B]; \
124  const ptype imin_a = s->depth == 32 ? td->fimin[A] : td->imin[A]; \
125  const ptype omin_r = s->depth == 32 ? td->fomin[R] : td->omin[R]; \
126  const ptype omin_g = s->depth == 32 ? td->fomin[G] : td->omin[G]; \
127  const ptype omin_b = s->depth == 32 ? td->fomin[B] : td->omin[B]; \
128  const ptype omin_a = s->depth == 32 ? td->fomin[A] : td->omin[A]; \
129  const float coeff_r = td->coeff[R]; \
130  const float coeff_g = td->coeff[G]; \
131  const float coeff_b = td->coeff[B]; \
132  const float coeff_a = td->coeff[A]; \
133  \
134  for (int y = slice_start; y < slice_end; y++) { \
135  for (int x = 0; x < linesize; x += step) { \
136  ptype ir, ig, ib, or, og, ob; \
137  ir = src_r[x]; \
138  ig = src_g[x]; \
139  ib = src_b[x]; \
140  if (preserve) { \
141  float ratio, icolor, ocolor, max = s->depth==32 ? 1.f : s->max; \
142  \
143  or = (ir - imin_r) * coeff_r + omin_r; \
144  og = (ig - imin_g) * coeff_g + omin_g; \
145  ob = (ib - imin_b) * coeff_b + omin_b; \
146  \
147  preserve_color(s->preserve_color, ir, ig, ib, or, og, ob, max, \
148  &icolor, &ocolor); \
149  if (ocolor > 0.f) { \
150  ratio = icolor / ocolor; \
151  \
152  or *= ratio; \
153  og *= ratio; \
154  ob *= ratio; \
155  } \
156  \
157  dst_r[x] = clip(or, depth); \
158  dst_g[x] = clip(og, depth); \
159  dst_b[x] = clip(ob, depth); \
160  } else { \
161  dst_r[x] = clip((ir - imin_r) * coeff_r + omin_r, depth); \
162  dst_g[x] = clip((ig - imin_g) * coeff_g + omin_g, depth); \
163  dst_b[x] = clip((ib - imin_b) * coeff_b + omin_b, depth); \
164  } \
165  } \
166  \
167  for (int x = 0; x < linesize && s->nb_comp == 4; x += step) \
168  dst_a[x] = clip((src_a[x] - imin_a) * coeff_a + omin_a, depth); \
169  \
170  src_r += src_linesize; \
171  src_g += src_linesize; \
172  src_b += src_linesize; \
173  src_a += src_linesize; \
174  \
175  dst_r += dst_linesize; \
176  dst_g += dst_linesize; \
177  dst_b += dst_linesize; \
178  dst_a += dst_linesize; \
179  }
180 
181 #define CLIP8(x, depth) av_clip_uint8(x)
182 #define CLIP16(x, depth) av_clip_uint16(x)
183 #define NOCLIP(x, depth) (x)
184 
185 static int colorlevels_slice_8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
186 {
187  ColorLevelsContext *s = ctx->priv;
188  DO_COMMON(uint8_t, int, CLIP8, 0, 0)
189  return 0;
190 }
191 
192 static int colorlevels_slice_16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
193 {
194  ColorLevelsContext *s = ctx->priv;\
195  DO_COMMON(uint16_t, int, CLIP16, 0, 0)
196  return 0;
197 }
198 
199 static int colorlevels_preserve_slice_8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
200 {
201  ColorLevelsContext *s = ctx->priv;
202  DO_COMMON(uint8_t, int, CLIP8, 1, 0)
203  return 0;
204 }
205 
206 static int colorlevels_preserve_slice_16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
207 {
208  ColorLevelsContext *s = ctx->priv;
209  DO_COMMON(uint16_t, int, CLIP16, 1, 0)
210  return 0;
211 }
212 
213 static int colorlevels_slice_8_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
214 {
215  ColorLevelsContext *s = ctx->priv;
216  DO_COMMON(uint8_t, int, CLIP8, 0, 1)
217  return 0;
218 }
219 
220 static int colorlevels_slice_9_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
221 {
222  ColorLevelsContext *s = ctx->priv;
223  const int depth = 9;
224  DO_COMMON(uint16_t, int, av_clip_uintp2, 0, 1)
225  return 0;
226 }
227 
228 static int colorlevels_slice_10_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
229 {
230  ColorLevelsContext *s = ctx->priv;
231  const int depth = 10;
232  DO_COMMON(uint16_t, int, av_clip_uintp2, 0, 1)
233  return 0;
234 }
235 
236 static int colorlevels_slice_12_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
237 {
238  ColorLevelsContext *s = ctx->priv;
239  const int depth = 12;
240  DO_COMMON(uint16_t, int, av_clip_uintp2, 0, 1)
241  return 0;
242 }
243 
244 static int colorlevels_slice_14_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
245 {
246  ColorLevelsContext *s = ctx->priv;
247  const int depth = 14;
248  DO_COMMON(uint16_t, int, av_clip_uintp2, 0, 1)
249  return 0;
250 }
251 
252 static int colorlevels_slice_16_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
253 {
254  ColorLevelsContext *s = ctx->priv;
255  DO_COMMON(uint16_t, int, CLIP16, 0, 1)
256  return 0;
257 }
258 
259 static int colorlevels_slice_32_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
260 {
261  ColorLevelsContext *s = ctx->priv;
262  DO_COMMON(float, float, NOCLIP, 0, 1)
263  return 0;
264 }
265 
266 static int colorlevels_preserve_slice_8_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
267 {
268  ColorLevelsContext *s = ctx->priv;
269  DO_COMMON(uint8_t, int, CLIP8, 1, 1)
270  return 0;
271 }
272 
273 static int colorlevels_preserve_slice_9_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
274 {
275  ColorLevelsContext *s = ctx->priv;
276  const int depth = 9;
277  DO_COMMON(uint16_t, int, av_clip_uintp2, 1, 1)
278  return 0;
279 }
280 
281 static int colorlevels_preserve_slice_10_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
282 {
283  ColorLevelsContext *s = ctx->priv;
284  const int depth = 10;
285  DO_COMMON(uint16_t, int, av_clip_uintp2, 1, 1)
286  return 0;
287 }
288 
289 static int colorlevels_preserve_slice_12_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
290 {
291  ColorLevelsContext *s = ctx->priv;
292  const int depth = 12;
293  DO_COMMON(uint16_t, int, av_clip_uintp2, 1, 1)
294  return 0;
295 }
296 
297 static int colorlevels_preserve_slice_14_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
298 {
299  ColorLevelsContext *s = ctx->priv;
300  const int depth = 14;
301  DO_COMMON(uint16_t, int, av_clip_uintp2, 1, 1)
302  return 0;
303 }
304 
305 static int colorlevels_preserve_slice_16_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
306 {
307  ColorLevelsContext *s = ctx->priv;
308  DO_COMMON(uint16_t, int, CLIP16, 1, 1)
309  return 0;
310 }
311 
312 static int colorlevels_preserve_slice_32_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
313 {
314  ColorLevelsContext *s = ctx->priv;
315  DO_COMMON(float, float, NOCLIP, 1, 1)
316  return 0;
317 }
318 
320 {
321  AVFilterContext *ctx = inlink->dst;
322  ColorLevelsContext *s = ctx->priv;
324 
325  s->nb_comp = desc->nb_components;
326  s->planar = desc->flags & AV_PIX_FMT_FLAG_PLANAR;
327  s->depth = desc->comp[0].depth;
328  s->max = (1 << s->depth) - 1;
329  s->bpp = (desc->comp[0].depth + 7) >> 3;
330  s->step = s->planar ? 1 : av_get_padded_bits_per_pixel(desc) >> (3 + (s->bpp == 2));
331  s->linesize = inlink->w * s->step;
332  ff_fill_rgba_map(s->rgba_map, inlink->format);
333 
334  if (!s->planar) {
335  s->colorlevels_slice[0] = colorlevels_slice_8;
336  s->colorlevels_slice[1] = colorlevels_preserve_slice_8;
337  if (s->bpp == 2) {
338  s->colorlevels_slice[0] = colorlevels_slice_16;
339  s->colorlevels_slice[1] = colorlevels_preserve_slice_16;
340  }
341  } else {
342  switch (s->depth) {
343  case 8:
344  s->colorlevels_slice[0] = colorlevels_slice_8_planar;
345  s->colorlevels_slice[1] = colorlevels_preserve_slice_8_planar;
346  break;
347  case 9:
348  s->colorlevels_slice[0] = colorlevels_slice_9_planar;
349  s->colorlevels_slice[1] = colorlevels_preserve_slice_9_planar;
350  break;
351  case 10:
352  s->colorlevels_slice[0] = colorlevels_slice_10_planar;
353  s->colorlevels_slice[1] = colorlevels_preserve_slice_10_planar;
354  break;
355  case 12:
356  s->colorlevels_slice[0] = colorlevels_slice_12_planar;
357  s->colorlevels_slice[1] = colorlevels_preserve_slice_12_planar;
358  break;
359  case 14:
360  s->colorlevels_slice[0] = colorlevels_slice_14_planar;
361  s->colorlevels_slice[1] = colorlevels_preserve_slice_14_planar;
362  break;
363  case 16:
364  s->colorlevels_slice[0] = colorlevels_slice_16_planar;
365  s->colorlevels_slice[1] = colorlevels_preserve_slice_16_planar;
366  break;
367  case 32:
368  s->colorlevels_slice[0] = colorlevels_slice_32_planar;
369  s->colorlevels_slice[1] = colorlevels_preserve_slice_32_planar;
370  break;
371  }
372  }
373 
374  return 0;
375 }
376 
378 {
379  AVFilterContext *ctx = inlink->dst;
380  ColorLevelsContext *s = ctx->priv;
381  AVFilterLink *outlink = ctx->outputs[0];
382  const int step = s->step;
383  ThreadData td;
384  AVFrame *out;
385 
386  if (av_frame_is_writable(in)) {
387  out = in;
388  } else {
389  out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
390  if (!out) {
391  av_frame_free(&in);
392  return AVERROR(ENOMEM);
393  }
395  }
396 
397  td.h = inlink->h;
398  td.dst_linesize = out->linesize[0];
399  td.src_linesize = in->linesize[0];
400  if (s->planar) {
401  td.srcrow[R] = in->data[2];
402  td.dstrow[R] = out->data[2];
403  td.srcrow[G] = in->data[0];
404  td.dstrow[G] = out->data[0];
405  td.srcrow[B] = in->data[1];
406  td.dstrow[B] = out->data[1];
407  td.srcrow[A] = in->data[3];
408  td.dstrow[A] = out->data[3];
409  } else {
410  td.srcrow[R] = in->data[0] + s->rgba_map[R] * s->bpp;
411  td.dstrow[R] = out->data[0] + s->rgba_map[R] * s->bpp;
412  td.srcrow[G] = in->data[0] + s->rgba_map[G] * s->bpp;
413  td.dstrow[G] = out->data[0] + s->rgba_map[G] * s->bpp;
414  td.srcrow[B] = in->data[0] + s->rgba_map[B] * s->bpp;
415  td.dstrow[B] = out->data[0] + s->rgba_map[B] * s->bpp;
416  td.srcrow[A] = in->data[0] + s->rgba_map[A] * s->bpp;
417  td.dstrow[A] = out->data[0] + s->rgba_map[A] * s->bpp;
418  }
419 
420  switch (s->bpp) {
421  case 1:
422  for (int i = 0; i < s->nb_comp; i++) {
423  Range *r = &s->range[i];
424  const uint8_t offset = s->rgba_map[i];
425  const uint8_t *srcrow = in->data[0];
426  int imin = lrint(r->in_min * UINT8_MAX);
427  int imax = lrint(r->in_max * UINT8_MAX);
428  int omin = lrint(r->out_min * UINT8_MAX);
429  int omax = lrint(r->out_max * UINT8_MAX);
430  float coeff;
431 
432  if (imin < 0) {
433  imin = UINT8_MAX;
434  for (int y = 0; y < inlink->h; y++) {
435  const uint8_t *src = srcrow;
436 
437  for (int x = 0; x < s->linesize; x += step)
438  imin = FFMIN(imin, src[x + offset]);
439  srcrow += in->linesize[0];
440  }
441  }
442  if (imax < 0) {
443  srcrow = in->data[0];
444  imax = 0;
445  for (int y = 0; y < inlink->h; y++) {
446  const uint8_t *src = srcrow;
447 
448  for (int x = 0; x < s->linesize; x += step)
449  imax = FFMAX(imax, src[x + offset]);
450  srcrow += in->linesize[0];
451  }
452  }
453 
454  coeff = (omax - omin) / (double)(imax - imin);
455 
456  td.coeff[i] = coeff;
457  td.imin[i] = imin;
458  td.omin[i] = omin;
459  }
460  break;
461  case 2:
462  for (int i = 0; i < s->nb_comp; i++) {
463  Range *r = &s->range[i];
464  const uint8_t offset = s->rgba_map[i];
465  const uint8_t *srcrow = in->data[0];
466  int imin = lrint(r->in_min * UINT16_MAX);
467  int imax = lrint(r->in_max * UINT16_MAX);
468  int omin = lrint(r->out_min * UINT16_MAX);
469  int omax = lrint(r->out_max * UINT16_MAX);
470  float coeff;
471 
472  if (imin < 0) {
473  imin = UINT16_MAX;
474  for (int y = 0; y < inlink->h; y++) {
475  const uint16_t *src = (const uint16_t *)srcrow;
476 
477  for (int x = 0; x < s->linesize; x += step)
478  imin = FFMIN(imin, src[x + offset]);
479  srcrow += in->linesize[0];
480  }
481  }
482  if (imax < 0) {
483  srcrow = in->data[0];
484  imax = 0;
485  for (int y = 0; y < inlink->h; y++) {
486  const uint16_t *src = (const uint16_t *)srcrow;
487 
488  for (int x = 0; x < s->linesize; x += step)
489  imax = FFMAX(imax, src[x + offset]);
490  srcrow += in->linesize[0];
491  }
492  }
493 
494  coeff = (omax - omin) / (double)(imax - imin);
495 
496  td.coeff[i] = coeff;
497  td.imin[i] = imin;
498  td.omin[i] = omin;
499  }
500  break;
501  case 4:
502  for (int i = 0; i < s->nb_comp; i++) {
503  Range *r = &s->range[i];
504  const uint8_t offset = s->rgba_map[i];
505  const uint8_t *srcrow = in->data[0];
506  float imin = r->in_min;
507  float imax = r->in_max;
508  float omin = r->out_min;
509  float omax = r->out_max;
510  float coeff;
511 
512  if (imin < 0.f) {
513  imin = 1.f;
514  for (int y = 0; y < inlink->h; y++) {
515  const float *src = (const float *)srcrow;
516 
517  for (int x = 0; x < s->linesize; x += step)
518  imin = fminf(imin, src[x + offset]);
519  srcrow += in->linesize[0];
520  }
521  }
522  if (imax < 0.f) {
523  srcrow = in->data[0];
524  imax = 0.f;
525  for (int y = 0; y < inlink->h; y++) {
526  const float *src = (const float *)srcrow;
527 
528  for (int x = 0; x < s->linesize; x += step)
529  imax = fmaxf(imax, src[x + offset]);
530  srcrow += in->linesize[0];
531  }
532  }
533 
534  coeff = (omax - omin) / (double)(imax - imin);
535 
536  td.coeff[i] = coeff;
537  td.fimin[i] = imin;
538  td.fomin[i] = omin;
539  }
540  break;
541  }
542 
543  ff_filter_execute(ctx, s->colorlevels_slice[s->preserve_color > 0], &td, NULL,
545 
546  if (in != out)
547  av_frame_free(&in);
548  return ff_filter_frame(outlink, out);
549 }
550 
551 static const AVFilterPad colorlevels_inputs[] = {
552  {
553  .name = "default",
554  .type = AVMEDIA_TYPE_VIDEO,
555  .filter_frame = filter_frame,
556  .config_props = config_input,
557  },
558 };
559 
561  .name = "colorlevels",
562  .description = NULL_IF_CONFIG_SMALL("Adjust the color levels."),
563  .priv_size = sizeof(ColorLevelsContext),
564  .priv_class = &colorlevels_class,
582  .process_command = ff_filter_process_command,
583 };
ff_get_video_buffer
AVFrame * ff_get_video_buffer(AVFilterLink *link, int w, int h)
Request a picture buffer with a specific set of permissions.
Definition: video.c:112
AV_PIX_FMT_GBRAP16
#define AV_PIX_FMT_GBRAP16
Definition: pixfmt.h:501
colorlevels_slice_8_planar
static int colorlevels_slice_8_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colorlevels.c:213
td
#define td
Definition: regdef.h:70
ThreadData::coeff
float coeff[4]
Definition: vf_colorlevels.c:94
ColorLevelsContext::linesize
int linesize
Definition: vf_colorlevels.c:51
r
const char * r
Definition: vf_curves.c:126
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
opt.h
preserve_color.h
P_AVG
@ P_AVG
Definition: preserve_color.h:30
out
FILE * out
Definition: movenc.c:54
ff_filter_frame
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition: avfilter.c:1018
av_pix_fmt_desc_get
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:2962
av_clip_uintp2
#define av_clip_uintp2
Definition: common.h:122
colorlevels_slice_16_planar
static int colorlevels_slice_16_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colorlevels.c:252
colorlevels_preserve_slice_16
static int colorlevels_preserve_slice_16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colorlevels.c:206
filter_frame
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
Definition: vf_colorlevels.c:377
inlink
The exact code depends on how similar the blocks are and how related they are to the and needs to apply these operations to the correct inlink or outlink if there are several Macros are available to factor that when no extra processing is inlink
Definition: filter_design.txt:212
ColorLevelsContext::preserve_color
int preserve_color
Definition: vf_colorlevels.c:42
av_frame_free
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:130
ThreadData::srcrow
const uint8_t * srcrow[4]
Definition: vf_colorlevels.c:89
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:344
pixdesc.h
colorlevels_inputs
static const AVFilterPad colorlevels_inputs[]
Definition: vf_colorlevels.c:551
step
trying all byte sequences megabyte in length and selecting the best looking sequence will yield cases to try But a word about which is also called distortion Distortion can be quantified by almost any quality measurement one chooses the sum of squared differences is used but more complex methods that consider psychovisual effects can be used as well It makes no difference in this discussion First step
Definition: rate_distortion.txt:58
preserve_color
static void preserve_color(int preserve_color, float ir, float ig, float ib, float r, float g, float b, float max, float *icolor, float *ocolor)
Definition: preserve_color.h:53
AVOption
AVOption.
Definition: opt.h:346
colorlevels_slice_32_planar
static int colorlevels_slice_32_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colorlevels.c:259
AV_PIX_FMT_BGR24
@ AV_PIX_FMT_BGR24
packed RGB 8:8:8, 24bpp, BGRBGR...
Definition: pixfmt.h:76
AV_PIX_FMT_BGRA
@ AV_PIX_FMT_BGRA
packed BGRA 8:8:8:8, 32bpp, BGRABGRA...
Definition: pixfmt.h:102
colorlevels_slice_9_planar
static int colorlevels_slice_9_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colorlevels.c:220
FFMAX
#define FFMAX(a, b)
Definition: macros.h:47
P_MAX
@ P_MAX
Definition: preserve_color.h:29
AVFilter::name
const char * name
Filter name.
Definition: avfilter.h:170
video.h
colorlevels_preserve_slice_12_planar
static int colorlevels_preserve_slice_12_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colorlevels.c:289
config_input
static int config_input(AVFilterLink *inlink)
Definition: vf_colorlevels.c:319
A
#define A
Definition: vf_colorlevels.c:32
AVFrame::data
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition: frame.h:365
NOCLIP
#define NOCLIP(x, depth)
Definition: vf_colorlevels.c:183
P_PWR
@ P_PWR
Definition: preserve_color.h:33
ff_vf_colorlevels
const AVFilter ff_vf_colorlevels
Definition: vf_colorlevels.c:560
AV_PIX_FMT_GBRP14
#define AV_PIX_FMT_GBRP14
Definition: pixfmt.h:496
AV_PIX_FMT_GBRAP
@ AV_PIX_FMT_GBRAP
planar GBRA 4:4:4:4 32bpp
Definition: pixfmt.h:212
AV_PIX_FMT_GBRP10
#define AV_PIX_FMT_GBRP10
Definition: pixfmt.h:494
colorlevels_preserve_slice_8_planar
static int colorlevels_preserve_slice_8_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colorlevels.c:266
AVFilterPad
A filter pad used for either input or output.
Definition: internal.h:33
ColorLevelsContext
Definition: vf_colorlevels.c:39
ThreadData::imin
int imin[4]
Definition: vf_colorlevels.c:100
lrint
#define lrint
Definition: tablegen.h:53
colorlevels_slice_10_planar
static int colorlevels_slice_10_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colorlevels.c:228
ff_video_default_filterpad
const AVFilterPad ff_video_default_filterpad[1]
An AVFilterPad array whose only entry has name "default" and is of type AVMEDIA_TYPE_VIDEO.
Definition: video.c:37
P_NONE
@ P_NONE
Definition: preserve_color.h:27
AV_PIX_FMT_GBRAP10
#define AV_PIX_FMT_GBRAP10
Definition: pixfmt.h:498
s
#define s(width, name)
Definition: cbs_vp9.c:198
AV_PIX_FMT_GBRAP12
#define AV_PIX_FMT_GBRAP12
Definition: pixfmt.h:499
colorlevels_preserve_slice_8
static int colorlevels_preserve_slice_8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colorlevels.c:199
colorlevels_slice_16
static int colorlevels_slice_16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colorlevels.c:192
Range::in_min
double in_min
Definition: vf_colorlevels.c:35
AV_OPT_TYPE_DOUBLE
@ AV_OPT_TYPE_DOUBLE
Definition: opt.h:237
fminf
float fminf(float, float)
P_LUM
@ P_LUM
Definition: preserve_color.h:28
colorlevels_preserve_slice_14_planar
static int colorlevels_preserve_slice_14_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colorlevels.c:297
ctx
AVFormatContext * ctx
Definition: movenc.c:48
FILTER_INPUTS
#define FILTER_INPUTS(array)
Definition: internal.h:182
ThreadData::h
int h
Definition: vf_blend.c:59
AV_PIX_FMT_RGBA
@ AV_PIX_FMT_RGBA
packed RGBA 8:8:8:8, 32bpp, RGBARGBA...
Definition: pixfmt.h:100
arg
const char * arg
Definition: jacosubdec.c:67
AV_PIX_FMT_GBRP16
#define AV_PIX_FMT_GBRP16
Definition: pixfmt.h:497
ColorLevelsContext::range
Range range[4]
Definition: vf_colorlevels.c:41
AV_PIX_FMT_RGBA64
#define AV_PIX_FMT_RGBA64
Definition: pixfmt.h:468
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:66
AV_PIX_FMT_BGR48
#define AV_PIX_FMT_BGR48
Definition: pixfmt.h:469
NULL
#define NULL
Definition: coverity.c:32
av_frame_copy_props
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition: frame.c:679
colorlevels_preserve_slice_16_planar
static int colorlevels_preserve_slice_16_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colorlevels.c:305
ColorLevelsContext::planar
int planar
Definition: vf_colorlevels.c:47
AV_PIX_FMT_BGR0
@ AV_PIX_FMT_BGR0
packed BGR 8:8:8, 32bpp, BGRXBGRX... X=unused/undefined
Definition: pixfmt.h:265
colorlevels_preserve_slice_32_planar
static int colorlevels_preserve_slice_32_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colorlevels.c:312
AV_PIX_FMT_GBRP9
#define AV_PIX_FMT_GBRP9
Definition: pixfmt.h:493
AV_PIX_FMT_ABGR
@ AV_PIX_FMT_ABGR
packed ABGR 8:8:8:8, 32bpp, ABGRABGR...
Definition: pixfmt.h:101
Range
Definition: vf_colorbalance.c:37
CLIP8
#define CLIP8(x, depth)
Definition: vf_colorlevels.c:181
G
#define G
Definition: vf_colorlevels.c:30
OFFSET
#define OFFSET(x)
Definition: vf_colorlevels.c:56
f
f
Definition: af_crystalizer.c:121
AV_PIX_FMT_RGB24
@ AV_PIX_FMT_RGB24
packed RGB 8:8:8, 24bpp, RGBRGB...
Definition: pixfmt.h:75
NULL_IF_CONFIG_SMALL
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:106
ColorLevelsContext::rgba_map
uint8_t rgba_map[4]
Definition: vf_colorlevels.c:50
av_get_padded_bits_per_pixel
int av_get_padded_bits_per_pixel(const AVPixFmtDescriptor *pixdesc)
Return the number of bits per pixel for the pixel format described by pixdesc, including any padding ...
Definition: pixdesc.c:2927
FILTER_PIXFMTS
#define FILTER_PIXFMTS(...)
Definition: internal.h:168
fmaxf
float fmaxf(float, float)
AV_PIX_FMT_GBRPF32
#define AV_PIX_FMT_GBRPF32
Definition: pixfmt.h:508
AV_PIX_FMT_RGB48
#define AV_PIX_FMT_RGB48
Definition: pixfmt.h:464
DO_COMMON
#define DO_COMMON(type, ptype, clip, preserve, planar)
Definition: vf_colorlevels.c:104
av_frame_is_writable
int av_frame_is_writable(AVFrame *frame)
Check if the frame data is writable.
Definition: frame.c:615
AVFILTER_DEFINE_CLASS
AVFILTER_DEFINE_CLASS(colorlevels)
range
enum AVColorRange range
Definition: mediacodec_wrapper.c:2557
ff_filter_process_command
int ff_filter_process_command(AVFilterContext *ctx, const char *cmd, const char *arg, char *res, int res_len, int flags)
Generic processing of user supplied commands that are set in the same way as the filter options.
Definition: avfilter.c:890
B
#define B
Definition: vf_colorlevels.c:31
offset
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf offset
Definition: writing_filters.txt:86
AV_PIX_FMT_RGB0
@ AV_PIX_FMT_RGB0
packed RGB 8:8:8, 32bpp, RGBXRGBX... X=unused/undefined
Definition: pixfmt.h:263
internal.h
AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
#define AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
Some filters support a generic "enable" expression option that can be used to enable or disable a fil...
Definition: avfilter.h:147
AV_PIX_FMT_ARGB
@ AV_PIX_FMT_ARGB
packed ARGB 8:8:8:8, 32bpp, ARGBARGB...
Definition: pixfmt.h:99
colorlevels_slice_8
static int colorlevels_slice_8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colorlevels.c:185
AV_PIX_FMT_BGRA64
#define AV_PIX_FMT_BGRA64
Definition: pixfmt.h:473
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:255
AV_PIX_FMT_GBRP12
#define AV_PIX_FMT_GBRP12
Definition: pixfmt.h:495
ColorLevelsContext::bpp
int bpp
Definition: vf_colorlevels.c:48
ff_filter_get_nb_threads
int ff_filter_get_nb_threads(AVFilterContext *ctx)
Get number of threads for current filter instance.
Definition: avfilter.c:825
ThreadData
Used for passing data between threads.
Definition: dsddec.c:69
FFMIN
#define FFMIN(a, b)
Definition: macros.h:49
AVFilterPad::name
const char * name
Pad name.
Definition: internal.h:39
ColorLevelsContext::nb_comp
int nb_comp
Definition: vf_colorlevels.c:44
Range::out_min
double out_min
Definition: vf_colorlevels.c:36
colorlevels_preserve_slice_9_planar
static int colorlevels_preserve_slice_9_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colorlevels.c:273
AVFilter
Filter definition.
Definition: avfilter.h:166
AV_PIX_FMT_0BGR
@ AV_PIX_FMT_0BGR
packed BGR 8:8:8, 32bpp, XBGRXBGR... X=unused/undefined
Definition: pixfmt.h:264
P_SUM
@ P_SUM
Definition: preserve_color.h:31
ThreadData::dstrow
uint8_t * dstrow[4]
Definition: vf_colorlevels.c:90
ColorLevelsContext::colorlevels_slice
int(* colorlevels_slice[2])(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colorlevels.c:53
ThreadData::src_linesize
int src_linesize
Definition: vf_bm3d.c:55
colorlevels_options
static const AVOption colorlevels_options[]
Definition: vf_colorlevels.c:58
AV_OPT_TYPE_INT
@ AV_OPT_TYPE_INT
Definition: opt.h:235
avfilter.h
AV_PIX_FMT_GBRAPF32
#define AV_PIX_FMT_GBRAPF32
Definition: pixfmt.h:509
ColorLevelsContext::step
int step
Definition: vf_colorlevels.c:49
AV_PIX_FMT_FLAG_PLANAR
#define AV_PIX_FMT_FLAG_PLANAR
At least one pixel component is not in the first data plane.
Definition: pixdesc.h:132
ColorLevelsContext::max
int max
Definition: vf_colorlevels.c:46
AVFilterContext
An instance of a filter.
Definition: avfilter.h:407
FLAGS
#define FLAGS
Definition: vf_colorlevels.c:57
ThreadData::fomin
float fomin[4]
Definition: vf_colorlevels.c:99
AV_PIX_FMT_GBRP
@ AV_PIX_FMT_GBRP
planar GBR 4:4:4 24bpp
Definition: pixfmt.h:165
AVFILTER_FLAG_SLICE_THREADS
#define AVFILTER_FLAG_SLICE_THREADS
The filter supports multithreading by splitting frames into multiple parts and processing them concur...
Definition: avfilter.h:117
ColorLevelsContext::depth
int depth
Definition: vf_colorlevels.c:45
desc
const char * desc
Definition: libsvtav1.c:75
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
colorlevels_slice_12_planar
static int colorlevels_slice_12_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colorlevels.c:236
ThreadData::dst_linesize
int dst_linesize
Definition: vf_colorlevels.c:91
AVPixFmtDescriptor
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition: pixdesc.h:69
ThreadData::omin
int omin[4]
Definition: vf_colorlevels.c:101
ThreadData::fimin
float fimin[4]
Definition: vf_colorlevels.c:98
P_NRM
@ P_NRM
Definition: preserve_color.h:32
colorlevels_preserve_slice_10_planar
static int colorlevels_preserve_slice_10_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colorlevels.c:281
FILTER_OUTPUTS
#define FILTER_OUTPUTS(array)
Definition: internal.h:183
src
INIT_CLIP pixel * src
Definition: h264pred_template.c:418
ff_fill_rgba_map
int ff_fill_rgba_map(uint8_t *rgba_map, enum AVPixelFormat pix_fmt)
Definition: drawutils.c:35
AVFrame::linesize
int linesize[AV_NUM_DATA_POINTERS]
For video, a positive or negative value, which is typically indicating the size in bytes of each pict...
Definition: frame.h:389
AV_PIX_FMT_0RGB
@ AV_PIX_FMT_0RGB
packed RGB 8:8:8, 32bpp, XRGBXRGB... X=unused/undefined
Definition: pixfmt.h:262
coeff
static const double coeff[2][5]
Definition: vf_owdenoise.c:79
colorlevels_slice_14_planar
static int colorlevels_slice_14_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colorlevels.c:244
drawutils.h
NB_PRESERVE
@ NB_PRESERVE
Definition: preserve_color.h:34
ff_filter_execute
static av_always_inline int ff_filter_execute(AVFilterContext *ctx, avfilter_action_func *func, void *arg, int *ret, int nb_jobs)
Definition: internal.h:134
int
int
Definition: ffmpeg_filter.c:409
Range::in_max
double in_max
Definition: vf_colorlevels.c:35
AV_OPT_TYPE_CONST
@ AV_OPT_TYPE_CONST
Definition: opt.h:244
Range::out_max
double out_max
Definition: vf_colorlevels.c:36
CLIP16
#define CLIP16(x, depth)
Definition: vf_colorlevels.c:182
R
#define R
Definition: vf_colorlevels.c:29