FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_pseudocolor.c
Date: 2024-04-24 18:52:15
Exec Total Coverage
Lines: 0 328 0.0%
Functions: 0 27 0.0%
Branches: 0 153 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2017 Paul B Mahol
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "libavutil/attributes.h"
22 #include "libavutil/common.h"
23 #include "libavutil/eval.h"
24 #include "libavutil/imgutils.h"
25 #include "libavutil/opt.h"
26 #include "libavutil/pixdesc.h"
27 #include "avfilter.h"
28 #include "internal.h"
29 #include "video.h"
30
31 static const char *const var_names[] = {
32 "w", ///< width of the input video
33 "h", ///< height of the input video
34 "val", ///< input value for the pixel
35 "ymin",
36 "umin",
37 "vmin",
38 "amin",
39 "ymax",
40 "umax",
41 "vmax",
42 "amax",
43 NULL
44 };
45
46 enum var_name {
47 VAR_W,
48 VAR_H,
49 VAR_VAL,
50 VAR_YMIN,
51 VAR_UMIN,
52 VAR_VMIN,
53 VAR_AMIN,
54 VAR_YMAX,
55 VAR_UMAX,
56 VAR_VMAX,
57 VAR_AMAX,
58 VAR_VARS_NB
59 };
60
61 enum Curves {
62 MAGMA,
63 INFERNO,
64 PLASMA,
65 VIRIDIS,
66 TURBO,
67 CIVIDIS,
68 SOLAR,
69 SPECTRAL,
70 COOL,
71 HEAT,
72 FIERY,
73 BLUES,
74 GREEN,
75 HELIX,
76 NB_CURVES,
77 };
78
79 enum Presets {
80 PRESET_MAGMA,
81 PRESET_INFERNO,
82 PRESET_PLASMA,
83 PRESET_VIRIDIS,
84 PRESET_TURBO,
85 PRESET_CIVIDIS,
86 PRESET_RANGE1,
87 PRESET_RANGE2,
88 PRESET_SHADOWS,
89 PRESET_HIGHLIGHTS,
90 PRESET_SOLAR,
91 PRESET_NOMINAL,
92 PRESET_PREFERRED,
93 PRESET_TOTAL,
94 PRESET_SPECTRAL,
95 PRESET_COOL,
96 PRESET_HEAT,
97 PRESET_FIERY,
98 PRESET_BLUES,
99 PRESET_GREEN,
100 PRESET_HELIX,
101 NB_PRESETS,
102 };
103
104 typedef double (*curve_fun)(double x);
105
106 typedef struct Curve {
107 double coef[3][8];
108 double offset[3];
109 curve_fun fun[3];
110 int yuv;
111 } Curve;
112
113 typedef struct Fill {
114 float fill[4];
115 } Fill;
116
117 typedef struct Range {
118 AVRational start, end;
119 } Range;
120
121 typedef struct Preset {
122 int nb_segments;
123 const Range *ranges;
124 const Curve *curves;
125 const Fill *fills;
126 } Preset;
127
128 static const Range full_range = {{0, 1}, {1, 1}};
129 static const Range nominal_range[] = {{{0, 1}, {4096, 65536}}, {{4096, 65536}, {60161, 65536}}, {{60161, 65536}, {1, 1}}};
130 static const Range preferred_range[] = {{{0, 1}, {1280, 65536}}, {{1280, 65536}, {62977, 65536}}, {{62977, 65536}, {1, 1}}};
131 static const Range total_range[] = {{{0, 1}, {256, 65536}}, {{256, 65536}, {65280, 65536}}, {{65280, 65536}, {1, 1}}};
132 static const Range spec1_range[] = {{{0, 1}, {16, 256}}, {{16, 256}, {236, 256}}, {{236, 256}, {256, 256}}};
133 static const Range spec2_range[] = {{{0, 1}, {16, 256}}, {{16, 256}, {22, 256}}, {{22, 256}, {226, 256}}, {{226, 256}, {236, 256}}, {{236, 256}, {256, 256}}};
134 static const Range shadows_range[] = {{{0, 1}, {32, 256}}, {{32, 256}, {256, 256}}};
135 static const Range highlights_range[] = {{{0,1}, {214,256}}, {{214, 256}, {224, 256}}, {{224, 256}, {256, 256}}};
136
137 static const Fill spec1_fills[] = {{{0.5f, 0.f, .5f, 1.f}}, {{-1.f, -1.f, -1.f, 1.f}}, {{1.f, 0.f, 0.f, 1.f}}};
138 static const Fill spec2_fills[] = {{{0.5f, 0.f, .5f, 1.f}}, {{0.f, 1.f, 1.f, 1.f}}, {{-1.f, -1.f, -1.f, 1.f}}, {{1.f, 1.f, 0.f, 1.f}}, {{1.f, 0.f, 0.f, 1.f}}};
139 static const Fill shadows_fills[] = {{{0.8f, 0.4f, .8f, 1.f}}, {{-1.f, -1.f, -1.f, 1.f}}};
140 static const Fill highlights_fills[] = {{{-1.f, -1.f, -1.f, 1.f}}, {{1.f, 0.3f, 0.6f, 1.f}}, {{1.f, 0.2f, .5f, 1.f}}};
141
142 static double limit(double x)
143 {
144 return av_clipd(x, 0., 1.);
145 }
146
147 static double solarfun(double x)
148 {
149 return 0.5 * sin(x) + 0.5;
150 }
151
152 static double coolfunu(double x)
153 {
154 return 0.25 * sin(2.0 * x * M_PI - M_PI) + 0.5;
155 }
156
157 static double coolfunv(double x)
158 {
159 return 0.25 * sin(2.0 * x * M_PI) + 0.5;
160 }
161
162 static double heatfunu(double x)
163 {
164 return 0.25 * cos(2.0 * x * M_PI + M_PI) + 0.75;
165 }
166
167 static double heatfunv(double x)
168 {
169 return 0.25 * sin(2.0 * x * M_PI) + 0.5;
170 }
171
172 static double fieryfunu(double x)
173 {
174 return 0.75 - 0.25 * cos(2.0 * x * M_PI);
175 }
176
177 static double fieryfunv(double x)
178 {
179 return 0.25 + 0.25 * cos(2.0 * x * M_PI);
180 }
181
182 static double helixfunu(double x)
183 {
184 return 0.5 + 0.15 * sin(5.0 * x * M_PI + M_PI);
185 }
186
187 static double helixfunv(double x)
188 {
189 return 0.5 + 0.15 * cos(6.0 * x * M_PI + M_PI_2);
190 }
191
192 static const Curve curves[] =
193 {
194 [MAGMA] = {{
195 {-7.5631093e-16, 7.4289183e-13, -2.8525484e-10, 5.4446085e-08, -5.5596238e-06, 3.0569325e-04, -2.3137421e-03, 1.2152095e-02 },
196 { 1.3217636e-15, -1.2214648e-12, 4.4319712e-10, -8.0197993e-08, 7.6598370e-06, -3.6523704e-04, 8.4836670e-03, -2.5536888e-02 },
197 {-1.1446568e-15, 1.0013446e-12, -3.5651575e-10, 6.6775016e-08, -6.7120346e-06, 2.7346619e-04, 4.7969657e-03, 1.1971441e-02 },
198 }, .fun = { limit, limit, limit }, },
199 [INFERNO] = {{
200 {-3.9848859e-18, 9.4821649e-14, -6.7371977e-11, 1.8469937e-08, -2.5359307e-06, 1.7959053e-04, 3.9782564e-04, 2.8845935e-04 },
201 { 6.8408539e-16, -6.5499979e-13, 2.4562526e-10, -4.5989298e-08, 4.5723324e-06, -2.2111913e-04, 5.2023164e-03, -1.1226064e-02 },
202 {-2.9921470e-15, 2.5864165e-12, -8.7403799e-10, 1.4713388e-07, -1.2701505e-05, 4.5159935e-04, 3.1087989e-03, 1.9122831e-02 },
203 }, .fun = { limit, limit, limit }, },
204 [PLASMA] = {{
205 { 3.6196089e-16, -3.3623041e-13, 1.2324010e-10, -2.2769060e-08, 2.2297792e-06, -1.2567829e-04, 9.9791629e-03, 5.7247918e-02 },
206 { 5.0262888e-16, -5.3193896e-13, 2.2451715e-10, -4.7529623e-08, 5.1374873e-06, -2.3260136e-04, 3.1502825e-03, 1.5362491e-02 },
207 {-1.7782261e-16, 2.2487839e-13, -1.0610236e-10, 2.4112644e-08, -2.6331623e-06, 8.9499751e-05, 2.1386328e-03, 5.3824268e-01 },
208 }, .fun = { limit, limit, limit }, },
209 [VIRIDIS] = {{
210 { 9.4850045e-16, -8.6629383e-13, 3.0310944e-10, -5.1340396e-08, 4.6024275e-06, -2.2744239e-04, 4.5559993e-03, 2.5662350e-01 },
211 { 9.6461041e-17, -6.9209477e-14, 1.7625397e-11, -2.0229773e-09, 1.4900110e-07, -1.9315187e-05, 5.8967339e-03, 3.9544827e-03 },
212 { 5.1785449e-16, -3.6663004e-13, 1.0249990e-10, -1.5431998e-08, 1.5007941e-06, -1.2001502e-04, 7.6951526e-03, 3.2292815e-01 },
213 }, .fun = { limit, limit, limit }, },
214 [TURBO] = {{
215 {-4.3683890e-15, 3.7020347e-12, -1.1712592e-09, 1.6401790e-07, -8.6842919e-06, -1.8542465e-06, 8.4485325e-03, 1.6267077e-01 },
216 {-4.0011069e-16, 2.7861423e-13, -6.3388921e-11, 5.8872238e-09, -5.4466522e-07, 1.8037114e-05, 1.0599869e-02, 7.6914696e-02 },
217 {-2.8242609e-15, 2.9234108e-12, -1.1726546e-09, 2.2552115e-07, -2.0059387e-05, 5.0595552e-04, 1.7714932e-02, 2.7271836e-01 },
218 }, .fun = { limit, limit, limit }, },
219 [CIVIDIS] = {{
220 {-9.5484131e-16, 9.6988184e-13, -4.0058766e-10, 8.5743924e-08, -9.9644797e-06, 5.9197908e-04, -1.0361579e-02, 3.3164429e-02 },
221 { 1.2731941e-17, -9.4238449e-15, 2.2808841e-12, -1.1548296e-10, -2.3888913e-08, 3.8986680e-06, 2.5879330e-03, 1.2769733e-01 },
222 { 4.6004608e-16, -5.0686849e-13, 2.2753449e-10, -5.3074099e-08, 6.7196096e-06, -4.4120020e-04, 1.3435551e-02, 2.8293355e-01 },
223 }, .fun = { limit, limit, limit }, },
224 [SOLAR] = {{
225 { 0, 0, 0, 0, 0.000001983938313, -0.0007618323, 0.2, -M_PI_2 },
226 { 0, 0, 0, 0, 0.000001983938313, -0.0007618323, 0.2, -M_PI_2 },
227 { 0, 0, 0, 0, 0.000001983938313, -0.0007618323, 0.2, -M_PI_2 },
228 },
229 .offset = { 0., -9., 9. },
230 .fun = { solarfun, solarfun, solarfun }, },
231 [SPECTRAL] = {{
232 { -1.6820e-15, 1.4982e-12, -5.0442e-10, 8.0490e-08, -6.1903e-06, 1.5821e-04, 6.4359e-03, 6.2887e-01 },
233 { 1.2526e-15, -1.2203e-12, 4.7013e-10, -8.9360e-08, 8.3839e-06, -3.6642e-04, 1.4784e-02, -9.8075e-03 },
234 { 1.4755e-15, -1.6765e-12, 7.3188e-10, -1.5522e-07, 1.6406e-05, -7.7883e-04, 1.4502e-02, 2.1597e-01 },
235 }, .fun = { limit, limit, limit }, },
236 [COOL] = {{
237 { 0, 0, 0, 0, 0, 0, 1./256, 0 },
238 { 0, 0, 0, 0, 0, 0, 1./256, 0 },
239 { 0, 0, 0, 0, 0, 0, 1./256, 0 },
240 },
241 .offset = { 0., 0., 0 },
242 .yuv = 1,
243 .fun = { coolfunu, limit, coolfunv }, },
244 [HEAT] = {{
245 { 0, 0, 0, 0, 0, 0, 1./256, 0 },
246 { 0, 0, 0, 0, 0, 0, 1./256, 0 },
247 { 0, 0, 0, 0, 0, 0, 1./256, 0 },
248 },
249 .offset = { 0., 0., 0 },
250 .yuv = 1,
251 .fun = { heatfunu, limit, heatfunv }, },
252 [FIERY] = {{
253 { 0, 0, 0, 0, 0, 0, 1./256, 0 },
254 { 0, 0, 0, 0, 0, 0, 1./256, 0 },
255 { 0, 0, 0, 0, 0, 0, 1./256, 0 },
256 },
257 .offset = { 0., 0., 0 },
258 .yuv = 1,
259 .fun = { fieryfunu, limit, fieryfunv }, },
260 [BLUES] = {{
261 { 0, 0, 0, 0, 0, 0, 1./256, 0 },
262 { 0, 0, 0, 0, 0, 0, 1./256, 0 },
263 { 0, 0, 0, 0, 0, 0, 1./256, 0 },
264 },
265 .offset = { 0., 0., 0 },
266 .yuv = 1,
267 .fun = { fieryfunv, limit, fieryfunu }, },
268 [GREEN] = {{
269 { 0, 0, 0, 0, 0, 0, 1./256, 0 },
270 { 0, 0, 0, 0, 0, 0, 1./256, 0 },
271 { 0, 0, 0, 0, 0, 0, 1./256, 0 },
272 },
273 .offset = { 0., 0., 0 },
274 .yuv = 1,
275 .fun = { fieryfunv, limit, fieryfunv }, },
276 [HELIX] = {{
277 { 0, 0, 0, 0, 0, 0, 1./256, 0 },
278 { 0, 0, 0, 0, 0, 0, 1./256, 0 },
279 { 0, 0, 0, 0, 0, 0, 1./256, 0 },
280 },
281 .offset = { 0., 0., 0 },
282 .yuv = 1,
283 .fun = { helixfunu, limit, helixfunv }, },
284 };
285
286 static const Preset presets[] =
287 {
288 [PRESET_MAGMA] = { 1, &full_range, &curves[MAGMA], NULL },
289 [PRESET_INFERNO] = { 1, &full_range, &curves[INFERNO], NULL },
290 [PRESET_PLASMA] = { 1, &full_range, &curves[PLASMA], NULL },
291 [PRESET_VIRIDIS] = { 1, &full_range, &curves[VIRIDIS], NULL },
292 [PRESET_TURBO] = { 1, &full_range, &curves[TURBO], NULL },
293 [PRESET_CIVIDIS] = { 1, &full_range, &curves[CIVIDIS], NULL },
294 [PRESET_RANGE1] = { 3, spec1_range, NULL, spec1_fills },
295 [PRESET_NOMINAL] = { 3, nominal_range, NULL, spec1_fills },
296 [PRESET_PREFERRED]={ 3, preferred_range, NULL, spec1_fills },
297 [PRESET_TOTAL] = { 3, total_range, NULL, spec1_fills },
298 [PRESET_RANGE2] = { 5, spec2_range, NULL, spec2_fills },
299 [PRESET_SHADOWS] = { 2, shadows_range, NULL, shadows_fills },
300 [PRESET_HIGHLIGHTS] = { 3, highlights_range, NULL, highlights_fills },
301 [PRESET_SOLAR] = { 1, &full_range, &curves[SOLAR], NULL },
302 [PRESET_SPECTRAL]= { 1, &full_range, &curves[SPECTRAL],NULL },
303 [PRESET_COOL] = { 1, &full_range, &curves[COOL], NULL },
304 [PRESET_HEAT] = { 1, &full_range, &curves[HEAT], NULL },
305 [PRESET_FIERY] = { 1, &full_range, &curves[FIERY], NULL },
306 [PRESET_BLUES] = { 1, &full_range, &curves[BLUES], NULL },
307 [PRESET_GREEN] = { 1, &full_range, &curves[GREEN], NULL },
308 [PRESET_HELIX] = { 1, &full_range, &curves[HELIX], NULL },
309 };
310
311 typedef struct PseudoColorContext {
312 const AVClass *class;
313 int preset;
314 float opacity;
315 int max;
316 int index;
317 int nb_planes;
318 int color;
319 int linesize[4];
320 int width[4], height[4];
321 double var_values[VAR_VARS_NB];
322 char *comp_expr_str[4];
323 AVExpr *comp_expr[4];
324 float lut[4][256*256];
325
326 void (*filter[4])(int max, int width, int height,
327 const uint8_t *index, const uint8_t *src,
328 uint8_t *dst,
329 ptrdiff_t ilinesize,
330 ptrdiff_t slinesize,
331 ptrdiff_t dlinesize,
332 float *lut,
333 float opacity);
334 } PseudoColorContext;
335
336 #define OFFSET(x) offsetof(PseudoColorContext, x)
337 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
338
339 static const AVOption pseudocolor_options[] = {
340 { "c0", "set component #0 expression", OFFSET(comp_expr_str[0]), AV_OPT_TYPE_STRING, {.str="val"}, .flags = FLAGS },
341 { "c1", "set component #1 expression", OFFSET(comp_expr_str[1]), AV_OPT_TYPE_STRING, {.str="val"}, .flags = FLAGS },
342 { "c2", "set component #2 expression", OFFSET(comp_expr_str[2]), AV_OPT_TYPE_STRING, {.str="val"}, .flags = FLAGS },
343 { "c3", "set component #3 expression", OFFSET(comp_expr_str[3]), AV_OPT_TYPE_STRING, {.str="val"}, .flags = FLAGS },
344 { "index", "set component as base", OFFSET(index), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, .flags = FLAGS },
345 { "i", "set component as base", OFFSET(index), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, .flags = FLAGS },
346 { "preset", "set preset", OFFSET(preset), AV_OPT_TYPE_INT, {.i64=-1},-1, NB_PRESETS-1, .flags = FLAGS, .unit = "preset" },
347 { "p", "set preset", OFFSET(preset), AV_OPT_TYPE_INT, {.i64=-1},-1, NB_PRESETS-1, .flags = FLAGS, .unit = "preset" },
348 { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=-1}, .flags = FLAGS, .unit = "preset" },
349 { "magma", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_MAGMA}, .flags = FLAGS, .unit = "preset" },
350 { "inferno", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_INFERNO}, .flags = FLAGS, .unit = "preset" },
351 { "plasma", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_PLASMA}, .flags = FLAGS, .unit = "preset" },
352 { "viridis", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_VIRIDIS}, .flags = FLAGS, .unit = "preset" },
353 { "turbo", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_TURBO}, .flags = FLAGS, .unit = "preset" },
354 { "cividis", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_CIVIDIS}, .flags = FLAGS, .unit = "preset" },
355 { "range1", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_RANGE1}, .flags = FLAGS, .unit = "preset" },
356 { "range2", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_RANGE2}, .flags = FLAGS, .unit = "preset" },
357 { "shadows", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_SHADOWS}, .flags = FLAGS, .unit = "preset" },
358 { "highlights", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_HIGHLIGHTS},.flags=FLAGS, .unit = "preset" },
359 { "solar", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_SOLAR}, .flags=FLAGS, .unit = "preset" },
360 { "nominal", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_NOMINAL}, .flags=FLAGS, .unit = "preset" },
361 { "preferred", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_PREFERRED},.flags=FLAGS, .unit = "preset" },
362 { "total", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_TOTAL}, .flags=FLAGS, .unit = "preset" },
363 { "spectral", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_SPECTRAL},.flags = FLAGS, .unit = "preset" },
364 { "cool", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_COOL}, .flags = FLAGS, .unit = "preset" },
365 { "heat", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_HEAT}, .flags = FLAGS, .unit = "preset" },
366 { "fiery", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_FIERY}, .flags = FLAGS, .unit = "preset" },
367 { "blues", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_BLUES}, .flags = FLAGS, .unit = "preset" },
368 { "green", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_GREEN}, .flags = FLAGS, .unit = "preset" },
369 { "helix", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_HELIX}, .flags = FLAGS, .unit = "preset" },
370 { "opacity", "set pseudocolor opacity",OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0, 1, .flags = FLAGS },
371 { NULL }
372 };
373
374 static const enum AVPixelFormat pix_fmts[] = {
375 AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16,
376 AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVA420P,
377 AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA422P,
378 AV_PIX_FMT_YUV444P, AV_PIX_FMT_GBRP,
379 AV_PIX_FMT_YUVA444P, AV_PIX_FMT_GBRAP,
380 AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUVA422P9,
381 AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUVA420P9,
382 AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUVA444P9,
383 AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUVA420P10,
384 AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUVA422P10,
385 AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUVA444P10,
386 AV_PIX_FMT_YUV420P12,
387 AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUVA422P12,
388 AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUVA444P12,
389 AV_PIX_FMT_YUV420P14,
390 AV_PIX_FMT_YUV422P14,
391 AV_PIX_FMT_YUV444P14,
392 AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUVA420P16,
393 AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUVA422P16,
394 AV_PIX_FMT_YUV444P16, AV_PIX_FMT_YUVA444P16,
395 AV_PIX_FMT_GBRP9,
396 AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10,
397 AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12,
398 AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRAP14,
399 AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16,
400 AV_PIX_FMT_NONE
401 };
402
403 static inline float lerpf(float v0, float v1, float f)
404 {
405 return v0 + (v1 - v0) * f;
406 }
407
408 #define PCLIP(v, max, dst, src, x) \
409 if (v >= 0 && v <= max) { \
410 dst[x] = lerpf(src[x], v, opacity);\
411 } else { \
412 dst[x] = src[x]; \
413 }
414
415 static void pseudocolor_filter(int max, int width, int height,
416 const uint8_t *index,
417 const uint8_t *src,
418 uint8_t *dst,
419 ptrdiff_t ilinesize,
420 ptrdiff_t slinesize,
421 ptrdiff_t dlinesize,
422 float *lut,
423 float opacity)
424 {
425 int x, y;
426
427 for (y = 0; y < height; y++) {
428 for (x = 0; x < width; x++) {
429 int v = lut[index[x]];
430
431 PCLIP(v, max, dst, src, x);
432 }
433 index += ilinesize;
434 src += slinesize;
435 dst += dlinesize;
436 }
437 }
438
439 static void pseudocolor_filter_11(int max, int width, int height,
440 const uint8_t *index,
441 const uint8_t *src,
442 uint8_t *dst,
443 ptrdiff_t ilinesize,
444 ptrdiff_t slinesize,
445 ptrdiff_t dlinesize,
446 float *lut,
447 float opacity)
448 {
449 int x, y;
450
451 for (y = 0; y < height; y++) {
452 for (x = 0; x < width; x++) {
453 int v = lut[index[(y << 1) * ilinesize + (x << 1)]];
454
455 PCLIP(v, max, dst, src, x);
456 }
457 src += slinesize;
458 dst += dlinesize;
459 }
460 }
461
462 static void pseudocolor_filter_11d(int max, int width, int height,
463 const uint8_t *index,
464 const uint8_t *src,
465 uint8_t *dst,
466 ptrdiff_t ilinesize,
467 ptrdiff_t slinesize,
468 ptrdiff_t dlinesize,
469 float *lut,
470 float opacity)
471 {
472 int x, y;
473
474 for (y = 0; y < height; y++) {
475 for (x = 0; x < width; x++) {
476 int v = lut[index[(y >> 1) * ilinesize + (x >> 1)]];
477
478 PCLIP(v, max, dst, src, x);
479 }
480 src += slinesize;
481 dst += dlinesize;
482 }
483 }
484
485 static void pseudocolor_filter_10(int max, int width, int height,
486 const uint8_t *index,
487 const uint8_t *src,
488 uint8_t *dst,
489 ptrdiff_t ilinesize,
490 ptrdiff_t slinesize,
491 ptrdiff_t dlinesize,
492 float *lut,
493 float opacity)
494 {
495 int x, y;
496
497 for (y = 0; y < height; y++) {
498 for (x = 0; x < width; x++) {
499 int v = lut[index[x << 1]];
500
501 PCLIP(v, max, dst, src, x);
502 }
503 index += ilinesize;
504 src += slinesize;
505 dst += dlinesize;
506 }
507 }
508
509 static void pseudocolor_filter_10d(int max, int width, int height,
510 const uint8_t *index,
511 const uint8_t *src,
512 uint8_t *dst,
513 ptrdiff_t ilinesize,
514 ptrdiff_t slinesize,
515 ptrdiff_t dlinesize,
516 float *lut,
517 float opacity)
518 {
519 int x, y;
520
521 for (y = 0; y < height; y++) {
522 for (x = 0; x < width; x++) {
523 int v = lut[index[x >> 1]];
524
525 PCLIP(v, max, dst, src, x);
526 }
527 index += ilinesize;
528 src += slinesize;
529 dst += dlinesize;
530 }
531 }
532
533 static void pseudocolor_filter_16(int max, int width, int height,
534 const uint8_t *iindex,
535 const uint8_t *ssrc,
536 uint8_t *ddst,
537 ptrdiff_t ilinesize,
538 ptrdiff_t slinesize,
539 ptrdiff_t dlinesize,
540 float *lut,
541 float opacity)
542 {
543 const uint16_t *index = (const uint16_t *)iindex;
544 const uint16_t *src = (const uint16_t *)ssrc;
545 uint16_t *dst = (uint16_t *)ddst;
546 int x, y;
547
548 for (y = 0; y < height; y++) {
549 for (x = 0; x < width; x++) {
550 int v = lut[index[x]];
551
552 PCLIP(v, max, dst, src, x);
553 }
554 index += ilinesize / 2;
555 src += slinesize / 2;
556 dst += dlinesize / 2;
557 }
558 }
559
560 static void pseudocolor_filter_16_10(int max, int width, int height,
561 const uint8_t *iindex,
562 const uint8_t *ssrc,
563 uint8_t *ddst,
564 ptrdiff_t ilinesize,
565 ptrdiff_t slinesize,
566 ptrdiff_t dlinesize,
567 float *lut,
568 float opacity)
569 {
570 const uint16_t *index = (const uint16_t *)iindex;
571 const uint16_t *src = (const uint16_t *)ssrc;
572 uint16_t *dst = (uint16_t *)ddst;
573 int x, y;
574
575 for (y = 0; y < height; y++) {
576 for (x = 0; x < width; x++) {
577 int v = lut[index[x << 1]];
578
579 PCLIP(v, max, dst, src, x);
580 }
581 index += ilinesize / 2;
582 src += slinesize / 2;
583 dst += dlinesize / 2;
584 }
585 }
586
587 static void pseudocolor_filter_16_10d(int max, int width, int height,
588 const uint8_t *iindex,
589 const uint8_t *ssrc,
590 uint8_t *ddst,
591 ptrdiff_t ilinesize,
592 ptrdiff_t slinesize,
593 ptrdiff_t dlinesize,
594 float *lut,
595 float opacity)
596 {
597 const uint16_t *index = (const uint16_t *)iindex;
598 const uint16_t *src = (const uint16_t *)ssrc;
599 uint16_t *dst = (uint16_t *)ddst;
600 int x, y;
601
602 for (y = 0; y < height; y++) {
603 for (x = 0; x < width; x++) {
604 int v = lut[index[x >> 1]];
605
606 PCLIP(v, max, dst, src, x);
607 }
608 index += ilinesize / 2;
609 src += slinesize / 2;
610 dst += dlinesize / 2;
611 }
612 }
613
614 static void pseudocolor_filter_16_11(int max, int width, int height,
615 const uint8_t *iindex,
616 const uint8_t *ssrc,
617 uint8_t *ddst,
618 ptrdiff_t ilinesize,
619 ptrdiff_t slinesize,
620 ptrdiff_t dlinesize,
621 float *lut,
622 float opacity)
623 {
624 const uint16_t *index = (const uint16_t *)iindex;
625 const uint16_t *src = (const uint16_t *)ssrc;
626 uint16_t *dst = (uint16_t *)ddst;
627 int x, y;
628
629 ilinesize /= 2;
630 dlinesize /= 2;
631 slinesize /= 2;
632
633 for (y = 0; y < height; y++) {
634 for (x = 0; x < width; x++) {
635 int v = lut[index[(y << 1) * ilinesize + (x << 1)]];
636
637 PCLIP(v, max, dst, src, x);
638 }
639 src += slinesize;
640 dst += dlinesize;
641 }
642 }
643
644 static void pseudocolor_filter_16_11d(int max, int width, int height,
645 const uint8_t *iindex,
646 const uint8_t *ssrc,
647 uint8_t *ddst,
648 ptrdiff_t ilinesize,
649 ptrdiff_t slinesize,
650 ptrdiff_t dlinesize,
651 float *lut,
652 float opacity)
653 {
654 const uint16_t *index = (const uint16_t *)iindex;
655 const uint16_t *src = (const uint16_t *)ssrc;
656 uint16_t *dst = (uint16_t *)ddst;
657 int x, y;
658
659 ilinesize /= 2;
660 dlinesize /= 2;
661 slinesize /= 2;
662
663 for (y = 0; y < height; y++) {
664 for (x = 0; x < width; x++) {
665 int v = lut[index[(y >> 1) * ilinesize + (x >> 1)]];
666
667 PCLIP(v, max, dst, src, x);
668 }
669 src += slinesize;
670 dst += dlinesize;
671 }
672 }
673
674 #define RGB_TO_Y_BT709(r, g, b) \
675 ((0.21260*219.0/255.0) * (r) + (0.71520*219.0/255.0) * (g) + \
676 (0.07220*219.0/255.0) * (b))
677
678 #define RGB_TO_U_BT709(r1, g1, b1, max) \
679 (-(0.11457*224.0/255.0) * r1 - (0.38543*224.0/255.0) * g1 + \
680 (0.50000*224.0/255.0) * b1 + max * 0.5)
681
682 #define RGB_TO_V_BT709(r1, g1, b1, max) \
683 ((0.50000*224.0/255.0) * r1 - (0.45415*224.0/255.0) * g1 - \
684 (0.04585*224.0/255.0) * b1 + max * 0.5)
685
686 #define Wr 0.2126
687 #define Wb 0.0722
688 #define Wg (1 - Wr - Wb)
689 #define Umax 0.436
690 #define Vmax 0.615
691
692 #define YUV_BT709_TO_R(y, u, v, max) \
693 ((y + v * (1 - Wr) / Vmax) * max)
694 #define YUV_BT709_TO_G(y, u, v, max) \
695 ((y - (u * Wb * (1 - Wb) / (Umax * Wg)) - (v * Wr * (1 - Wr) / (Vmax * Wg))) * max)
696 #define YUV_BT709_TO_B(y, u, v, max) \
697 ((y + u * (1 - Wb) / Umax) * max)
698
699 static double poly_eval(const double *const poly, double x, curve_fun fun)
700 {
701 double res = 0.;
702
703 for (int i = 0; i < 8; i++) {
704 res += pow(x, i) * poly[7-i];
705 }
706
707 return fun(res);
708 }
709
710 static int config_input(AVFilterLink *inlink)
711 {
712 AVFilterContext *ctx = inlink->dst;
713 PseudoColorContext *s = ctx->priv;
714 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
715 int depth, ret, hsub, vsub, color, rgb;
716
717 rgb = desc->flags & AV_PIX_FMT_FLAG_RGB;
718 depth = desc->comp[0].depth;
719 s->max = (1 << depth) - 1;
720 s->nb_planes = av_pix_fmt_count_planes(inlink->format);
721
722 if (s->index >= s->nb_planes) {
723 av_log(ctx, AV_LOG_ERROR, "index out of allowed range\n");
724 return AVERROR(EINVAL);
725 }
726
727 if ((ret = av_image_fill_linesizes(s->linesize, inlink->format, inlink->w)) < 0)
728 return ret;
729
730 hsub = desc->log2_chroma_w;
731 vsub = desc->log2_chroma_h;
732 s->height[1] = s->height[2] = AV_CEIL_RSHIFT(inlink->h, vsub);
733 s->height[0] = s->height[3] = inlink->h;
734 s->width[1] = s->width[2] = AV_CEIL_RSHIFT(inlink->w, hsub);
735 s->width[0] = s->width[3] = inlink->w;
736
737 s->var_values[VAR_W] = inlink->w;
738 s->var_values[VAR_H] = inlink->h;
739
740 s->var_values[VAR_YMIN] = 16 * (1 << (depth - 8));
741 s->var_values[VAR_UMIN] = 16 * (1 << (depth - 8));
742 s->var_values[VAR_VMIN] = 16 * (1 << (depth - 8));
743 s->var_values[VAR_AMIN] = 0;
744 s->var_values[VAR_YMAX] = 235 * (1 << (depth - 8));
745 s->var_values[VAR_UMAX] = 240 * (1 << (depth - 8));
746 s->var_values[VAR_VMAX] = 240 * (1 << (depth - 8));
747 s->var_values[VAR_AMAX] = s->max;
748
749 for (color = 0; color < s->nb_planes && s->preset < 0; color++) {
750 double res;
751 int val;
752
753 /* create the parsed expression */
754 av_expr_free(s->comp_expr[color]);
755 s->comp_expr[color] = NULL;
756 ret = av_expr_parse(&s->comp_expr[color], s->comp_expr_str[color],
757 var_names, NULL, NULL, NULL, NULL, 0, ctx);
758 if (ret < 0) {
759 av_log(ctx, AV_LOG_ERROR,
760 "Error when parsing the expression '%s' for the component %d and color %d.\n",
761 s->comp_expr_str[color], color, color);
762 return AVERROR(EINVAL);
763 }
764
765 /* compute the lut */
766 for (val = 0; val < FF_ARRAY_ELEMS(s->lut[color]); val++) {
767 s->var_values[VAR_VAL] = val;
768
769 res = av_expr_eval(s->comp_expr[color], s->var_values, s);
770 if (isnan(res)) {
771 av_log(ctx, AV_LOG_ERROR,
772 "Error when evaluating the expression '%s' for the value %d for the component %d.\n",
773 s->comp_expr_str[color], val, color);
774 return AVERROR(EINVAL);
775 }
776 s->lut[color][val] = res;
777 }
778 }
779
780 if (s->preset >= 0) {
781 int nb_segments = presets[s->preset].nb_segments;
782
783 for (int seg = 0; seg < nb_segments; seg++) {
784 AVRational rstart = presets[s->preset].ranges[seg].start;
785 AVRational rend = presets[s->preset].ranges[seg].end;
786 int start = av_rescale_rnd(s->max + 1, rstart.num, rstart.den, AV_ROUND_UP);
787 int end = av_rescale_rnd(s->max + 1, rend.num, rend.den, AV_ROUND_UP);
788
789 for (int i = start; i < end; i++) {
790 if (!presets[s->preset].curves) {
791 const Fill fill = presets[s->preset].fills[seg];
792 double r, g, b, a;
793
794 g = fill.fill[1];
795 b = fill.fill[2];
796 r = fill.fill[0];
797 a = fill.fill[3];
798
799 if (g >= 0.f && b >= 0.f && r >= 0.f) {
800 g *= s->max;
801 b *= s->max;
802 r *= s->max;
803
804 if (!rgb) {
805 double y = RGB_TO_Y_BT709(r, g, b);
806 double u = RGB_TO_U_BT709(r, g, b, s->max);
807 double v = RGB_TO_V_BT709(r, g, b, s->max);
808
809 r = v;
810 g = y;
811 b = u;
812 }
813 }
814
815 s->lut[0][i] = g;
816 s->lut[1][i] = b;
817 s->lut[2][i] = r;
818 s->lut[3][i] = a * s->max;
819 } else {
820 const Curve curve = presets[s->preset].curves[seg];
821 const double lf = i / (double)s->max * 256.;
822 double r, g, b;
823
824 g = poly_eval(curve.coef[1], lf + curve.offset[1], curve.fun[1]);
825 b = poly_eval(curve.coef[2], lf + curve.offset[2], curve.fun[2]);
826 r = poly_eval(curve.coef[0], lf + curve.offset[0], curve.fun[0]);
827
828 if (!curve.yuv || !rgb) {
829 g *= s->max;
830 b *= s->max;
831 r *= s->max;
832 }
833
834 if (!rgb && !curve.yuv) {
835 double y = RGB_TO_Y_BT709(r, g, b);
836 double u = RGB_TO_U_BT709(r, g, b, s->max);
837 double v = RGB_TO_V_BT709(r, g, b, s->max);
838
839 r = v;
840 g = y;
841 b = u;
842 } else if (rgb && curve.yuv) {
843 double y = g;
844 double u = b - 0.5;
845 double v = r - 0.5;
846
847 r = av_clipd(YUV_BT709_TO_R(y, u, v, s->max), 0, s->max);
848 g = av_clipd(YUV_BT709_TO_G(y, u, v, s->max), 0, s->max);
849 b = av_clipd(YUV_BT709_TO_B(y, u, v, s->max), 0, s->max);
850 }
851
852 s->lut[0][i] = g;
853 s->lut[1][i] = b;
854 s->lut[2][i] = r;
855 s->lut[3][i] = 1.f * s->max;
856 }
857 }
858 }
859 }
860
861 switch (inlink->format) {
862 case AV_PIX_FMT_YUV444P:
863 case AV_PIX_FMT_YUVA444P:
864 case AV_PIX_FMT_GBRP:
865 case AV_PIX_FMT_GBRAP:
866 case AV_PIX_FMT_GRAY8:
867 s->filter[0] = s->filter[1] = s->filter[2] = s->filter[3] = pseudocolor_filter;
868 break;
869 case AV_PIX_FMT_YUV420P:
870 case AV_PIX_FMT_YUVA420P:
871 switch (s->index) {
872 case 0:
873 case 3:
874 s->filter[0] = s->filter[3] = pseudocolor_filter;
875 s->filter[1] = s->filter[2] = pseudocolor_filter_11;
876 break;
877 case 1:
878 case 2:
879 s->filter[0] = s->filter[3] = pseudocolor_filter_11d;
880 s->filter[1] = s->filter[2] = pseudocolor_filter;
881 break;
882 }
883 break;
884 case AV_PIX_FMT_YUV422P:
885 case AV_PIX_FMT_YUVA422P:
886 switch (s->index) {
887 case 0:
888 case 3:
889 s->filter[0] = s->filter[3] = pseudocolor_filter;
890 s->filter[1] = s->filter[2] = pseudocolor_filter_10;
891 break;
892 case 1:
893 case 2:
894 s->filter[0] = s->filter[3] = pseudocolor_filter_10d;
895 s->filter[1] = s->filter[2] = pseudocolor_filter;
896 break;
897 }
898 break;
899 case AV_PIX_FMT_YUV444P9:
900 case AV_PIX_FMT_YUVA444P9:
901 case AV_PIX_FMT_YUV444P10:
902 case AV_PIX_FMT_YUVA444P10:
903 case AV_PIX_FMT_YUV444P12:
904 case AV_PIX_FMT_YUVA444P12:
905 case AV_PIX_FMT_YUV444P14:
906 case AV_PIX_FMT_YUV444P16:
907 case AV_PIX_FMT_YUVA444P16:
908 case AV_PIX_FMT_GBRP9:
909 case AV_PIX_FMT_GBRP10:
910 case AV_PIX_FMT_GBRP12:
911 case AV_PIX_FMT_GBRP14:
912 case AV_PIX_FMT_GBRP16:
913 case AV_PIX_FMT_GBRAP10:
914 case AV_PIX_FMT_GBRAP12:
915 case AV_PIX_FMT_GBRAP14:
916 case AV_PIX_FMT_GBRAP16:
917 case AV_PIX_FMT_GRAY9:
918 case AV_PIX_FMT_GRAY10:
919 case AV_PIX_FMT_GRAY12:
920 case AV_PIX_FMT_GRAY14:
921 case AV_PIX_FMT_GRAY16:
922 s->filter[0] = s->filter[1] = s->filter[2] = s->filter[3] = pseudocolor_filter_16;
923 break;
924 case AV_PIX_FMT_YUV422P9:
925 case AV_PIX_FMT_YUVA422P9:
926 case AV_PIX_FMT_YUV422P10:
927 case AV_PIX_FMT_YUVA422P10:
928 case AV_PIX_FMT_YUV422P12:
929 case AV_PIX_FMT_YUVA422P12:
930 case AV_PIX_FMT_YUV422P14:
931 case AV_PIX_FMT_YUV422P16:
932 case AV_PIX_FMT_YUVA422P16:
933 switch (s->index) {
934 case 0:
935 case 3:
936 s->filter[0] = s->filter[3] = pseudocolor_filter_16;
937 s->filter[1] = s->filter[2] = pseudocolor_filter_16_10;
938 break;
939 case 1:
940 case 2:
941 s->filter[0] = s->filter[3] = pseudocolor_filter_16_10d;
942 s->filter[1] = s->filter[2] = pseudocolor_filter_16;
943 break;
944 }
945 break;
946 case AV_PIX_FMT_YUV420P9:
947 case AV_PIX_FMT_YUVA420P9:
948 case AV_PIX_FMT_YUV420P10:
949 case AV_PIX_FMT_YUVA420P10:
950 case AV_PIX_FMT_YUV420P12:
951 case AV_PIX_FMT_YUV420P14:
952 case AV_PIX_FMT_YUV420P16:
953 case AV_PIX_FMT_YUVA420P16:
954 switch (s->index) {
955 case 0:
956 case 3:
957 s->filter[0] = s->filter[3] = pseudocolor_filter_16;
958 s->filter[1] = s->filter[2] = pseudocolor_filter_16_11;
959 break;
960 case 1:
961 case 2:
962 s->filter[0] = s->filter[3] = pseudocolor_filter_16_11d;
963 s->filter[1] = s->filter[2] = pseudocolor_filter_16;
964 break;
965 }
966 break;
967 }
968
969 return 0;
970 }
971
972 typedef struct ThreadData {
973 AVFrame *in, *out;
974 } ThreadData;
975
976 static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
977 {
978 PseudoColorContext *s = ctx->priv;
979 ThreadData *td = arg;
980 AVFrame *in = td->in;
981 AVFrame *out = td->out;
982
983 for (int plane = 0; plane < s->nb_planes; plane++) {
984 const int slice_start = (s->height[plane] * jobnr) / nb_jobs;
985 const int slice_end = (s->height[plane] * (jobnr+1)) / nb_jobs;
986 const int islice_start = (s->height[s->index] * jobnr) / nb_jobs;
987 ptrdiff_t ilinesize = in->linesize[s->index];
988 ptrdiff_t slinesize = in->linesize[plane];
989 ptrdiff_t dlinesize = out->linesize[plane];
990 const uint8_t *index = in->data[s->index] + islice_start * ilinesize;
991 const uint8_t *src = in->data[plane] + slice_start * slinesize;
992 uint8_t *dst = out->data[plane] + slice_start * dlinesize;
993
994 s->filter[plane](s->max, s->width[plane], slice_end - slice_start,
995 index, src, dst, ilinesize, slinesize,
996 dlinesize, s->lut[plane], s->opacity);
997 }
998
999 return 0;
1000 }
1001
1002 static int filter_frame(AVFilterLink *inlink, AVFrame *in)
1003 {
1004 AVFilterContext *ctx = inlink->dst;
1005 PseudoColorContext *s = ctx->priv;
1006 AVFilterLink *outlink = ctx->outputs[0];
1007 ThreadData td;
1008 AVFrame *out;
1009
1010 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
1011 if (!out) {
1012 av_frame_free(&in);
1013 return AVERROR(ENOMEM);
1014 }
1015 av_frame_copy_props(out, in);
1016
1017 td.out = out, td.in = in;
1018 ff_filter_execute(ctx, filter_slice, &td, NULL,
1019 FFMIN(s->height[1], ff_filter_get_nb_threads(ctx)));
1020
1021 av_frame_free(&in);
1022 return ff_filter_frame(outlink, out);
1023 }
1024
1025 static int process_command(AVFilterContext *ctx, const char *cmd, const char *args,
1026 char *res, int res_len, int flags)
1027 {
1028 int ret = ff_filter_process_command(ctx, cmd, args, res, res_len, flags);
1029
1030 if (ret < 0)
1031 return ret;
1032
1033 return config_input(ctx->inputs[0]);
1034 }
1035
1036 static const AVFilterPad inputs[] = {
1037 {
1038 .name = "default",
1039 .type = AVMEDIA_TYPE_VIDEO,
1040 .filter_frame = filter_frame,
1041 .config_props = config_input,
1042 },
1043 };
1044
1045 static av_cold void uninit(AVFilterContext *ctx)
1046 {
1047 PseudoColorContext *s = ctx->priv;
1048 int i;
1049
1050 for (i = 0; i < 4; i++) {
1051 av_expr_free(s->comp_expr[i]);
1052 s->comp_expr[i] = NULL;
1053 }
1054 }
1055
1056 AVFILTER_DEFINE_CLASS(pseudocolor);
1057
1058 const AVFilter ff_vf_pseudocolor = {
1059 .name = "pseudocolor",
1060 .description = NULL_IF_CONFIG_SMALL("Make pseudocolored video frames."),
1061 .priv_size = sizeof(PseudoColorContext),
1062 .priv_class = &pseudocolor_class,
1063 .uninit = uninit,
1064 FILTER_INPUTS(inputs),
1065 FILTER_OUTPUTS(ff_video_default_filterpad),
1066 FILTER_PIXFMTS_ARRAY(pix_fmts),
1067 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
1068 .process_command = process_command,
1069 };
1070