FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
vf_lut3d.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013 Clément Bœsch
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 /**
22  * @file
23  * 3D Lookup table filter
24  */
25 
26 #include "libavutil/opt.h"
27 #include "libavutil/file.h"
28 #include "libavutil/intreadwrite.h"
29 #include "libavutil/avassert.h"
30 #include "libavutil/pixdesc.h"
31 #include "libavutil/avstring.h"
32 #include "avfilter.h"
33 #include "drawutils.h"
34 #include "dualinput.h"
35 #include "formats.h"
36 #include "internal.h"
37 #include "video.h"
38 
39 #define R 0
40 #define G 1
41 #define B 2
42 #define A 3
43 
49 };
50 
51 struct rgbvec {
52  float r, g, b;
53 };
54 
55 /* 3D LUT don't often go up to level 32, but it is common to have a Hald CLUT
56  * of 512x512 (64x64x64) */
57 #define MAX_LEVEL 64
58 
59 typedef struct LUT3DContext {
60  const AVClass *class;
62  char *file;
64  int step;
65  int is16bit;
66  struct rgbvec (*interp_8) (const struct LUT3DContext*, uint8_t, uint8_t, uint8_t);
67  struct rgbvec (*interp_16)(const struct LUT3DContext*, uint16_t, uint16_t, uint16_t);
69  int lutsize;
70 #if CONFIG_HALDCLUT_FILTER
71  uint8_t clut_rgba_map[4];
72  int clut_step;
73  int clut_is16bit;
74  int clut_width;
75  FFDualInputContext dinput;
76 #endif
77 } LUT3DContext;
78 
79 #define OFFSET(x) offsetof(LUT3DContext, x)
80 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
81 #define COMMON_OPTIONS \
82  { "interp", "select interpolation mode", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=INTERPOLATE_TETRAHEDRAL}, 0, NB_INTERP_MODE-1, FLAGS, "interp_mode" }, \
83  { "nearest", "use values from the nearest defined points", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_NEAREST}, INT_MIN, INT_MAX, FLAGS, "interp_mode" }, \
84  { "trilinear", "interpolate values using the 8 points defining a cube", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_TRILINEAR}, INT_MIN, INT_MAX, FLAGS, "interp_mode" }, \
85  { "tetrahedral", "interpolate values using a tetrahedron", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_TETRAHEDRAL}, INT_MIN, INT_MAX, FLAGS, "interp_mode" }, \
86  { NULL }
87 
88 static inline float lerpf(float v0, float v1, float f)
89 {
90  return v0 + (v1 - v0) * f;
91 }
92 
93 static inline struct rgbvec lerp(const struct rgbvec *v0, const struct rgbvec *v1, float f)
94 {
95  struct rgbvec v = {
96  lerpf(v0->r, v1->r, f), lerpf(v0->g, v1->g, f), lerpf(v0->b, v1->b, f)
97  };
98  return v;
99 }
100 
101 #define NEAR(x) ((int)((x) + .5))
102 #define PREV(x) ((int)(x))
103 #define NEXT(x) (FFMIN((int)(x) + 1, lut3d->lutsize - 1))
104 
105 /**
106  * Get the nearest defined point
107  */
108 static inline struct rgbvec interp_nearest(const LUT3DContext *lut3d,
109  const struct rgbvec *s)
110 {
111  return lut3d->lut[NEAR(s->r)][NEAR(s->g)][NEAR(s->b)];
112 }
113 
114 /**
115  * Interpolate using the 8 vertices of a cube
116  * @see https://en.wikipedia.org/wiki/Trilinear_interpolation
117  */
118 static inline struct rgbvec interp_trilinear(const LUT3DContext *lut3d,
119  const struct rgbvec *s)
120 {
121  const int prev[] = {PREV(s->r), PREV(s->g), PREV(s->b)};
122  const int next[] = {NEXT(s->r), NEXT(s->g), NEXT(s->b)};
123  const struct rgbvec d = {s->r - prev[0], s->g - prev[1], s->b - prev[2]};
124  const struct rgbvec c000 = lut3d->lut[prev[0]][prev[1]][prev[2]];
125  const struct rgbvec c001 = lut3d->lut[prev[0]][prev[1]][next[2]];
126  const struct rgbvec c010 = lut3d->lut[prev[0]][next[1]][prev[2]];
127  const struct rgbvec c011 = lut3d->lut[prev[0]][next[1]][next[2]];
128  const struct rgbvec c100 = lut3d->lut[next[0]][prev[1]][prev[2]];
129  const struct rgbvec c101 = lut3d->lut[next[0]][prev[1]][next[2]];
130  const struct rgbvec c110 = lut3d->lut[next[0]][next[1]][prev[2]];
131  const struct rgbvec c111 = lut3d->lut[next[0]][next[1]][next[2]];
132  const struct rgbvec c00 = lerp(&c000, &c100, d.r);
133  const struct rgbvec c10 = lerp(&c010, &c110, d.r);
134  const struct rgbvec c01 = lerp(&c001, &c101, d.r);
135  const struct rgbvec c11 = lerp(&c011, &c111, d.r);
136  const struct rgbvec c0 = lerp(&c00, &c10, d.g);
137  const struct rgbvec c1 = lerp(&c01, &c11, d.g);
138  const struct rgbvec c = lerp(&c0, &c1, d.b);
139  return c;
140 }
141 
142 /**
143  * Tetrahedral interpolation. Based on code found in Truelight Software Library paper.
144  * @see http://www.filmlight.ltd.uk/pdf/whitepapers/FL-TL-TN-0057-SoftwareLib.pdf
145  */
146 static inline struct rgbvec interp_tetrahedral(const LUT3DContext *lut3d,
147  const struct rgbvec *s)
148 {
149  const int prev[] = {PREV(s->r), PREV(s->g), PREV(s->b)};
150  const int next[] = {NEXT(s->r), NEXT(s->g), NEXT(s->b)};
151  const struct rgbvec d = {s->r - prev[0], s->g - prev[1], s->b - prev[2]};
152  const struct rgbvec c000 = lut3d->lut[prev[0]][prev[1]][prev[2]];
153  const struct rgbvec c111 = lut3d->lut[next[0]][next[1]][next[2]];
154  struct rgbvec c;
155  if (d.r > d.g) {
156  if (d.g > d.b) {
157  const struct rgbvec c100 = lut3d->lut[next[0]][prev[1]][prev[2]];
158  const struct rgbvec c110 = lut3d->lut[next[0]][next[1]][prev[2]];
159  c.r = (1-d.r) * c000.r + (d.r-d.g) * c100.r + (d.g-d.b) * c110.r + (d.b) * c111.r;
160  c.g = (1-d.r) * c000.g + (d.r-d.g) * c100.g + (d.g-d.b) * c110.g + (d.b) * c111.g;
161  c.b = (1-d.r) * c000.b + (d.r-d.g) * c100.b + (d.g-d.b) * c110.b + (d.b) * c111.b;
162  } else if (d.r > d.b) {
163  const struct rgbvec c100 = lut3d->lut[next[0]][prev[1]][prev[2]];
164  const struct rgbvec c101 = lut3d->lut[next[0]][prev[1]][next[2]];
165  c.r = (1-d.r) * c000.r + (d.r-d.b) * c100.r + (d.b-d.g) * c101.r + (d.g) * c111.r;
166  c.g = (1-d.r) * c000.g + (d.r-d.b) * c100.g + (d.b-d.g) * c101.g + (d.g) * c111.g;
167  c.b = (1-d.r) * c000.b + (d.r-d.b) * c100.b + (d.b-d.g) * c101.b + (d.g) * c111.b;
168  } else {
169  const struct rgbvec c001 = lut3d->lut[prev[0]][prev[1]][next[2]];
170  const struct rgbvec c101 = lut3d->lut[next[0]][prev[1]][next[2]];
171  c.r = (1-d.b) * c000.r + (d.b-d.r) * c001.r + (d.r-d.g) * c101.r + (d.g) * c111.r;
172  c.g = (1-d.b) * c000.g + (d.b-d.r) * c001.g + (d.r-d.g) * c101.g + (d.g) * c111.g;
173  c.b = (1-d.b) * c000.b + (d.b-d.r) * c001.b + (d.r-d.g) * c101.b + (d.g) * c111.b;
174  }
175  } else {
176  if (d.b > d.g) {
177  const struct rgbvec c001 = lut3d->lut[prev[0]][prev[1]][next[2]];
178  const struct rgbvec c011 = lut3d->lut[prev[0]][next[1]][next[2]];
179  c.r = (1-d.b) * c000.r + (d.b-d.g) * c001.r + (d.g-d.r) * c011.r + (d.r) * c111.r;
180  c.g = (1-d.b) * c000.g + (d.b-d.g) * c001.g + (d.g-d.r) * c011.g + (d.r) * c111.g;
181  c.b = (1-d.b) * c000.b + (d.b-d.g) * c001.b + (d.g-d.r) * c011.b + (d.r) * c111.b;
182  } else if (d.b > d.r) {
183  const struct rgbvec c010 = lut3d->lut[prev[0]][next[1]][prev[2]];
184  const struct rgbvec c011 = lut3d->lut[prev[0]][next[1]][next[2]];
185  c.r = (1-d.g) * c000.r + (d.g-d.b) * c010.r + (d.b-d.r) * c011.r + (d.r) * c111.r;
186  c.g = (1-d.g) * c000.g + (d.g-d.b) * c010.g + (d.b-d.r) * c011.g + (d.r) * c111.g;
187  c.b = (1-d.g) * c000.b + (d.g-d.b) * c010.b + (d.b-d.r) * c011.b + (d.r) * c111.b;
188  } else {
189  const struct rgbvec c010 = lut3d->lut[prev[0]][next[1]][prev[2]];
190  const struct rgbvec c110 = lut3d->lut[next[0]][next[1]][prev[2]];
191  c.r = (1-d.g) * c000.r + (d.g-d.r) * c010.r + (d.r-d.b) * c110.r + (d.b) * c111.r;
192  c.g = (1-d.g) * c000.g + (d.g-d.r) * c010.g + (d.r-d.b) * c110.g + (d.b) * c111.g;
193  c.b = (1-d.g) * c000.b + (d.g-d.r) * c010.b + (d.r-d.b) * c110.b + (d.b) * c111.b;
194  }
195  }
196  return c;
197 }
198 
199 #define DEFINE_INTERP_FUNC(name, nbits) \
200 static struct rgbvec interp_##nbits##_##name(const LUT3DContext *lut3d, \
201  uint##nbits##_t r, \
202  uint##nbits##_t g, \
203  uint##nbits##_t b) \
204 { \
205  const float scale = (1. / ((1<<nbits) - 1)) * (lut3d->lutsize - 1); \
206  const struct rgbvec scaled_rgb = {r * scale, g * scale, b * scale}; \
207  return interp_##name(lut3d, &scaled_rgb); \
208 }
209 
210 DEFINE_INTERP_FUNC(nearest, 8)
211 DEFINE_INTERP_FUNC(trilinear, 8)
212 DEFINE_INTERP_FUNC(tetrahedral, 8)
213 
214 DEFINE_INTERP_FUNC(nearest, 16)
215 DEFINE_INTERP_FUNC(trilinear, 16)
216 DEFINE_INTERP_FUNC(tetrahedral, 16)
217 
218 #define MAX_LINE_SIZE 512
219 
220 static int skip_line(const char *p)
221 {
222  while (*p && av_isspace(*p))
223  p++;
224  return !*p || *p == '#';
225 }
226 
227 #define NEXT_LINE(loop_cond) do { \
228  if (!fgets(line, sizeof(line), f)) { \
229  av_log(ctx, AV_LOG_ERROR, "Unexpected EOF\n"); \
230  return AVERROR_INVALIDDATA; \
231  } \
232 } while (loop_cond)
233 
234 /* Basically r g and b float values on each line; seems to be generated by
235  * Davinci */
236 static int parse_dat(AVFilterContext *ctx, FILE *f)
237 {
238  LUT3DContext *lut3d = ctx->priv;
239  const int size = lut3d->lutsize;
240  int i, j, k;
241 
242  for (k = 0; k < size; k++) {
243  for (j = 0; j < size; j++) {
244  for (i = 0; i < size; i++) {
245  char line[MAX_LINE_SIZE];
246  struct rgbvec *vec = &lut3d->lut[k][j][i];
247  NEXT_LINE(skip_line(line));
248  sscanf(line, "%f %f %f", &vec->r, &vec->g, &vec->b);
249  }
250  }
251  }
252  return 0;
253 }
254 
255 /* Iridas format */
256 static int parse_cube(AVFilterContext *ctx, FILE *f)
257 {
258  LUT3DContext *lut3d = ctx->priv;
259  char line[MAX_LINE_SIZE];
260  float min[3] = {0.0, 0.0, 0.0};
261  float max[3] = {1.0, 1.0, 1.0};
262 
263  while (fgets(line, sizeof(line), f)) {
264  if (!strncmp(line, "LUT_3D_SIZE ", 12)) {
265  int i, j, k;
266  const int size = strtol(line + 12, NULL, 0);
267 
268  if (size < 2 || size > MAX_LEVEL) {
269  av_log(ctx, AV_LOG_ERROR, "Too large or invalid 3D LUT size\n");
270  return AVERROR(EINVAL);
271  }
272  lut3d->lutsize = size;
273  for (k = 0; k < size; k++) {
274  for (j = 0; j < size; j++) {
275  for (i = 0; i < size; i++) {
276  struct rgbvec *vec = &lut3d->lut[k][j][i];
277 
278  do {
279  NEXT_LINE(0);
280  if (!strncmp(line, "DOMAIN_", 7)) {
281  float *vals = NULL;
282  if (!strncmp(line + 7, "MIN ", 4)) vals = min;
283  else if (!strncmp(line + 7, "MAX ", 4)) vals = max;
284  if (!vals)
285  return AVERROR_INVALIDDATA;
286  sscanf(line + 11, "%f %f %f", vals, vals + 1, vals + 2);
287  av_log(ctx, AV_LOG_DEBUG, "min: %f %f %f | max: %f %f %f\n",
288  min[0], min[1], min[2], max[0], max[1], max[2]);
289  continue;
290  }
291  } while (skip_line(line));
292  if (sscanf(line, "%f %f %f", &vec->r, &vec->g, &vec->b) != 3)
293  return AVERROR_INVALIDDATA;
294  vec->r *= max[0] - min[0];
295  vec->g *= max[1] - min[1];
296  vec->b *= max[2] - min[2];
297  }
298  }
299  }
300  break;
301  }
302  }
303  return 0;
304 }
305 
306 /* Assume 17x17x17 LUT with a 16-bit depth
307  * FIXME: it seems there are various 3dl formats */
308 static int parse_3dl(AVFilterContext *ctx, FILE *f)
309 {
310  char line[MAX_LINE_SIZE];
311  LUT3DContext *lut3d = ctx->priv;
312  int i, j, k;
313  const int size = 17;
314  const float scale = 16*16*16;
315 
316  lut3d->lutsize = size;
317  NEXT_LINE(skip_line(line));
318  for (k = 0; k < size; k++) {
319  for (j = 0; j < size; j++) {
320  for (i = 0; i < size; i++) {
321  int r, g, b;
322  struct rgbvec *vec = &lut3d->lut[k][j][i];
323 
324  NEXT_LINE(skip_line(line));
325  if (sscanf(line, "%d %d %d", &r, &g, &b) != 3)
326  return AVERROR_INVALIDDATA;
327  vec->r = r / scale;
328  vec->g = g / scale;
329  vec->b = b / scale;
330  }
331  }
332  }
333  return 0;
334 }
335 
336 /* Pandora format */
337 static int parse_m3d(AVFilterContext *ctx, FILE *f)
338 {
339  LUT3DContext *lut3d = ctx->priv;
340  float scale;
341  int i, j, k, size, in = -1, out = -1;
342  char line[MAX_LINE_SIZE];
343  uint8_t rgb_map[3] = {0, 1, 2};
344 
345  while (fgets(line, sizeof(line), f)) {
346  if (!strncmp(line, "in", 2)) in = strtol(line + 2, NULL, 0);
347  else if (!strncmp(line, "out", 3)) out = strtol(line + 3, NULL, 0);
348  else if (!strncmp(line, "values", 6)) {
349  const char *p = line + 6;
350 #define SET_COLOR(id) do { \
351  while (av_isspace(*p)) \
352  p++; \
353  switch (*p) { \
354  case 'r': rgb_map[id] = 0; break; \
355  case 'g': rgb_map[id] = 1; break; \
356  case 'b': rgb_map[id] = 2; break; \
357  } \
358  while (*p && !av_isspace(*p)) \
359  p++; \
360 } while (0)
361  SET_COLOR(0);
362  SET_COLOR(1);
363  SET_COLOR(2);
364  break;
365  }
366  }
367 
368  if (in == -1 || out == -1) {
369  av_log(ctx, AV_LOG_ERROR, "in and out must be defined\n");
370  return AVERROR_INVALIDDATA;
371  }
372  if (in < 2 || out < 2 ||
375  av_log(ctx, AV_LOG_ERROR, "invalid in (%d) or out (%d)\n", in, out);
376  return AVERROR_INVALIDDATA;
377  }
378  for (size = 1; size*size*size < in; size++);
379  lut3d->lutsize = size;
380  scale = 1. / (out - 1);
381 
382  for (k = 0; k < size; k++) {
383  for (j = 0; j < size; j++) {
384  for (i = 0; i < size; i++) {
385  struct rgbvec *vec = &lut3d->lut[k][j][i];
386  float val[3];
387 
388  NEXT_LINE(0);
389  if (sscanf(line, "%f %f %f", val, val + 1, val + 2) != 3)
390  return AVERROR_INVALIDDATA;
391  vec->r = val[rgb_map[0]] * scale;
392  vec->g = val[rgb_map[1]] * scale;
393  vec->b = val[rgb_map[2]] * scale;
394  }
395  }
396  }
397  return 0;
398 }
399 
400 static void set_identity_matrix(LUT3DContext *lut3d, int size)
401 {
402  int i, j, k;
403  const float c = 1. / (size - 1);
404 
405  lut3d->lutsize = size;
406  for (k = 0; k < size; k++) {
407  for (j = 0; j < size; j++) {
408  for (i = 0; i < size; i++) {
409  struct rgbvec *vec = &lut3d->lut[k][j][i];
410  vec->r = k * c;
411  vec->g = j * c;
412  vec->b = i * c;
413  }
414  }
415  }
416 }
417 
419 {
420  static const enum AVPixelFormat pix_fmts[] = {
429  };
431  return 0;
432 }
433 
434 static int config_input(AVFilterLink *inlink)
435 {
436  LUT3DContext *lut3d = inlink->dst->priv;
437  const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
438 
439  switch (inlink->format) {
440  case AV_PIX_FMT_RGB48:
441  case AV_PIX_FMT_BGR48:
442  case AV_PIX_FMT_RGBA64:
443  case AV_PIX_FMT_BGRA64:
444  lut3d->is16bit = 1;
445  }
446 
447  ff_fill_rgba_map(lut3d->rgba_map, inlink->format);
448  lut3d->step = av_get_padded_bits_per_pixel(desc) >> (3 + lut3d->is16bit);
449 
450 #define SET_FUNC(name) do { \
451  if (lut3d->is16bit) lut3d->interp_16 = interp_16_##name; \
452  else lut3d->interp_8 = interp_8_##name; \
453 } while (0)
454 
455  switch (lut3d->interpolation) {
456  case INTERPOLATE_NEAREST: SET_FUNC(nearest); break;
457  case INTERPOLATE_TRILINEAR: SET_FUNC(trilinear); break;
458  case INTERPOLATE_TETRAHEDRAL: SET_FUNC(tetrahedral); break;
459  default:
460  av_assert0(0);
461  }
462 
463  return 0;
464 }
465 
466 #define FILTER(nbits) do { \
467  uint8_t *dstrow = out->data[0]; \
468  const uint8_t *srcrow = in ->data[0]; \
469  \
470  for (y = 0; y < inlink->h; y++) { \
471  uint##nbits##_t *dst = (uint##nbits##_t *)dstrow; \
472  const uint##nbits##_t *src = (const uint##nbits##_t *)srcrow; \
473  for (x = 0; x < inlink->w * step; x += step) { \
474  struct rgbvec vec = lut3d->interp_##nbits(lut3d, src[x + r], src[x + g], src[x + b]); \
475  dst[x + r] = av_clip_uint##nbits(vec.r * (float)((1<<nbits) - 1)); \
476  dst[x + g] = av_clip_uint##nbits(vec.g * (float)((1<<nbits) - 1)); \
477  dst[x + b] = av_clip_uint##nbits(vec.b * (float)((1<<nbits) - 1)); \
478  if (!direct && step == 4) \
479  dst[x + a] = src[x + a]; \
480  } \
481  dstrow += out->linesize[0]; \
482  srcrow += in ->linesize[0]; \
483  } \
484 } while (0)
485 
487 {
488  int x, y, direct = 0;
489  AVFilterContext *ctx = inlink->dst;
490  LUT3DContext *lut3d = ctx->priv;
491  AVFilterLink *outlink = inlink->dst->outputs[0];
492  AVFrame *out;
493  const int step = lut3d->step;
494  const uint8_t r = lut3d->rgba_map[R];
495  const uint8_t g = lut3d->rgba_map[G];
496  const uint8_t b = lut3d->rgba_map[B];
497  const uint8_t a = lut3d->rgba_map[A];
498 
499  if (av_frame_is_writable(in)) {
500  direct = 1;
501  out = in;
502  } else {
503  out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
504  if (!out) {
505  av_frame_free(&in);
506  return NULL;
507  }
508  av_frame_copy_props(out, in);
509  }
510 
511  if (lut3d->is16bit) FILTER(16);
512  else FILTER(8);
513 
514  if (!direct)
515  av_frame_free(&in);
516 
517  return out;
518 }
519 
520 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
521 {
522  AVFilterLink *outlink = inlink->dst->outputs[0];
523  AVFrame *out = apply_lut(inlink, in);
524  if (!out)
525  return AVERROR(ENOMEM);
526  return ff_filter_frame(outlink, out);
527 }
528 
529 #if CONFIG_LUT3D_FILTER
530 static const AVOption lut3d_options[] = {
531  { "file", "set 3D LUT file name", OFFSET(file), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
533 };
534 
535 AVFILTER_DEFINE_CLASS(lut3d);
536 
537 static av_cold int lut3d_init(AVFilterContext *ctx)
538 {
539  int ret;
540  FILE *f;
541  const char *ext;
542  LUT3DContext *lut3d = ctx->priv;
543 
544  if (!lut3d->file) {
545  set_identity_matrix(lut3d, 32);
546  return 0;
547  }
548 
549  f = fopen(lut3d->file, "r");
550  if (!f) {
551  ret = AVERROR(errno);
552  av_log(ctx, AV_LOG_ERROR, "%s: %s\n", lut3d->file, av_err2str(ret));
553  return ret;
554  }
555 
556  ext = strrchr(lut3d->file, '.');
557  if (!ext) {
558  av_log(ctx, AV_LOG_ERROR, "Unable to guess the format from the extension\n");
559  ret = AVERROR_INVALIDDATA;
560  goto end;
561  }
562  ext++;
563 
564  if (!av_strcasecmp(ext, "dat")) {
565  lut3d->lutsize = 33;
566  ret = parse_dat(ctx, f);
567  } else if (!av_strcasecmp(ext, "3dl")) {
568  ret = parse_3dl(ctx, f);
569  } else if (!av_strcasecmp(ext, "cube")) {
570  ret = parse_cube(ctx, f);
571  } else if (!av_strcasecmp(ext, "m3d")) {
572  ret = parse_m3d(ctx, f);
573  } else {
574  av_log(ctx, AV_LOG_ERROR, "Unrecognized '.%s' file type\n", ext);
575  ret = AVERROR(EINVAL);
576  }
577 
578  if (!ret && !lut3d->lutsize) {
579  av_log(ctx, AV_LOG_ERROR, "3D LUT is empty\n");
580  ret = AVERROR_INVALIDDATA;
581  }
582 
583 end:
584  fclose(f);
585  return ret;
586 }
587 
588 static const AVFilterPad lut3d_inputs[] = {
589  {
590  .name = "default",
591  .type = AVMEDIA_TYPE_VIDEO,
592  .filter_frame = filter_frame,
593  .config_props = config_input,
594  },
595  { NULL }
596 };
597 
598 static const AVFilterPad lut3d_outputs[] = {
599  {
600  .name = "default",
601  .type = AVMEDIA_TYPE_VIDEO,
602  },
603  { NULL }
604 };
605 
606 AVFilter avfilter_vf_lut3d = {
607  .name = "lut3d",
608  .description = NULL_IF_CONFIG_SMALL("Adjust colors using a 3D LUT."),
609  .priv_size = sizeof(LUT3DContext),
610  .init = lut3d_init,
612  .inputs = lut3d_inputs,
613  .outputs = lut3d_outputs,
614  .priv_class = &lut3d_class,
616 };
617 #endif
618 
619 #if CONFIG_HALDCLUT_FILTER
620 
621 static void update_clut(LUT3DContext *lut3d, const AVFrame *frame)
622 {
623  const uint8_t *data = frame->data[0];
624  const int linesize = frame->linesize[0];
625  const int w = lut3d->clut_width;
626  const int step = lut3d->clut_step;
627  const uint8_t *rgba_map = lut3d->clut_rgba_map;
628  const int level = lut3d->lutsize;
629 
630 #define LOAD_CLUT(nbits) do { \
631  int i, j, k, x = 0, y = 0; \
632  \
633  for (k = 0; k < level; k++) { \
634  for (j = 0; j < level; j++) { \
635  for (i = 0; i < level; i++) { \
636  const uint##nbits##_t *src = (const uint##nbits##_t *) \
637  (data + y*linesize + x*step); \
638  struct rgbvec *vec = &lut3d->lut[k][j][i]; \
639  vec->r = src[rgba_map[0]] / (float)((1<<(nbits)) - 1); \
640  vec->g = src[rgba_map[1]] / (float)((1<<(nbits)) - 1); \
641  vec->b = src[rgba_map[2]] / (float)((1<<(nbits)) - 1); \
642  if (++x == w) { \
643  x = 0; \
644  y++; \
645  } \
646  } \
647  } \
648  } \
649 } while (0)
650 
651  if (!lut3d->clut_is16bit) LOAD_CLUT(8);
652  else LOAD_CLUT(16);
653 }
654 
655 
656 static int config_output(AVFilterLink *outlink)
657 {
658  AVFilterContext *ctx = outlink->src;
659  LUT3DContext *lut3d = ctx->priv;
660  int ret;
661 
662  outlink->w = ctx->inputs[0]->w;
663  outlink->h = ctx->inputs[0]->h;
664  outlink->time_base = ctx->inputs[0]->time_base;
665  if ((ret = ff_dualinput_init(ctx, &lut3d->dinput)) < 0)
666  return ret;
667  return 0;
668 }
669 
670 static int filter_frame_hald(AVFilterLink *inlink, AVFrame *inpicref)
671 {
672  LUT3DContext *s = inlink->dst->priv;
673  return ff_dualinput_filter_frame(&s->dinput, inlink, inpicref);
674 }
675 
676 static int request_frame(AVFilterLink *outlink)
677 {
678  LUT3DContext *s = outlink->src->priv;
679  return ff_dualinput_request_frame(&s->dinput, outlink);
680 }
681 
682 static int config_clut(AVFilterLink *inlink)
683 {
684  int size, level, w, h;
685  AVFilterContext *ctx = inlink->dst;
686  LUT3DContext *lut3d = ctx->priv;
687  const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
688 
689  lut3d->clut_is16bit = 0;
690  switch (inlink->format) {
691  case AV_PIX_FMT_RGB48:
692  case AV_PIX_FMT_BGR48:
693  case AV_PIX_FMT_RGBA64:
694  case AV_PIX_FMT_BGRA64:
695  lut3d->clut_is16bit = 1;
696  }
697 
698  lut3d->clut_step = av_get_padded_bits_per_pixel(desc) >> 3;
699  ff_fill_rgba_map(lut3d->clut_rgba_map, inlink->format);
700 
701  if (inlink->w > inlink->h)
702  av_log(ctx, AV_LOG_INFO, "Padding on the right (%dpx) of the "
703  "Hald CLUT will be ignored\n", inlink->w - inlink->h);
704  else if (inlink->w < inlink->h)
705  av_log(ctx, AV_LOG_INFO, "Padding at the bottom (%dpx) of the "
706  "Hald CLUT will be ignored\n", inlink->h - inlink->w);
707  lut3d->clut_width = w = h = FFMIN(inlink->w, inlink->h);
708 
709  for (level = 1; level*level*level < w; level++);
710  size = level*level*level;
711  if (size != w) {
712  av_log(ctx, AV_LOG_WARNING, "The Hald CLUT width does not match the level\n");
713  return AVERROR_INVALIDDATA;
714  }
715  av_assert0(w == h && w == size);
716  level *= level;
717  if (level > MAX_LEVEL) {
718  const int max_clut_level = sqrt(MAX_LEVEL);
719  const int max_clut_size = max_clut_level*max_clut_level*max_clut_level;
720  av_log(ctx, AV_LOG_ERROR, "Too large Hald CLUT "
721  "(maximum level is %d, or %dx%d CLUT)\n",
722  max_clut_level, max_clut_size, max_clut_size);
723  return AVERROR(EINVAL);
724  }
725  lut3d->lutsize = level;
726 
727  return 0;
728 }
729 
730 static AVFrame *update_apply_clut(AVFilterContext *ctx, AVFrame *main,
731  const AVFrame *second)
732 {
733  AVFilterLink *inlink = ctx->inputs[0];
734  update_clut(ctx->priv, second);
735  return apply_lut(inlink, main);
736 }
737 
738 static av_cold int haldclut_init(AVFilterContext *ctx)
739 {
740  LUT3DContext *lut3d = ctx->priv;
741  lut3d->dinput.process = update_apply_clut;
742  return 0;
743 }
744 
745 static av_cold void haldclut_uninit(AVFilterContext *ctx)
746 {
747  LUT3DContext *lut3d = ctx->priv;
748  ff_dualinput_uninit(&lut3d->dinput);
749 }
750 
751 static const AVOption haldclut_options[] = {
752  { "shortest", "force termination when the shortest input terminates", OFFSET(dinput.shortest), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS },
753  { "repeatlast", "continue applying the last clut after eos", OFFSET(dinput.repeatlast), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, FLAGS },
755 };
756 
757 AVFILTER_DEFINE_CLASS(haldclut);
758 
759 static const AVFilterPad haldclut_inputs[] = {
760  {
761  .name = "main",
762  .type = AVMEDIA_TYPE_VIDEO,
763  .filter_frame = filter_frame_hald,
764  .config_props = config_input,
765  },{
766  .name = "clut",
767  .type = AVMEDIA_TYPE_VIDEO,
768  .filter_frame = filter_frame_hald,
769  .config_props = config_clut,
770  },
771  { NULL }
772 };
773 
774 static const AVFilterPad haldclut_outputs[] = {
775  {
776  .name = "default",
777  .type = AVMEDIA_TYPE_VIDEO,
778  .request_frame = request_frame,
779  .config_props = config_output,
780  },
781  { NULL }
782 };
783 
784 AVFilter avfilter_vf_haldclut = {
785  .name = "haldclut",
786  .description = NULL_IF_CONFIG_SMALL("Adjust colors using a Hald CLUT."),
787  .priv_size = sizeof(LUT3DContext),
788  .init = haldclut_init,
789  .uninit = haldclut_uninit,
791  .inputs = haldclut_inputs,
792  .outputs = haldclut_outputs,
793  .priv_class = &haldclut_class,
795 };
796 #endif