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 "filters.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 |
|
|
|