History

avfilter: add colormap video filter

Paul B Mahol committed 4 years ago

Commit 43ea19f1 parent b1b72496 files · +619 −1Browse files

Unified · Split

6 files
  1. Changelog +1 −0
  2. doc/filters.texi +33 −0
  3. libavfilter/Makefile +1 −0
  4. libavfilter/allfilters.c +1 −0
  5. libavfilter/version.h +1 −1
  6. libavfilter/vf_colormap.c +582 −0
Changelog+1−0
@@ -11,6 +11,7 @@ version 5.1: · 10 unmodified lines
1111 - avsynctest source filter
1212 - feedback video filter
1313 - pixelize video filter
14+- colormap video filter
1415
1516
1617 version 5.0:
doc/filters.texi+33−0
@@ -8944,6 +8944,39 @@ colorlevels=romin=0.5:gomin=0.5:bomin=0.5 · 8943 unmodified lines
89448944
89458945 This filter supports the all above options as @ref{commands}.
89468946
8947+@section colormap
8948+Apply custom color maps to video stream.
8949+
8950+This filter needs three input video streams.
8951+First stream is video stream that is going to be filtered out.
8952+Second and third video stream specify color patches for source
8953+color to target color mapping.
8954+
8955+The filter accepts the following options:
8956+
8957+@table @option
8958+@item patch_size
8959+Set the source and target video stream patch size in pixels.
8960+
8961+@item nb_patches
8962+Set the max number of used patches from source and target video stream.
8963+
8964+@item type
8965+Set the adjustments used for target colors. Can be @code{relative} or @code{absolute}.
8966+Defaults is @code{absolute}.
8967+
8968+@item kernel
8969+Set the kernel used to measure color differences between mapped colors.
8970+
8971+The accepted values are:
8972+@table @samp
8973+@item euclidean
8974+@item weuclidean
8975+@end table
8976+
8977+Default is @code{euclidean}.
8978+@end table
8979+
89478980 @section colormatrix
89488981
89498982 Convert color matrix.
libavfilter/Makefile+1−0
@@ -218,6 +218,7 @@ OBJS-$(CONFIG_COLORKEY_OPENCL_FILTER) += vf_colorkey_opencl.o opencl.o \ · 217 unmodified lines
218218 opencl/colorkey.o
219219 OBJS-$(CONFIG_COLORHOLD_FILTER) += vf_colorkey.o
220220 OBJS-$(CONFIG_COLORLEVELS_FILTER) += vf_colorlevels.o
221+OBJS-$(CONFIG_COLORMAP_FILTER) += vf_colormap.o
221222 OBJS-$(CONFIG_COLORMATRIX_FILTER) += vf_colormatrix.o
222223 OBJS-$(CONFIG_COLORSPACE_FILTER) += vf_colorspace.o colorspace.o colorspacedsp.o
223224 OBJS-$(CONFIG_COLORTEMPERATURE_FILTER) += vf_colortemperature.o
libavfilter/allfilters.c+1−0
@@ -204,6 +204,7 @@ extern const AVFilter ff_vf_colorkey; · 203 unmodified lines
204204 extern const AVFilter ff_vf_colorkey_opencl;
205205 extern const AVFilter ff_vf_colorhold;
206206 extern const AVFilter ff_vf_colorlevels;
207+extern const AVFilter ff_vf_colormap;
207208 extern const AVFilter ff_vf_colormatrix;
208209 extern const AVFilter ff_vf_colorspace;
209210 extern const AVFilter ff_vf_colortemperature;
libavfilter/version.h+1−1
@@ -31,7 +31,7 @@ · 30 unmodified lines
3131
3232 #include "version_major.h"
3333
34−#define LIBAVFILTER_VERSION_MINOR 33
34+#define LIBAVFILTER_VERSION_MINOR 34
3535 #define LIBAVFILTER_VERSION_MICRO 100
3636
3737
libavfilter/vf_colormap.c+582−0
@@ -0,0 +1,582 @@
1+/*
2+ * Copyright (c) 2022 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+/**
22+ * @file
23+ * Compute a look-up table from map of colors.
24+ */
25+
26+#include "config_components.h"
27+
28+#include "libavutil/attributes.h"
29+#include "libavutil/avstring.h"
30+#include "libavutil/common.h"
31+#include "libavutil/opt.h"
32+#include "libavutil/pixdesc.h"
33+#include "avfilter.h"
34+#include "formats.h"
35+#include "internal.h"
36+#include "framesync.h"
37+#include "video.h"
38+
39+#define MAX_SIZE 64
40+
41+enum KernelType {
42+ EUCLIDEAN,
43+ WEUCLIDEAN,
44+ NB_KERNELS,
45+};
46+
47+typedef struct ColorMapContext {
48+ const AVClass *class;
49+ int w, h;
50+ int size;
51+ int nb_maps;
52+ int changed[2];
53+
54+ float source[MAX_SIZE][4];
55+ float ttarget[MAX_SIZE][4];
56+ float target[MAX_SIZE][4];
57+ float icoeff[4][4];
58+ float coeff[MAX_SIZE][4];
59+
60+ int target_type;
61+ int kernel_type;
62+ float (*kernel)(const float *x, const float *y);
63+
64+ FFFrameSync fs;
65+} ColorMapContext;
66+
67+#define OFFSET(x) offsetof(ColorMapContext, x)
68+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
69+
70+static const AVOption colormap_options[] = {
71+ { "patch_size", "set patch size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "64x64"}, 0, 0, FLAGS },
72+ { "nb_patches", "set number of patches", OFFSET(size), AV_OPT_TYPE_INT, {.i64 = 8}, 1, MAX_SIZE, FLAGS },
73+ { "type", "set the target type used", OFFSET(target_type), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "type" },
74+ { "relative", "the target colors are relative", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 1, FLAGS, "type" },
75+ { "absolute", "the target colors are absolute", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 1, FLAGS, "type" },
76+ { "kernel", "set the kernel used for measuring color difference", OFFSET(kernel_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_KERNELS-1, FLAGS, "kernel" },
77+ { "euclidean", "square root of sum of squared differences", 0, AV_OPT_TYPE_CONST, {.i64=EUCLIDEAN}, 0, 0, FLAGS, "kernel" },
78+ { "weuclidean", "weighted square root of sum of squared differences",0, AV_OPT_TYPE_CONST, {.i64=WEUCLIDEAN}, 0, 0, FLAGS, "kernel" },
79+ { NULL }
80+};
81+
82+static int gauss_make_triangular(double *A, int *p, int n)
83+{
84+ p[n - 1] = n - 1;
85+ for (int k = 0; k < n; k++) {
86+ double t1;
87+ int m = k;
88+
89+ for (int i = k + 1; i < n; i++)
90+ if (fabs(A[k + n * i]) > fabs(A[k + n * m]))
91+ m = i;
92+ p[k] = m;
93+ t1 = A[k + n * m];
94+ A[k + n * m] = A[k + n * k];
95+ A[k + n * k] = t1;
96+ if (t1 != 0) {
97+ for (int i = k + 1; i < n; i++)
98+ A[k + n * i] /= -t1;
99+ if (k != m)
100+ for (int i = k + 1; i < n; i++) {
101+ double t2 = A[i + n * m];
102+ A[i + n * m] = A[i + n * k];
103+ A[i + n * k] = t2;
104+ }
105+ for (int j = k + 1; j < n; j++)
106+ for (int i = k + 1; i < n; i++)
107+ A[i + n * j] += A[k + j * n] * A[i + k * n];
108+ } else {
109+ return 0;
110+ }
111+ }
112+
113+ return 1;
114+}
115+
116+static void gauss_solve_triangular(const double *A, const int *p, double *b, int n)
117+{
118+ for(int k = 0; k < n - 1; k++) {
119+ int m = p[k];
120+ double t = b[m];
121+ b[m] = b[k];
122+ b[k] = t;
123+ for (int i = k + 1; i < n; i++)
124+ b[i] += A[k + n * i] * t;
125+ }
126+
127+ for(int k = n - 1; k > 0; k--) {
128+ b[k] /= A[k + n * k];
129+ double t = b[k];
130+ for (int i = 0; i < k; i++)
131+ b[i] -= A[k + n * i] * t;
132+ }
133+
134+ b[0] /= A[0 + 0 * n];
135+}
136+
137+static int gauss_solve(double *A, double *b, int n)
138+{
139+ int *p = av_calloc(n, sizeof(*p));
140+
141+ if (!p)
142+ return 1;
143+
144+ if (!gauss_make_triangular(A, p, n)) {
145+ av_freep(&p);
146+ return 1;
147+ }
148+
149+ gauss_solve_triangular(A, p, b, n);
150+
151+ av_freep(&p);
152+
153+ return 0;
154+}
155+
156+#define P2(x) ((x)*(x))
157+
158+static float euclidean_kernel(const float *x, const float *y)
159+{
160+ const float d2 = P2(x[0]-y[0]) +
161+ P2(x[1]-y[1]) +
162+ P2(x[2]-y[2]);
163+ return sqrtf(d2);
164+}
165+
166+static float weuclidean_kernel(const float *x, const float *y)
167+{
168+ const float rm = (x[0] + y[0]) * 0.5f;
169+ const float d2 = P2(x[0]-y[0]) * (2.f + rm) +
170+ P2(x[1]-y[1]) * 4.f +
171+ P2(x[2]-y[2]) * (3.f - rm);
172+ return sqrtf(d2);
173+}
174+
175+static void build_map(AVFilterContext *ctx)
176+{
177+ ColorMapContext *s = ctx->priv;
178+
179+ for (int j = 0; j < s->nb_maps; j++) {
180+ s->target[j][0] = s->target_type == 0 ? s->source[j][0] + s->ttarget[j][0] : s->ttarget[j][0];
181+ s->target[j][1] = s->target_type == 0 ? s->source[j][1] + s->ttarget[j][1] : s->ttarget[j][1];
182+ s->target[j][2] = s->target_type == 0 ? s->source[j][2] + s->ttarget[j][2] : s->ttarget[j][2];
183+ }
184+
185+ for (int c = 0; c < 3; c++) {
186+ for (int j = 0; j < s->nb_maps; j++)
187+ s->coeff[j][c] = 0.f;
188+
189+ for (int j = 0; j < 4; j++) {
190+ s->icoeff[j][c] = 0;
191+ s->icoeff[j][c] = 0;
192+ s->icoeff[j][c] = 0;
193+ }
194+
195+ s->icoeff[c+1][c] = 1.f;
196+
197+ switch (s->nb_maps) {
198+ case 1:
199+ {
200+ float div = fabsf(s->source[0][c]) < 1e-6f ? 1e-6f : s->source[0][c];
201+ s->icoeff[c][1+c] = s->target[0][c] / div;
202+ }
203+ break;
204+ case 2:
205+ {
206+ double A[2 * 2] = { 1, s->source[0][c],
207+ 1, s->source[1][c] };
208+ double b[2] = { s->target[0][c], s->target[1][c] };
209+
210+ if (gauss_solve(A, b, 2))
211+ continue;
212+
213+ s->icoeff[0 ][c] = b[0];
214+ s->icoeff[1+c][c] = b[1];
215+ }
216+ break;
217+ case 3:
218+ {
219+ const uint8_t idx[3][3] = {{ 0, 1, 2 },
220+ { 1, 0, 2 },
221+ { 2, 0, 1 }};
222+ const uint8_t didx[3][4] = {{ 0, 1, 2, 2 },
223+ { 0, 2, 1, 2 },
224+ { 0, 2, 2, 1 }};
225+ const int C0 = idx[c][0];
226+ const int C1 = idx[c][1];
227+ const int C2 = idx[c][2];
228+ double A[3 * 3] = { 1, s->source[0][C0], s->source[0][C1] + s->source[0][C2],
229+ 1, s->source[1][C0], s->source[1][C1] + s->source[1][C2],
230+ 1, s->source[2][C0], s->source[2][C1] + s->source[2][C2] };
231+ double b[3] = { s->target[0][c], s->target[1][c], s->target[2][c] };
232+
233+ if (gauss_solve(A, b, 3))
234+ continue;
235+
236+ s->icoeff[0][c] = b[didx[c][0]];
237+ s->icoeff[1][c] = b[didx[c][1]];
238+ s->icoeff[2][c] = b[didx[c][2]];
239+ s->icoeff[3][c] = b[didx[c][3]];
240+ }
241+ break;
242+ case 4:
243+ {
244+ double A[4 * 4] = { 1, s->source[0][0], s->source[0][1], s->source[0][2],
245+ 1, s->source[1][0], s->source[1][1], s->source[1][2],
246+ 1, s->source[2][0], s->source[2][1], s->source[2][2],
247+ 1, s->source[3][0], s->source[3][1], s->source[3][2] };
248+ double b[4] = { s->target[0][c], s->target[1][c], s->target[2][c], s->target[3][c] };
249+ int pivot[4];
250+
251+ if (!gauss_make_triangular(A, pivot, 4))
252+ continue;
253+ gauss_solve_triangular(A, pivot, b, 4);
254+
255+ s->icoeff[0][c] = b[0];
256+ s->icoeff[1][c] = b[1];
257+ s->icoeff[2][c] = b[2];
258+ s->icoeff[3][c] = b[3];
259+ }
260+ break;
261+ default:
262+ {
263+ const int N = s->nb_maps;
264+ const int N4 = N + 4;
265+ double *A = av_calloc(sizeof(*A), N4 * N4);
266+ double *b = av_calloc(sizeof(*b), N4);
267+ int *pivot = NULL;
268+
269+ if (!A || !b)
270+ goto error;
271+
272+ for (int j = 0; j < N; j++)
273+ for (int i = j; i < N; i++)
274+ A[j*N4+i] = A[i*N4+j] = s->kernel(s->source[i], s->source[j]);
275+
276+ for (int i = 0; i < N; i++)
277+ A[i*N4+N+0] = A[(N+0)*N4+i] = 1;
278+ for (int i = 0; i < N; i++)
279+ A[i*N4+N+1] = A[(N+1)*N4+i] = s->source[i][0];
280+ for (int i = 0; i < N; i++)
281+ A[i*N4+N+2] = A[(N+2)*N4+i] = s->source[i][1];
282+ for (int i = 0; i < N; i++)
283+ A[i*N4+N+3] = A[(N+3)*N4+i] = s->source[i][2];
284+
285+ for (int j = N; j < N4; j++)
286+ for (int i = N;i < N4; i++)
287+ A[j * N4 + i] = 0.;
288+
289+ pivot = av_calloc(N4, sizeof(*pivot));
290+ if (!pivot)
291+ goto error;
292+
293+ if (gauss_make_triangular(A, pivot, N4)) {
294+ for (int i = 0; i < N; i++)
295+ b[i] = s->target[i][c];
296+ for (int i = N; i < N + 4; i++)
297+ b[i] = 0;
298+
299+ gauss_solve_triangular(A, pivot, b, N4);
300+
301+ for (int i = 0; i < N; i++)
302+ s->coeff[i][c] = b[i];
303+
304+ for (int i = 0; i < 4; i++)
305+ s->icoeff[i][c] = b[N + i];
306+ }
307+error:
308+ av_free(pivot);
309+ av_free(b);
310+ av_free(A);
311+ }
312+ }
313+ }
314+}
315+
316+typedef struct ThreadData {
317+ AVFrame *in, *out;
318+} ThreadData;
319+
320+static int colormap_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
321+{
322+ ColorMapContext *s = ctx->priv;
323+ ThreadData *td = arg;
324+ AVFrame *in = td->in;
325+ AVFrame *out = td->out;
326+ const int maps = s->nb_maps;
327+ const int width = out->width;
328+ const int height = out->height;
329+ const int slice_start = (height * jobnr) / nb_jobs;
330+ const int slice_end = (height * (jobnr + 1)) / nb_jobs;
331+ const int sr_linesize = in->linesize[2] / 4;
332+ const int dr_linesize = out->linesize[2] / 4;
333+ const int sg_linesize = in->linesize[0] / 4;
334+ const int dg_linesize = out->linesize[0] / 4;
335+ const int sb_linesize = in->linesize[1] / 4;
336+ const int db_linesize = out->linesize[1] / 4;
337+ const float *sr = (float *)in->data[2] + slice_start * sr_linesize;
338+ const float *sg = (float *)in->data[0] + slice_start * sg_linesize;
339+ const float *sb = (float *)in->data[1] + slice_start * sb_linesize;
340+ float *r = (float *)out->data[2] + slice_start * dr_linesize;
341+ float *g = (float *)out->data[0] + slice_start * dg_linesize;
342+ float *b = (float *)out->data[1] + slice_start * db_linesize;
343+ float (*kernel)(const float *x, const float *y) = s->kernel;
344+ const float *icoeff[4] = { s->icoeff[0], s->icoeff[1], s->icoeff[2], s->icoeff[3] };
345+
346+ for (int y = slice_start; y < slice_end; y++) {
347+ for (int x = 0; x < width; x++) {
348+ const float input[3] = { sr[x], sg[x], sb[x] };
349+ float srv, sgv, sbv;
350+ float rv, gv, bv;
351+
352+ srv = sr[x];
353+ sgv = sg[x];
354+ sbv = sb[x];
355+
356+ rv = icoeff[0][0];
357+ gv = icoeff[0][1];
358+ bv = icoeff[0][2];
359+
360+ rv += icoeff[1][0] * srv + icoeff[2][0] * sgv + icoeff[3][0] * sbv;
361+ gv += icoeff[1][1] * srv + icoeff[2][1] * sgv + icoeff[3][1] * sbv;
362+ bv += icoeff[1][2] * srv + icoeff[2][2] * sgv + icoeff[3][2] * sbv;
363+
364+ for (int z = 0; z < maps && maps > 4; z++) {
365+ const float *coeff = s->coeff[z];
366+ const float cr = coeff[0];
367+ const float cg = coeff[1];
368+ const float cb = coeff[2];
369+ const float f = kernel(input, s->source[z]);
370+
371+ rv += f * cr;
372+ gv += f * cg;
373+ bv += f * cb;
374+ }
375+
376+ r[x] = rv;
377+ g[x] = gv;
378+ b[x] = bv;
379+ }
380+
381+ sg += sg_linesize;
382+ g += dg_linesize;
383+ sb += sb_linesize;
384+ b += db_linesize;
385+ sr += sr_linesize;
386+ r += dr_linesize;
387+ }
388+
389+ return 0;
390+}
391+
392+static int import_map(AVFilterLink *inlink, AVFrame *in)
393+{
394+ AVFilterContext *ctx = inlink->dst;
395+ ColorMapContext *s = ctx->priv;
396+ const int is_target = FF_INLINK_IDX(inlink) > 1;
397+ const int pw = s->w;
398+ const int pw2 = s->w / 2;
399+ const int ph = s->h;
400+ const int ph2 = s->h / 2;
401+ int changed = 0;
402+ int idx;
403+
404+ for (int plane = 0; plane < 3; plane++) {
405+ const int c = plane == 0 ? 1 : plane == 1 ? 2 : 0;
406+
407+ idx = 0;
408+ for (int y = ph2; y < in->height && idx < MAX_SIZE; y += ph) {
409+ const float *src = (const float *)(in->data[plane] + y * in->linesize[plane]);
410+
411+ for (int x = pw2; x < in->width && idx < MAX_SIZE; x += pw) {
412+ float value = src[x];
413+
414+ if (is_target) {
415+ if (s->ttarget[idx][c] != value)
416+ changed = 1;
417+ s->ttarget[idx][c] = value;
418+ } else {
419+ if (s->source[idx][c] != value)
420+ changed = 1;
421+ s->source[idx][c] = value;
422+ }
423+
424+ idx++;
425+ }
426+ }
427+ }
428+
429+ if (changed)
430+ s->changed[is_target] = 1;
431+ if (!is_target)
432+ s->nb_maps = FFMIN(idx, s->size);
433+
434+ return 0;
435+}
436+
437+static int process_frame(FFFrameSync *fs)
438+{
439+ AVFilterContext *ctx = fs->parent;
440+ ColorMapContext *s = fs->opaque;
441+ AVFilterLink *outlink = ctx->outputs[0];
442+ AVFrame *in, *out, *source, *target;
443+ ThreadData td;
444+ int ret;
445+
446+ switch (s->kernel_type) {
447+ case EUCLIDEAN:
448+ s->kernel = euclidean_kernel;
449+ break;
450+ case WEUCLIDEAN:
451+ s->kernel = weuclidean_kernel;
452+ break;
453+ default:
454+ return AVERROR_BUG;
455+ }
456+
457+ if ((ret = ff_framesync_get_frame(&s->fs, 0, &in, 1)) < 0 ||
458+ (ret = ff_framesync_get_frame(&s->fs, 1, &source, 0)) < 0 ||
459+ (ret = ff_framesync_get_frame(&s->fs, 2, &target, 0)) < 0)
460+ return ret;
461+
462+ import_map(ctx->inputs[1], source);
463+ import_map(ctx->inputs[2], target);
464+
465+ if (s->changed[0] || s->changed[1]) {
466+ build_map(ctx);
467+ s->changed[0] = s->changed[1] = 0;
468+ }
469+
470+ if (!ctx->is_disabled) {
471+ if (av_frame_is_writable(in)) {
472+ out = in;
473+ } else {
474+ out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
475+ if (!out) {
476+ av_frame_free(&in);
477+ return AVERROR(ENOMEM);
478+ }
479+ av_frame_copy_props(out, in);
480+ }
481+
482+ td.in = in;
483+ td.out = out;
484+ ff_filter_execute(ctx, colormap_slice, &td, NULL,
485+ FFMIN(in->height, ff_filter_get_nb_threads(ctx)));
486+
487+ if (out != in)
488+ av_frame_free(&in);
489+ } else {
490+ out = in;
491+ }
492+
493+ out->pts = av_rescale_q(s->fs.pts, s->fs.time_base, outlink->time_base);
494+
495+ return ff_filter_frame(outlink, out);
496+}
497+
498+static int config_output(AVFilterLink *outlink)
499+{
500+ AVFilterContext *ctx = outlink->src;
501+ ColorMapContext *s = ctx->priv;
502+ AVFilterLink *inlink = ctx->inputs[0];
503+ AVFilterLink *source = ctx->inputs[1];
504+ AVFilterLink *target = ctx->inputs[2];
505+ FFFrameSyncIn *in;
506+ int ret;
507+
508+ outlink->time_base = inlink->time_base;
509+ outlink->frame_rate = inlink->frame_rate;
510+ outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
511+ outlink->w = inlink->w;
512+ outlink->h = inlink->h;
513+
514+ if ((ret = ff_framesync_init(&s->fs, ctx, 3)) < 0)
515+ return ret;
516+
517+ in = s->fs.in;
518+ in[0].time_base = inlink->time_base;
519+ in[1].time_base = source->time_base;
520+ in[2].time_base = target->time_base;
521+ in[0].sync = 1;
522+ in[0].before = EXT_STOP;
523+ in[0].after = EXT_INFINITY;
524+ in[1].sync = 1;
525+ in[1].before = EXT_STOP;
526+ in[1].after = EXT_INFINITY;
527+ in[2].sync = 1;
528+ in[2].before = EXT_STOP;
529+ in[2].after = EXT_INFINITY;
530+ s->fs.opaque = s;
531+ s->fs.on_event = process_frame;
532+
533+ ret = ff_framesync_configure(&s->fs);
534+ outlink->time_base = s->fs.time_base;
535+
536+ return ret;
537+}
538+
539+static int activate(AVFilterContext *ctx)
540+{
541+ ColorMapContext *s = ctx->priv;
542+ return ff_framesync_activate(&s->fs);
543+}
544+
545+static const AVFilterPad inputs[] = {
546+ {
547+ .name = "default",
548+ .type = AVMEDIA_TYPE_VIDEO,
549+ },
550+ {
551+ .name = "source",
552+ .type = AVMEDIA_TYPE_VIDEO,
553+ },
554+ {
555+ .name = "target",
556+ .type = AVMEDIA_TYPE_VIDEO,
557+ },
558+};
559+
560+static const AVFilterPad outputs[] = {
561+ {
562+ .name = "default",
563+ .type = AVMEDIA_TYPE_VIDEO,
564+ .config_props = config_output,
565+ },
566+};
567+
568+AVFILTER_DEFINE_CLASS(colormap);
569+
570+const AVFilter ff_vf_colormap = {
571+ .name = "colormap",
572+ .description = NULL_IF_CONFIG_SMALL("Apply custom Color Maps to video stream."),
573+ .priv_class = &colormap_class,
574+ .priv_size = sizeof(ColorMapContext),
575+ .activate = activate,
576+ FILTER_INPUTS(inputs),
577+ FILTER_OUTPUTS(outputs),
578+ FILTER_PIXFMTS(AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32),
579+ .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL |
580+ AVFILTER_FLAG_SLICE_THREADS,
581+ .process_command = ff_filter_process_command,
582+};

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t