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  if (!fgets(line, sizeof(line), f))
318  return AVERROR_INVALIDDATA;
319  for (k = 0; k < size; k++) {
320  for (j = 0; j < size; j++) {
321  for (i = 0; i < size; i++) {
322  int r, g, b;
323  struct rgbvec *vec = &lut3d->lut[k][j][i];
324 
325  NEXT_LINE(skip_line(line));
326  if (sscanf(line, "%d %d %d", &r, &g, &b) != 3)
327  return AVERROR_INVALIDDATA;
328  vec->r = r / scale;
329  vec->g = g / scale;
330  vec->b = b / scale;
331  }
332  }
333  }
334  return 0;
335 }
336 
337 /* Pandora format */
338 static int parse_m3d(AVFilterContext *ctx, FILE *f)
339 {
340  LUT3DContext *lut3d = ctx->priv;
341  float scale;
342  int i, j, k, size, in = -1, out = -1;
343  char line[MAX_LINE_SIZE];
344  uint8_t rgb_map[3] = {0, 1, 2};
345 
346  while (fgets(line, sizeof(line), f)) {
347  if (!strncmp(line, "in", 2)) in = strtol(line + 2, NULL, 0);
348  else if (!strncmp(line, "out", 3)) out = strtol(line + 3, NULL, 0);
349  else if (!strncmp(line, "values", 6)) {
350  const char *p = line + 6;
351 #define SET_COLOR(id) do { \
352  while (av_isspace(*p)) \
353  p++; \
354  switch (*p) { \
355  case 'r': rgb_map[id] = 0; break; \
356  case 'g': rgb_map[id] = 1; break; \
357  case 'b': rgb_map[id] = 2; break; \
358  } \
359  while (*p && !av_isspace(*p)) \
360  p++; \
361 } while (0)
362  SET_COLOR(0);
363  SET_COLOR(1);
364  SET_COLOR(2);
365  break;
366  }
367  }
368 
369  if (in == -1 || out == -1) {
370  av_log(ctx, AV_LOG_ERROR, "in and out must be defined\n");
371  return AVERROR_INVALIDDATA;
372  }
373  if (in < 2 || out < 2 ||
376  av_log(ctx, AV_LOG_ERROR, "invalid in (%d) or out (%d)\n", in, out);
377  return AVERROR_INVALIDDATA;
378  }
379  for (size = 1; size*size*size < in; size++);
380  lut3d->lutsize = size;
381  scale = 1. / (out - 1);
382 
383  for (k = 0; k < size; k++) {
384  for (j = 0; j < size; j++) {
385  for (i = 0; i < size; i++) {
386  struct rgbvec *vec = &lut3d->lut[k][j][i];
387  float val[3];
388 
389  NEXT_LINE(0);
390  if (sscanf(line, "%f %f %f", val, val + 1, val + 2) != 3)
391  return AVERROR_INVALIDDATA;
392  vec->r = val[rgb_map[0]] * scale;
393  vec->g = val[rgb_map[1]] * scale;
394  vec->b = val[rgb_map[2]] * scale;
395  }
396  }
397  }
398  return 0;
399 }
400 
401 static void set_identity_matrix(LUT3DContext *lut3d, int size)
402 {
403  int i, j, k;
404  const float c = 1. / (size - 1);
405 
406  lut3d->lutsize = size;
407  for (k = 0; k < size; k++) {
408  for (j = 0; j < size; j++) {
409  for (i = 0; i < size; i++) {
410  struct rgbvec *vec = &lut3d->lut[k][j][i];
411  vec->r = k * c;
412  vec->g = j * c;
413  vec->b = i * c;
414  }
415  }
416  }
417 }
418 
420 {
421  static const enum AVPixelFormat pix_fmts[] = {
430  };
432  return 0;
433 }
434 
435 static int config_input(AVFilterLink *inlink)
436 {
437  LUT3DContext *lut3d = inlink->dst->priv;
438  const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
439 
440  switch (inlink->format) {
441  case AV_PIX_FMT_RGB48:
442  case AV_PIX_FMT_BGR48:
443  case AV_PIX_FMT_RGBA64:
444  case AV_PIX_FMT_BGRA64:
445  lut3d->is16bit = 1;
446  }
447 
448  ff_fill_rgba_map(lut3d->rgba_map, inlink->format);
449  lut3d->step = av_get_padded_bits_per_pixel(desc) >> (3 + lut3d->is16bit);
450 
451 #define SET_FUNC(name) do { \
452  if (lut3d->is16bit) lut3d->interp_16 = interp_16_##name; \
453  else lut3d->interp_8 = interp_8_##name; \
454 } while (0)
455 
456  switch (lut3d->interpolation) {
457  case INTERPOLATE_NEAREST: SET_FUNC(nearest); break;
458  case INTERPOLATE_TRILINEAR: SET_FUNC(trilinear); break;
459  case INTERPOLATE_TETRAHEDRAL: SET_FUNC(tetrahedral); break;
460  default:
461  av_assert0(0);
462  }
463 
464  return 0;
465 }
466 
467 #define FILTER(nbits) do { \
468  uint8_t *dstrow = out->data[0]; \
469  const uint8_t *srcrow = in ->data[0]; \
470  \
471  for (y = 0; y < inlink->h; y++) { \
472  uint##nbits##_t *dst = (uint##nbits##_t *)dstrow; \
473  const uint##nbits##_t *src = (const uint##nbits##_t *)srcrow; \
474  for (x = 0; x < inlink->w * step; x += step) { \
475  struct rgbvec vec = lut3d->interp_##nbits(lut3d, src[x + r], src[x + g], src[x + b]); \
476  dst[x + r] = av_clip_uint##nbits(vec.r * (float)((1<<nbits) - 1)); \
477  dst[x + g] = av_clip_uint##nbits(vec.g * (float)((1<<nbits) - 1)); \
478  dst[x + b] = av_clip_uint##nbits(vec.b * (float)((1<<nbits) - 1)); \
479  if (!direct && step == 4) \
480  dst[x + a] = src[x + a]; \
481  } \
482  dstrow += out->linesize[0]; \
483  srcrow += in ->linesize[0]; \
484  } \
485 } while (0)
486 
488 {
489  int x, y, direct = 0;
490  AVFilterContext *ctx = inlink->dst;
491  LUT3DContext *lut3d = ctx->priv;
492  AVFilterLink *outlink = inlink->dst->outputs[0];
493  AVFrame *out;
494  const int step = lut3d->step;
495  const uint8_t r = lut3d->rgba_map[R];
496  const uint8_t g = lut3d->rgba_map[G];
497  const uint8_t b = lut3d->rgba_map[B];
498  const uint8_t a = lut3d->rgba_map[A];
499 
500  if (av_frame_is_writable(in)) {
501  direct = 1;
502  out = in;
503  } else {
504  out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
505  if (!out) {
506  av_frame_free(&in);
507  return NULL;
508  }
509  av_frame_copy_props(out, in);
510  }
511 
512  if (lut3d->is16bit) FILTER(16);
513  else FILTER(8);
514 
515  if (!direct)
516  av_frame_free(&in);
517 
518  return out;
519 }
520 
521 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
522 {
523  AVFilterLink *outlink = inlink->dst->outputs[0];
524  AVFrame *out = apply_lut(inlink, in);
525  if (!out)
526  return AVERROR(ENOMEM);
527  return ff_filter_frame(outlink, out);
528 }
529 
530 #if CONFIG_LUT3D_FILTER
531 static const AVOption lut3d_options[] = {
532  { "file", "set 3D LUT file name", OFFSET(file), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS },
534 };
535 
536 AVFILTER_DEFINE_CLASS(lut3d);
537 
538 static av_cold int lut3d_init(AVFilterContext *ctx)
539 {
540  int ret;
541  FILE *f;
542  const char *ext;
543  LUT3DContext *lut3d = ctx->priv;
544 
545  if (!lut3d->file) {
546  set_identity_matrix(lut3d, 32);
547  return 0;
548  }
549 
550  f = fopen(lut3d->file, "r");
551  if (!f) {
552  ret = AVERROR(errno);
553  av_log(ctx, AV_LOG_ERROR, "%s: %s\n", lut3d->file, av_err2str(ret));
554  return ret;
555  }
556 
557  ext = strrchr(lut3d->file, '.');
558  if (!ext) {
559  av_log(ctx, AV_LOG_ERROR, "Unable to guess the format from the extension\n");
560  ret = AVERROR_INVALIDDATA;
561  goto end;
562  }
563  ext++;
564 
565  if (!av_strcasecmp(ext, "dat")) {
566  lut3d->lutsize = 33;
567  ret = parse_dat(ctx, f);
568  } else if (!av_strcasecmp(ext, "3dl")) {
569  ret = parse_3dl(ctx, f);
570  } else if (!av_strcasecmp(ext, "cube")) {
571  ret = parse_cube(ctx, f);
572  } else if (!av_strcasecmp(ext, "m3d")) {
573  ret = parse_m3d(ctx, f);
574  } else {
575  av_log(ctx, AV_LOG_ERROR, "Unrecognized '.%s' file type\n", ext);
576  ret = AVERROR(EINVAL);
577  }
578 
579  if (!ret && !lut3d->lutsize) {
580  av_log(ctx, AV_LOG_ERROR, "3D LUT is empty\n");
581  ret = AVERROR_INVALIDDATA;
582  }
583 
584 end:
585  fclose(f);
586  return ret;
587 }
588 
589 static const AVFilterPad lut3d_inputs[] = {
590  {
591  .name = "default",
592  .type = AVMEDIA_TYPE_VIDEO,
593  .filter_frame = filter_frame,
594  .config_props = config_input,
595  },
596  { NULL }
597 };
598 
599 static const AVFilterPad lut3d_outputs[] = {
600  {
601  .name = "default",
602  .type = AVMEDIA_TYPE_VIDEO,
603  },
604  { NULL }
605 };
606 
607 AVFilter avfilter_vf_lut3d = {
608  .name = "lut3d",
609  .description = NULL_IF_CONFIG_SMALL("Adjust colors using a 3D LUT."),
610  .priv_size = sizeof(LUT3DContext),
611  .init = lut3d_init,
613  .inputs = lut3d_inputs,
614  .outputs = lut3d_outputs,
615  .priv_class = &lut3d_class,
617 };
618 #endif
619 
620 #if CONFIG_HALDCLUT_FILTER
621 
622 static void update_clut(LUT3DContext *lut3d, const AVFrame *frame)
623 {
624  const uint8_t *data = frame->data[0];
625  const int linesize = frame->linesize[0];
626  const int w = lut3d->clut_width;
627  const int step = lut3d->clut_step;
628  const uint8_t *rgba_map = lut3d->clut_rgba_map;
629  const int level = lut3d->lutsize;
630 
631 #define LOAD_CLUT(nbits) do { \
632  int i, j, k, x = 0, y = 0; \
633  \
634  for (k = 0; k < level; k++) { \
635  for (j = 0; j < level; j++) { \
636  for (i = 0; i < level; i++) { \
637  const uint##nbits##_t *src = (const uint##nbits##_t *) \
638  (data + y*linesize + x*step); \
639  struct rgbvec *vec = &lut3d->lut[k][j][i]; \
640  vec->r = src[rgba_map[0]] / (float)((1<<(nbits)) - 1); \
641  vec->g = src[rgba_map[1]] / (float)((1<<(nbits)) - 1); \
642  vec->b = src[rgba_map[2]] / (float)((1<<(nbits)) - 1); \
643  if (++x == w) { \
644  x = 0; \
645  y++; \
646  } \
647  } \
648  } \
649  } \
650 } while (0)
651 
652  if (!lut3d->clut_is16bit) LOAD_CLUT(8);
653  else LOAD_CLUT(16);
654 }
655 
656 
657 static int config_output(AVFilterLink *outlink)
658 {
659  AVFilterContext *ctx = outlink->src;
660 
661  outlink->w = ctx->inputs[0]->w;
662  outlink->h = ctx->inputs[0]->h;
663  outlink->time_base = ctx->inputs[0]->time_base;
664  return 0;
665 }
666 
667 static int filter_frame_main(AVFilterLink *inlink, AVFrame *inpicref)
668 {
669  LUT3DContext *s = inlink->dst->priv;
670  return ff_dualinput_filter_frame_main(&s->dinput, inlink, inpicref);
671 }
672 
673 static int filter_frame_clut(AVFilterLink *inlink, AVFrame *inpicref)
674 {
675  LUT3DContext *s = inlink->dst->priv;
676  return ff_dualinput_filter_frame_second(&s->dinput, inlink, inpicref);
677 }
678 
679 static int request_frame(AVFilterLink *outlink)
680 {
681  LUT3DContext *s = outlink->src->priv;
682  return ff_dualinput_request_frame(&s->dinput, outlink);
683 }
684 
685 static int config_clut(AVFilterLink *inlink)
686 {
687  int size, level, w, h;
688  AVFilterContext *ctx = inlink->dst;
689  LUT3DContext *lut3d = ctx->priv;
690  const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
691 
692  lut3d->clut_is16bit = 0;
693  switch (inlink->format) {
694  case AV_PIX_FMT_RGB48:
695  case AV_PIX_FMT_BGR48:
696  case AV_PIX_FMT_RGBA64:
697  case AV_PIX_FMT_BGRA64:
698  lut3d->clut_is16bit = 1;
699  }
700 
701  lut3d->clut_step = av_get_padded_bits_per_pixel(desc) >> 3;
702  ff_fill_rgba_map(lut3d->clut_rgba_map, inlink->format);
703 
704  if (inlink->w > inlink->h)
705  av_log(ctx, AV_LOG_INFO, "Padding on the right (%dpx) of the "
706  "Hald CLUT will be ignored\n", inlink->w - inlink->h);
707  else if (inlink->w < inlink->h)
708  av_log(ctx, AV_LOG_INFO, "Padding at the bottom (%dpx) of the "
709  "Hald CLUT will be ignored\n", inlink->h - inlink->w);
710  lut3d->clut_width = w = h = FFMIN(inlink->w, inlink->h);
711 
712  for (level = 1; level*level*level < w; level++);
713  size = level*level*level;
714  if (size != w) {
715  av_log(ctx, AV_LOG_WARNING, "The Hald CLUT width does not match the level\n");
716  return AVERROR_INVALIDDATA;
717  }
718  av_assert0(w == h && w == size);
719  level *= level;
720  if (level > MAX_LEVEL) {
721  const int max_clut_level = sqrt(MAX_LEVEL);
722  const int max_clut_size = max_clut_level*max_clut_level*max_clut_level;
723  av_log(ctx, AV_LOG_ERROR, "Too large Hald CLUT "
724  "(maximum level is %d, or %dx%d CLUT)\n",
725  max_clut_level, max_clut_size, max_clut_size);
726  return AVERROR(EINVAL);
727  }
728  lut3d->lutsize = level;
729 
730  return 0;
731 }
732 
733 static AVFrame *update_apply_clut(AVFilterContext *ctx, AVFrame *main,
734  const AVFrame *second)
735 {
736  AVFilterLink *inlink = ctx->inputs[0];
737  update_clut(ctx->priv, second);
738  return apply_lut(inlink, main);
739 }
740 
741 static av_cold int haldclut_init(AVFilterContext *ctx)
742 {
743  LUT3DContext *lut3d = ctx->priv;
744  lut3d->dinput.process = update_apply_clut;
745  return 0;
746 }
747 
748 static av_cold void haldclut_uninit(AVFilterContext *ctx)
749 {
750  LUT3DContext *lut3d = ctx->priv;
751  ff_dualinput_uninit(&lut3d->dinput);
752 }
753 
754 static const AVOption haldclut_options[] = {
755  { "shortest", "force termination when the shortest input terminates", OFFSET(dinput.shortest), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS },
756  { "repeatlast", "continue applying the last clut after eos", OFFSET(dinput.repeatlast), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, FLAGS },
758 };
759 
760 AVFILTER_DEFINE_CLASS(haldclut);
761 
762 static const AVFilterPad haldclut_inputs[] = {
763  {
764  .name = "main",
765  .type = AVMEDIA_TYPE_VIDEO,
766  .filter_frame = filter_frame_main,
767  .config_props = config_input,
768  },{
769  .name = "clut",
770  .type = AVMEDIA_TYPE_VIDEO,
771  .filter_frame = filter_frame_clut,
772  .config_props = config_clut,
773  },
774  { NULL }
775 };
776 
777 static const AVFilterPad haldclut_outputs[] = {
778  {
779  .name = "default",
780  .type = AVMEDIA_TYPE_VIDEO,
781  .request_frame = request_frame,
782  .config_props = config_output,
783  },
784  { NULL }
785 };
786 
787 AVFilter avfilter_vf_haldclut = {
788  .name = "haldclut",
789  .description = NULL_IF_CONFIG_SMALL("Adjust colors using a Hald CLUT."),
790  .priv_size = sizeof(LUT3DContext),
791  .init = haldclut_init,
792  .uninit = haldclut_uninit,
794  .inputs = haldclut_inputs,
795  .outputs = haldclut_outputs,
796  .priv_class = &haldclut_class,
798 };
799 #endif