FFmpeg  4.4.8
vf_colorconstancy.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018 Mina Sami
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  * Color Constancy filter
24  *
25  * @see http://colorconstancy.com/
26  *
27  * @cite
28  * J. van de Weijer, Th. Gevers, A. Gijsenij "Edge-Based Color Constancy".
29  */
30 
31 #include "libavutil/imgutils.h"
32 #include "libavutil/opt.h"
33 #include "libavutil/pixdesc.h"
34 
35 #include "avfilter.h"
36 #include "filters.h"
37 #include "formats.h"
38 #include "internal.h"
39 #include "video.h"
40 
41 #include <math.h>
42 
43 #define GREY_EDGE "greyedge"
44 
45 #define SQRT3 1.73205080757
46 
47 #define NUM_PLANES 3
48 #define MAX_DIFF_ORD 2
49 #define MAX_META_DATA 4
50 #define MAX_DATA 4
51 
52 #define INDEX_TEMP 0
53 #define INDEX_DX 1
54 #define INDEX_DY 2
55 #define INDEX_DXY 3
56 #define INDEX_NORM INDEX_DX
57 #define INDEX_SRC 0
58 #define INDEX_DST 1
59 #define INDEX_ORD 2
60 #define INDEX_DIR 3
61 #define DIR_X 0
62 #define DIR_Y 1
63 
64 /**
65  * Used for passing data between threads.
66  */
67 typedef struct ThreadData {
68  AVFrame *in, *out;
71 } ThreadData;
72 
73 /**
74  * Common struct for all algorithms contexts.
75  */
76 typedef struct ColorConstancyContext {
77  const AVClass *class;
78 
79  int difford;
80  int minknorm; /**< @minknorm = 0 : getMax instead */
81  double sigma;
82 
84  int planeheight[4];
85  int planewidth[4];
86 
88  double *gauss[MAX_DIFF_ORD+1];
89 
90  double white[NUM_PLANES];
92 
93 #define OFFSET(x) offsetof(ColorConstancyContext, x)
94 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
95 
96 #define GINDX(s, i) ( (i) - ((s) >> 2) )
97 
98 /**
99  * Sets gauss filters used for calculating gauss derivatives. Filter size
100  * depends on sigma which is a user option hence we calculate these
101  * filters each time. Also each higher order depends on lower ones. Sigma
102  * can be zero only at difford = 0, then we only convert data to double
103  * instead.
104  *
105  * @param ctx the filter context.
106  *
107  * @return 0 in case of success, a negative value corresponding to an
108  * AVERROR code in case of failure.
109  */
111 {
112  ColorConstancyContext *s = ctx->priv;
113  int filtersize = s->filtersize;
114  int difford = s->difford;
115  double sigma = s->sigma;
116  double sum1, sum2;
117  int i;
118 
119  for (i = 0; i <= difford; ++i) {
120  s->gauss[i] = av_mallocz_array(filtersize, sizeof(*s->gauss[i]));
121  if (!s->gauss[i]) {
122  for (; i >= 0; --i) {
123  av_freep(&s->gauss[i]);
124  }
125  return AVERROR(ENOMEM);
126  }
127  }
128 
129  // Order 0
130  av_log(ctx, AV_LOG_TRACE, "Setting 0-d gauss with filtersize = %d.\n", filtersize);
131  sum1 = 0.0;
132  if (!sigma) {
133  s->gauss[0][0] = 1; // Copying data to double instead of convolution
134  } else {
135  for (i = 0; i < filtersize; ++i) {
136  s->gauss[0][i] = exp(- pow(GINDX(filtersize, i), 2.) / (2 * sigma * sigma)) / ( sqrt(2 * M_PI) * sigma );
137  sum1 += s->gauss[0][i];
138  }
139  for (i = 0; i < filtersize; ++i) {
140  s->gauss[0][i] /= sum1;
141  }
142  }
143  // Order 1
144  if (difford > 0) {
145  av_log(ctx, AV_LOG_TRACE, "Setting 1-d gauss with filtersize = %d.\n", filtersize);
146  sum1 = 0.0;
147  for (i = 0; i < filtersize; ++i) {
148  s->gauss[1][i] = - (GINDX(filtersize, i) / pow(sigma, 2)) * s->gauss[0][i];
149  sum1 += s->gauss[1][i] * GINDX(filtersize, i);
150  }
151 
152  for (i = 0; i < filtersize; ++i) {
153  s->gauss[1][i] /= sum1;
154  }
155 
156  // Order 2
157  if (difford > 1) {
158  av_log(ctx, AV_LOG_TRACE, "Setting 2-d gauss with filtersize = %d.\n", filtersize);
159  sum1 = 0.0;
160  for (i = 0; i < filtersize; ++i) {
161  s->gauss[2][i] = ( pow(GINDX(filtersize, i), 2) / pow(sigma, 4) - 1/pow(sigma, 2) )
162  * s->gauss[0][i];
163  sum1 += s->gauss[2][i];
164  }
165 
166  sum2 = 0.0;
167  for (i = 0; i < filtersize; ++i) {
168  s->gauss[2][i] -= sum1 / (filtersize);
169  sum2 += (0.5 * GINDX(filtersize, i) * GINDX(filtersize, i) * s->gauss[2][i]);
170  }
171  for (i = 0; i < filtersize ; ++i) {
172  s->gauss[2][i] /= sum2;
173  }
174  }
175  }
176  return 0;
177 }
178 
179 /**
180  * Frees up buffers used by grey edge for storing derivatives final
181  * and intermidiate results. Number of buffers and number of planes
182  * for last buffer are given so it can be safely called at allocation
183  * failure instances.
184  *
185  * @param td holds the buffers.
186  * @param nb_buff number of buffers to be freed.
187  * @param nb_planes number of planes for last buffer to be freed.
188  */
189 static void cleanup_derivative_buffers(ThreadData *td, int nb_buff, int nb_planes)
190 {
191  int b, p;
192 
193  for (b = 0; b < nb_buff; ++b) {
194  for (p = 0; p < NUM_PLANES; ++p) {
195  av_freep(&td->data[b][p]);
196  }
197  }
198  // Final buffer may not be fully allocated at fail cases
199  for (p = 0; p < nb_planes; ++p) {
200  av_freep(&td->data[b][p]);
201  }
202 }
203 
204 /**
205  * Allocates buffers used by grey edge for storing derivatives final
206  * and intermidiate results.
207  *
208  * @param ctx the filter context.
209  * @param td holds the buffers.
210  *
211  * @return 0 in case of success, a negative value corresponding to an
212  * AVERROR code in case of failure.
213  */
215 {
216  ColorConstancyContext *s = ctx->priv;
217  int nb_buff = s->difford + 1;
218  int b, p;
219 
220  av_log(ctx, AV_LOG_TRACE, "Allocating %d buffer(s) for grey edge.\n", nb_buff);
221  for (b = 0; b <= nb_buff; ++b) { // We need difford + 1 buffers
222  for (p = 0; p < NUM_PLANES; ++p) {
223  td->data[b][p] = av_mallocz_array(s->planeheight[p] * s->planewidth[p], sizeof(*td->data[b][p]));
224  if (!td->data[b][p]) {
226  return AVERROR(ENOMEM);
227  }
228  }
229  }
230  return 0;
231 }
232 
233 #define CLAMP(x, mx) av_clip((x), 0, (mx-1))
234 #define INDX2D(r, c, w) ( (r) * (w) + (c) )
235 #define GAUSS(s, sr, sc, sls, sh, sw, g) ( (s)[ INDX2D(CLAMP((sr), (sh)), CLAMP((sc), (sw)), (sls)) ] * (g) )
236 
237 /**
238  * Slice calculation of gaussian derivatives. Applies 1-D gaussian derivative filter
239  * either horizontally or vertically according to meta data given in thread data.
240  * When convoluting horizontally source is always the in frame withing thread data
241  * while when convoluting vertically source is a buffer.
242  *
243  * @param ctx the filter context.
244  * @param arg data to be passed between threads.
245  * @param jobnr current job nubmer.
246  * @param nb_jobs total number of jobs.
247  *
248  * @return 0.
249  */
250 static int slice_get_derivative(AVFilterContext* ctx, void* arg, int jobnr, int nb_jobs)
251 {
252  ColorConstancyContext *s = ctx->priv;
253  ThreadData *td = arg;
254  AVFrame *in = td->in;
255  const int ord = td->meta_data[INDEX_ORD];
256  const int dir = td->meta_data[INDEX_DIR];
257  const int src_index = td->meta_data[INDEX_SRC];
258  const int dst_index = td->meta_data[INDEX_DST];
259  const int filtersize = s->filtersize;
260  const double *gauss = s->gauss[ord];
261  int plane;
262 
263  for (plane = 0; plane < NUM_PLANES; ++plane) {
264  const int height = s->planeheight[plane];
265  const int width = s->planewidth[plane];
266  const int in_linesize = in->linesize[plane];
267  double *dst = td->data[dst_index][plane];
268  int slice_start, slice_end;
269  int r, c, g;
270 
271  if (dir == DIR_X) {
272  /** Applying gauss horizontally along each row */
273  const uint8_t *src = in->data[plane];
274  slice_start = ff_slice_pos(height, jobnr, nb_jobs);
275  slice_end = ff_slice_pos(height, jobnr + 1, nb_jobs);
276 
277  for (r = slice_start; r < slice_end; ++r) {
278  for (c = 0; c < width; ++c) {
279  dst[INDX2D(r, c, width)] = 0;
280  for (g = 0; g < filtersize; ++g) {
281  dst[INDX2D(r, c, width)] += GAUSS(src, r, c + GINDX(filtersize, g),
282  in_linesize, height, width, gauss[g]);
283  }
284  }
285  }
286  } else {
287  /** Applying gauss vertically along each column */
288  const double *src = td->data[src_index][plane];
289  slice_start = ff_slice_pos(width, jobnr, nb_jobs);
290  slice_end = ff_slice_pos(width, jobnr + 1, nb_jobs);
291 
292  for (c = slice_start; c < slice_end; ++c) {
293  for (r = 0; r < height; ++r) {
294  dst[INDX2D(r, c, width)] = 0;
295  for (g = 0; g < filtersize; ++g) {
296  dst[INDX2D(r, c, width)] += GAUSS(src, r + GINDX(filtersize, g), c,
297  width, height, width, gauss[g]);
298  }
299  }
300  }
301  }
302 
303  }
304  return 0;
305 }
306 
307 /**
308  * Slice Frobius normalization of gaussian derivatives. Only called for difford values of
309  * 1 or 2.
310  *
311  * @param ctx the filter context.
312  * @param arg data to be passed between threads.
313  * @param jobnr current job nubmer.
314  * @param nb_jobs total number of jobs.
315  *
316  * @return 0.
317  */
318 static int slice_normalize(AVFilterContext* ctx, void* arg, int jobnr, int nb_jobs)
319 {
320  ColorConstancyContext *s = ctx->priv;
321  ThreadData *td = arg;
322  const int difford = s->difford;
323  int plane;
324 
325  for (plane = 0; plane < NUM_PLANES; ++plane) {
326  const int height = s->planeheight[plane];
327  const int width = s->planewidth[plane];
328  const int64_t numpixels = width * (int64_t)height;
329  const int slice_start = (numpixels * jobnr ) / nb_jobs;
330  const int slice_end = (numpixels * (jobnr+1)) / nb_jobs;
331  const double *dx = td->data[INDEX_DX][plane];
332  const double *dy = td->data[INDEX_DY][plane];
333  double *norm = td->data[INDEX_NORM][plane];
334  int i;
335 
336  if (difford == 1) {
337  for (i = slice_start; i < slice_end; ++i) {
338  norm[i] = sqrt( pow(dx[i], 2) + pow(dy[i], 2));
339  }
340  } else {
341  const double *dxy = td->data[INDEX_DXY][plane];
342  for (i = slice_start; i < slice_end; ++i) {
343  norm[i] = sqrt( pow(dx[i], 2) + 4 * pow(dxy[i], 2) + pow(dy[i], 2) );
344  }
345  }
346  }
347 
348  return 0;
349 }
350 
351 /**
352  * Utility function for setting up differentiation data/metadata.
353  *
354  * @param ctx the filter context.
355  * @param td to be used for passing data between threads.
356  * @param ord ord of differentiation.
357  * @param dir direction of differentiation.
358  * @param src index of source used for differentiation.
359  * @param dst index destination used for saving differentiation result.
360  * @param dim maximum dimension in current direction.
361  * @param nb_threads number of threads to use.
362  */
363 static void av_always_inline
365  int src, int dst, int dim, int nb_threads) {
366  td->meta_data[INDEX_ORD] = ord;
367  td->meta_data[INDEX_DIR] = dir;
368  td->meta_data[INDEX_SRC] = src;
369  td->meta_data[INDEX_DST] = dst;
370  ctx->internal->execute(ctx, slice_get_derivative, td, NULL, FFMIN(dim, nb_threads));
371 }
372 
373 /**
374  * Main control function for calculating gaussian derivatives.
375  *
376  * @param ctx the filter context.
377  * @param td holds the buffers used for storing results.
378  *
379  * @return 0 in case of success, a negative value corresponding to an
380  * AVERROR code in case of failure.
381  */
383 {
384  ColorConstancyContext *s = ctx->priv;
385  int nb_threads = s->nb_threads;
386  int height = s->planeheight[1];
387  int width = s->planewidth[1];
388 
389  switch(s->difford) {
390  case 0:
391  if (!s->sigma) { // Only copy once
392  get_deriv(ctx, td, 0, DIR_X, 0 , INDEX_NORM, height, nb_threads);
393  } else {
394  get_deriv(ctx, td, 0, DIR_X, 0, INDEX_TEMP, height, nb_threads);
395  get_deriv(ctx, td, 0, DIR_Y, INDEX_TEMP, INDEX_NORM, width , nb_threads);
396  // save to INDEX_NORM because this will not be normalied and
397  // end gry edge filter expects result to be found in INDEX_NORM
398  }
399  return 0;
400 
401  case 1:
402  get_deriv(ctx, td, 1, DIR_X, 0, INDEX_TEMP, height, nb_threads);
403  get_deriv(ctx, td, 0, DIR_Y, INDEX_TEMP, INDEX_DX, width , nb_threads);
404 
405  get_deriv(ctx, td, 0, DIR_X, 0, INDEX_TEMP, height, nb_threads);
406  get_deriv(ctx, td, 1, DIR_Y, INDEX_TEMP, INDEX_DY, width , nb_threads);
407  return 0;
408 
409  case 2:
410  get_deriv(ctx, td, 2, DIR_X, 0, INDEX_TEMP, height, nb_threads);
411  get_deriv(ctx, td, 0, DIR_Y, INDEX_TEMP, INDEX_DX, width , nb_threads);
412 
413  get_deriv(ctx, td, 0, DIR_X, 0, INDEX_TEMP, height, nb_threads);
414  get_deriv(ctx, td, 2, DIR_Y, INDEX_TEMP, INDEX_DY, width , nb_threads);
415 
416  get_deriv(ctx, td, 1, DIR_X, 0, INDEX_TEMP, height, nb_threads);
417  get_deriv(ctx, td, 1, DIR_Y, INDEX_TEMP, INDEX_DXY, width , nb_threads);
418  return 0;
419 
420  default:
421  av_log(ctx, AV_LOG_ERROR, "Unsupported difford value: %d.\n", s->difford);
422  return AVERROR(EINVAL);
423  }
424 
425 }
426 
427 /**
428  * Slice function for grey edge algorithm that does partial summing/maximizing
429  * of gaussian derivatives.
430  *
431  * @param ctx the filter context.
432  * @param arg data to be passed between threads.
433  * @param jobnr current job nubmer.
434  * @param nb_jobs total number of jobs.
435  *
436  * @return 0.
437  */
438 static int filter_slice_grey_edge(AVFilterContext* ctx, void* arg, int jobnr, int nb_jobs)
439 {
440  ColorConstancyContext *s = ctx->priv;
441  ThreadData *td = arg;
442  AVFrame *in = td->in;
443  int minknorm = s->minknorm;
444  const uint8_t thresh = 255;
445  int plane;
446 
447  for (plane = 0; plane < NUM_PLANES; ++plane) {
448  const int height = s->planeheight[plane];
449  const int width = s->planewidth[plane];
450  const int in_linesize = in->linesize[plane];
451  const int slice_start = ff_slice_pos(height, jobnr, nb_jobs);
452  const int slice_end = ff_slice_pos(height, jobnr + 1, nb_jobs);
453  const uint8_t *img_data = in->data[plane];
454  const double *src = td->data[INDEX_NORM][plane];
455  double *dst = td->data[INDEX_DST][plane];
456  int r, c;
457 
458  dst[jobnr] = 0;
459  if (!minknorm) {
460  for (r = slice_start; r < slice_end; ++r) {
461  for (c = 0; c < width; ++c) {
462  dst[jobnr] = FFMAX( dst[jobnr], fabs(src[INDX2D(r, c, width)])
463  * (img_data[INDX2D(r, c, in_linesize)] < thresh) );
464  }
465  }
466  } else {
467  for (r = slice_start; r < slice_end; ++r) {
468  for (c = 0; c < width; ++c) {
469  dst[jobnr] += ( pow( fabs(src[INDX2D(r, c, width)] / 255.), minknorm)
470  * (img_data[INDX2D(r, c, in_linesize)] < thresh) );
471  }
472  }
473  }
474  }
475  return 0;
476 }
477 
478 /**
479  * Main control function for grey edge algorithm.
480  *
481  * @param ctx the filter context.
482  * @param in frame to perfrom grey edge on.
483  *
484  * @return 0 in case of success, a negative value corresponding to an
485  * AVERROR code in case of failure.
486  */
488 {
489  ColorConstancyContext *s = ctx->priv;
490  ThreadData td;
491  int minknorm = s->minknorm;
492  int difford = s->difford;
493  double *white = s->white;
494  int nb_jobs = FFMIN3(s->planeheight[1], s->planewidth[1], s->nb_threads);
495  int plane, job, ret;
496 
497  td.in = in;
499  if (ret) {
500  return ret;
501  }
502  get_derivative(ctx, &td);
503  if (difford > 0) {
504  ctx->internal->execute(ctx, slice_normalize, &td, NULL, nb_jobs);
505  }
506 
507  ctx->internal->execute(ctx, filter_slice_grey_edge, &td, NULL, nb_jobs);
508  if (!minknorm) {
509  for (plane = 0; plane < NUM_PLANES; ++plane) {
510  white[plane] = 0; // All values are absolute
511  for (job = 0; job < nb_jobs; ++job) {
512  white[plane] = FFMAX(white[plane] , td.data[INDEX_DST][plane][job]);
513  }
514  }
515  } else {
516  for (plane = 0; plane < NUM_PLANES; ++plane) {
517  white[plane] = 0;
518  for (job = 0; job < nb_jobs; ++job) {
519  white[plane] += td.data[INDEX_DST][plane][job];
520  }
521  white[plane] = pow(white[plane], 1./minknorm);
522  }
523  }
524 
525  cleanup_derivative_buffers(&td, difford + 1, NUM_PLANES);
526  return 0;
527 }
528 
529 /**
530  * Normalizes estimated illumination since only illumination vector
531  * direction is required for color constancy.
532  *
533  * @param light the estimated illumination to be normalized in place
534  */
535 static void normalize_light(double *light)
536 {
537  double abs_val = pow( pow(light[0], 2.0) + pow(light[1], 2.0) + pow(light[2], 2.0), 0.5);
538  int plane;
539 
540  // TODO: check if setting to 1.0 when estimated = 0.0 is the best thing to do
541 
542  if (!abs_val) {
543  for (plane = 0; plane < NUM_PLANES; ++plane) {
544  light[plane] = 1.0;
545  }
546  } else {
547  for (plane = 0; plane < NUM_PLANES; ++plane) {
548  light[plane] = (light[plane] / abs_val);
549  if (!light[plane]) { // to avoid division by zero when correcting
550  light[plane] = 1.0;
551  }
552  }
553  }
554 }
555 
556 /**
557  * Redirects to corresponding algorithm estimation function and performs normalization
558  * after estimation.
559  *
560  * @param ctx the filter context.
561  * @param in frame to perfrom estimation on.
562  *
563  * @return 0 in case of success, a negative value corresponding to an
564  * AVERROR code in case of failure.
565  */
567 {
568  ColorConstancyContext *s = ctx->priv;
569  int ret;
570 
571  ret = filter_grey_edge(ctx, in);
572 
573  av_log(ctx, AV_LOG_DEBUG, "Estimated illumination= %f %f %f\n",
574  s->white[0], s->white[1], s->white[2]);
575  normalize_light(s->white);
576  av_log(ctx, AV_LOG_DEBUG, "Estimated illumination after normalization= %f %f %f\n",
577  s->white[0], s->white[1], s->white[2]);
578 
579  return ret;
580 }
581 
582 /**
583  * Performs simple correction via diagonal transformation model.
584  *
585  * @param ctx the filter context.
586  * @param arg data to be passed between threads.
587  * @param jobnr current job nubmer.
588  * @param nb_jobs total number of jobs.
589  *
590  * @return 0.
591  */
592 static int diagonal_transformation(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
593 {
594  ColorConstancyContext *s = ctx->priv;
595  ThreadData *td = arg;
596  AVFrame *in = td->in;
597  AVFrame *out = td->out;
598  int plane;
599 
600  for (plane = 0; plane < NUM_PLANES; ++plane) {
601  const int height = s->planeheight[plane];
602  const int width = s->planewidth[plane];
603  const int64_t numpixels = width * (int64_t)height;
604  const int slice_start = (numpixels * jobnr) / nb_jobs;
605  const int slice_end = (numpixels * (jobnr+1)) / nb_jobs;
606  const uint8_t *src = in->data[plane];
607  uint8_t *dst = out->data[plane];
608  double temp;
609  unsigned i;
610 
611  for (i = slice_start; i < slice_end; ++i) {
612  temp = src[i] / (s->white[plane] * SQRT3);
613  dst[i] = av_clip_uint8((int)(temp + 0.5));
614  }
615  }
616  return 0;
617 }
618 
619 /**
620  * Main control function for correcting scene illumination based on
621  * estimated illumination.
622  *
623  * @param ctx the filter context.
624  * @param in holds frame to correct
625  * @param out holds corrected frame
626  */
628 {
629  ColorConstancyContext *s = ctx->priv;
630  ThreadData td;
631  int nb_jobs = FFMIN3(s->planeheight[1], s->planewidth[1], s->nb_threads);
632 
633  td.in = in;
634  td.out = out;
635  ctx->internal->execute(ctx, diagonal_transformation, &td, NULL, nb_jobs);
636 }
637 
639 {
640  static const enum AVPixelFormat pix_fmts[] = {
641  // TODO: support more formats
642  // FIXME: error when saving to .jpg
645  };
646 
648 }
649 
650 static int config_props(AVFilterLink *inlink)
651 {
652  AVFilterContext *ctx = inlink->dst;
653  ColorConstancyContext *s = ctx->priv;
655  const double break_off_sigma = 3.0;
656  double sigma = s->sigma;
657  int ret;
658 
659  if (!floor(break_off_sigma * sigma + 0.5) && s->difford) {
660  av_log(ctx, AV_LOG_ERROR, "floor(%f * sigma) must be > 0 when difford > 0.\n", break_off_sigma);
661  return AVERROR(EINVAL);
662  }
663 
664  s->filtersize = 2 * floor(break_off_sigma * sigma + 0.5) + 1;
665  if (ret=set_gauss(ctx)) {
666  return ret;
667  }
668 
669  s->nb_threads = ff_filter_get_nb_threads(ctx);
670  s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
671  s->planewidth[0] = s->planewidth[3] = inlink->w;
672  s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
673  s->planeheight[0] = s->planeheight[3] = inlink->h;
674 
675  return 0;
676 }
677 
678 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
679 {
680  AVFilterContext *ctx = inlink->dst;
681  AVFilterLink *outlink = ctx->outputs[0];
682  AVFrame *out;
683  int ret;
684  int direct = 0;
685 
687  if (ret) {
688  av_frame_free(&in);
689  return ret;
690  }
691 
692  if (av_frame_is_writable(in)) {
693  direct = 1;
694  out = in;
695  } else {
696  out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
697  if (!out) {
698  av_frame_free(&in);
699  return AVERROR(ENOMEM);
700  }
702  }
704 
705  if (!direct)
706  av_frame_free(&in);
707 
708  return ff_filter_frame(outlink, out);
709 }
710 
712 {
713  ColorConstancyContext *s = ctx->priv;
714  int difford = s->difford;
715  int i;
716 
717  for (i = 0; i <= difford; ++i) {
718  av_freep(&s->gauss[i]);
719  }
720 }
721 
723  {
724  .name = "default",
725  .type = AVMEDIA_TYPE_VIDEO,
726  .config_props = config_props,
727  .filter_frame = filter_frame,
728  },
729  { NULL }
730 };
731 
733  {
734  .name = "default",
735  .type = AVMEDIA_TYPE_VIDEO,
736  },
737  { NULL }
738 };
739 
740 #if CONFIG_GREYEDGE_FILTER
741 
742 static const AVOption greyedge_options[] = {
743  { "difford", "set differentiation order", OFFSET(difford), AV_OPT_TYPE_INT, {.i64=1}, 0, 2, FLAGS },
744  { "minknorm", "set Minkowski norm", OFFSET(minknorm), AV_OPT_TYPE_INT, {.i64=1}, 0, 20, FLAGS },
745  { "sigma", "set sigma", OFFSET(sigma), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.0, 1024.0, FLAGS },
746  { NULL }
747 };
748 
749 AVFILTER_DEFINE_CLASS(greyedge);
750 
752  .name = GREY_EDGE,
753  .description = NULL_IF_CONFIG_SMALL("Estimates scene illumination by grey edge assumption."),
754  .priv_size = sizeof(ColorConstancyContext),
755  .priv_class = &greyedge_class,
757  .uninit = uninit,
761 };
762 
763 #endif /* CONFIG_GREY_EDGE_FILTER */
static const AVFilterPad inputs[]
Definition: af_acontrast.c:193
static const AVFilterPad outputs[]
Definition: af_acontrast.c:203
AVFilter ff_vf_greyedge
#define av_always_inline
Definition: attributes.h:45
#define av_cold
Definition: attributes.h:88
uint8_t pi<< 24) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_U8, uint8_t,(*(const uint8_t *) pi - 0x80) *(1.0f/(1<< 7))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_U8, uint8_t,(*(const uint8_t *) pi - 0x80) *(1.0/(1<< 7))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S16, int16_t,(*(const int16_t *) pi >> 8)+0x80) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S16, int16_t, *(const int16_t *) pi *(1.0f/(1<< 15))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S16, int16_t, *(const int16_t *) pi *(1.0/(1<< 15))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S32, int32_t,(*(const int32_t *) pi >> 24)+0x80) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S32, int32_t, *(const int32_t *) pi *(1.0f/(1U<< 31))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S32, int32_t, *(const int32_t *) pi *(1.0/(1U<< 31))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_FLT, float, av_clip_uint8(lrintf(*(const float *) pi *(1<< 7))+0x80)) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_FLT, float, av_clip_int16(lrintf(*(const float *) pi *(1<< 15)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_FLT, float, av_clipl_int32(llrintf(*(const float *) pi *(1U<< 31)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_DBL, double, av_clip_uint8(lrint(*(const double *) pi *(1<< 7))+0x80)) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_DBL, double, av_clip_int16(lrint(*(const double *) pi *(1<< 15)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_DBL, double, av_clipl_int32(llrint(*(const double *) pi *(1U<< 31)))) #define SET_CONV_FUNC_GROUP(ofmt, ifmt) static void set_generic_function(AudioConvert *ac) { } void ff_audio_convert_free(AudioConvert **ac) { if(! *ac) return;ff_dither_free(&(*ac) ->dc);av_freep(ac);} AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, enum AVSampleFormat out_fmt, enum AVSampleFormat in_fmt, int channels, int sample_rate, int apply_map) { AudioConvert *ac;int in_planar, out_planar;ac=av_mallocz(sizeof(*ac));if(!ac) return NULL;ac->avr=avr;ac->out_fmt=out_fmt;ac->in_fmt=in_fmt;ac->channels=channels;ac->apply_map=apply_map;if(avr->dither_method !=AV_RESAMPLE_DITHER_NONE &&av_get_packed_sample_fmt(out_fmt)==AV_SAMPLE_FMT_S16 &&av_get_bytes_per_sample(in_fmt) > 2) { ac->dc=ff_dither_alloc(avr, out_fmt, in_fmt, channels, sample_rate, apply_map);if(!ac->dc) { av_free(ac);return NULL;} return ac;} in_planar=ff_sample_fmt_is_planar(in_fmt, channels);out_planar=ff_sample_fmt_is_planar(out_fmt, channels);if(in_planar==out_planar) { ac->func_type=CONV_FUNC_TYPE_FLAT;ac->planes=in_planar ? ac->channels :1;} else if(in_planar) ac->func_type=CONV_FUNC_TYPE_INTERLEAVE;else ac->func_type=CONV_FUNC_TYPE_DEINTERLEAVE;set_generic_function(ac);if(ARCH_AARCH64) ff_audio_convert_init_aarch64(ac);if(ARCH_ARM) ff_audio_convert_init_arm(ac);if(ARCH_X86) ff_audio_convert_init_x86(ac);return ac;} int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in) { int use_generic=1;int len=in->nb_samples;int p;if(ac->dc) { av_log(ac->avr, AV_LOG_TRACE, "%d samples - audio_convert: %s to %s (dithered)\n", len, av_get_sample_fmt_name(ac->in_fmt), av_get_sample_fmt_name(ac->out_fmt));return ff_convert_dither(ac-> in
uint8_t
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition: avfilter.c:1096
int ff_filter_get_nb_threads(AVFilterContext *ctx)
Get number of threads for current filter instance.
Definition: avfilter.c:802
Main libavfilter public API header.
#define flags(name, subs,...)
Definition: cbs_av1.c:572
#define s(width, name)
Definition: cbs_vp9.c:257
#define FFMIN(a, b)
Definition: common.h:105
#define AV_CEIL_RSHIFT(a, b)
Definition: common.h:58
#define FFMAX(a, b)
Definition: common.h:103
#define av_clip_uint8
Definition: common.h:128
#define FFMIN3(a, b, c)
Definition: common.h:106
#define NULL
Definition: coverity.c:32
long long int64_t
Definition: coverity.c:34
static __device__ float fabs(float a)
Definition: cuda_runtime.h:182
static __device__ float floor(float a)
Definition: cuda_runtime.h:173
int8_t exp
Definition: eval.c:74
static int ff_slice_pos(int total, int jobnr, int nb_jobs)
Compute the boundary index for a slice when work of size total is split into nb_jobs slices.
Definition: filters.h:271
int ff_set_common_formats(AVFilterContext *ctx, AVFilterFormats *formats)
A helper for query_formats() which sets all links to the same list of formats.
Definition: formats.c:587
AVFilterFormats * ff_make_format_list(const int *fmts)
Create a list of supported formats.
Definition: formats.c:286
@ AV_OPT_TYPE_INT
Definition: opt.h:225
@ AV_OPT_TYPE_DOUBLE
Definition: opt.h:227
#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:126
#define AVFILTER_FLAG_SLICE_THREADS
The filter supports multithreading by splitting frames into multiple parts and processing them concur...
Definition: avfilter.h:117
#define AVERROR(e)
Definition: error.h:43
int av_frame_is_writable(AVFrame *frame)
Check if the frame data is writable.
Definition: frame.c:594
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:203
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition: frame.c:658
#define AV_LOG_TRACE
Extremely verbose debugging, useful for libav* development.
Definition: log.h:220
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition: log.h:215
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:194
void * av_mallocz_array(size_t nmemb, size_t size)
Allocate a memory block for an array with av_mallocz().
Definition: mem.c:190
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
misc image utilities
int i
Definition: input.c:407
const char * arg
Definition: jacosubdec.c:66
static void direct(const float *in, const FFTComplex *ir, int len, float *out)
Definition: af_afir.c:60
#define AVFILTER_DEFINE_CLASS(fname)
Definition: internal.h:288
common internal API header
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:117
static enum AVPixelFormat pix_fmts[]
Definition: libkvazaar.c:309
const char * desc
Definition: libsvtav1.c:79
#define M_PI
Definition: mathematics.h:52
static int slice_end(AVCodecContext *avctx, AVFrame *pict)
Handle slice ends.
Definition: mpeg12dec.c:2033
AVOptions.
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:2573
AVPixelFormat
Pixel format.
Definition: pixfmt.h:64
@ AV_PIX_FMT_NONE
Definition: pixfmt.h:65
@ AV_PIX_FMT_GBRP
planar GBR 4:4:4 24bpp
Definition: pixfmt.h:168
#define td
Definition: regdef.h:70
Describe the class of an AVClass context structure.
Definition: log.h:67
An instance of a filter.
Definition: avfilter.h:341
A filter pad used for either input or output.
Definition: internal.h:54
const char * name
Pad name.
Definition: internal.h:60
Filter definition.
Definition: avfilter.h:145
const char * name
Filter name.
Definition: avfilter.h:149
AVFormatInternal * internal
An opaque field for libavformat internal usage.
Definition: avformat.h:1699
This structure describes decoded (raw) audio or video data.
Definition: frame.h:318
AVOption.
Definition: opt.h:248
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition: pixdesc.h:81
Common struct for all algorithms contexts.
int minknorm
@minknorm = 0 : getMax instead
double white[NUM_PLANES]
double * gauss[MAX_DIFF_ORD+1]
Used for passing data between threads.
Definition: dsddec.c:67
double * data[MAX_DATA][NUM_PLANES]
AVFrame * out
Definition: af_adeclick.c:502
int meta_data[MAX_META_DATA]
AVFrame * in
Definition: af_adenorm.c:223
#define av_freep(p)
#define av_log(a,...)
#define src
Definition: vp8dsp.c:255
FILE * out
Definition: movenc.c:54
AVFormatContext * ctx
Definition: movenc.c:48
#define height
#define width
#define NUM_PLANES
static const AVFilterPad colorconstancy_inputs[]
static void av_always_inline get_deriv(AVFilterContext *ctx, ThreadData *td, int ord, int dir, int src, int dst, int dim, int nb_threads)
Utility function for setting up differentiation data/metadata.
#define DIR_Y
#define GAUSS(s, sr, sc, sls, sh, sw, g)
#define INDEX_DX
static int diagonal_transformation(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Performs simple correction via diagonal transformation model.
#define INDEX_NORM
#define GINDX(s, i)
static void normalize_light(double *light)
Normalizes estimated illumination since only illumination vector direction is required for color cons...
#define INDEX_DY
#define DIR_X
#define INDEX_TEMP
#define INDX2D(r, c, w)
static int query_formats(AVFilterContext *ctx)
static int setup_derivative_buffers(AVFilterContext *ctx, ThreadData *td)
Allocates buffers used by grey edge for storing derivatives final and intermidiate results.
#define FLAGS
static int filter_grey_edge(AVFilterContext *ctx, AVFrame *in)
Main control function for grey edge algorithm.
#define INDEX_ORD
#define INDEX_DIR
static int slice_normalize(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Slice Frobius normalization of gaussian derivatives.
#define INDEX_DST
#define INDEX_DXY
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
#define MAX_META_DATA
static const AVFilterPad colorconstancy_outputs[]
static int get_derivative(AVFilterContext *ctx, ThreadData *td)
Main control function for calculating gaussian derivatives.
#define INDEX_SRC
#define MAX_DATA
static int config_props(AVFilterLink *inlink)
static int set_gauss(AVFilterContext *ctx)
Sets gauss filters used for calculating gauss derivatives.
static void chromatic_adaptation(AVFilterContext *ctx, AVFrame *in, AVFrame *out)
Main control function for correcting scene illumination based on estimated illumination.
static av_cold void uninit(AVFilterContext *ctx)
static void cleanup_derivative_buffers(ThreadData *td, int nb_buff, int nb_planes)
Frees up buffers used by grey edge for storing derivatives final and intermidiate results.
#define OFFSET(x)
#define MAX_DIFF_ORD
static int illumination_estimation(AVFilterContext *ctx, AVFrame *in)
Redirects to corresponding algorithm estimation function and performs normalization after estimation.
#define GREY_EDGE
static int slice_get_derivative(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Slice calculation of gaussian derivatives.
#define SQRT3
static int filter_slice_grey_edge(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Slice function for grey edge algorithm that does partial summing/maximizing of gaussian derivatives.
const char * b
Definition: vf_curves.c:119
const char * g
Definition: vf_curves.c:118
const char * r
Definition: vf_curves.c:117
else temp
Definition: vf_mcdeint.c:259
AVFrame * ff_get_video_buffer(AVFilterLink *link, int w, int h)
Request a picture buffer with a specific set of permissions.
Definition: video.c:104
int dim
static double c[64]