FFmpeg
Loading...
Searching...
No Matches
fflcms2.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2022 Niklas Haas
3 * This file is part of FFmpeg.
4 *
5 * FFmpeg is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * FFmpeg is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with FFmpeg; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#include "libavutil/csp.h"
21
22#include "fflcms2.h"
23
24static void log_cb(cmsContext ctx, cmsUInt32Number error, const char *str)
25{
26 FFIccContext *s = cmsGetContextUserData(ctx);
27 av_log(s->avctx, AV_LOG_ERROR, "lcms2: [%"PRIu32"] %s\n", error, str);
28}
29
31{
32 memset(s, 0, sizeof(*s));
33 s->avctx = avctx;
34 s->ctx = cmsCreateContext(NULL, s);
35 if (!s->ctx)
36 return AVERROR(ENOMEM);
37
38 cmsSetLogErrorHandlerTHR(s->ctx, log_cb);
39 return 0;
40}
41
43{
44 for (int i = 0; i < FF_ARRAY_ELEMS(s->curves); i++)
45 cmsFreeToneCurve(s->curves[i]);
46 if (s->ctx)
47 cmsDeleteContext(s->ctx);
48 memset(s, 0, sizeof(*s));
49}
50
52 cmsToneCurve **out_curve)
53{
54 if ((unsigned)trc < AVCOL_TRC_NB && s->curves[trc])
55 goto done;
56
57 switch (trc) {
59 s->curves[trc] = cmsBuildGamma(s->ctx, 1.0);
60 break;
62 s->curves[trc] = cmsBuildGamma(s->ctx, 2.2);
63 break;
65 s->curves[trc] = cmsBuildGamma(s->ctx, 2.8);
66 break;
67 case AVCOL_TRC_BT709:
71 s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 4, (double[5]) {
72 /* γ = */ 1/0.45,
73 /* a = */ 1/1.099296826809442,
74 /* b = */ 1 - 1/1.099296826809442,
75 /* c = */ 1/4.5,
76 /* d = */ 4.5 * 0.018053968510807,
77 });
78 break;
80 s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 4, (double[5]) {
81 /* γ = */ 1/0.45,
82 /* a = */ 1/1.1115,
83 /* b = */ 1 - 1/1.1115,
84 /* c = */ 1/4.0,
85 /* d = */ 4.0 * 0.0228,
86 });
87 break;
88 case AVCOL_TRC_LOG:
89 s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 8, (double[5]) {
90 /* a = */ 1.0,
91 /* b = */ 10.0,
92 /* c = */ 2.0,
93 /* d = */ -1.0,
94 /* e = */ 0.0
95 });
96 break;
98 s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 8, (double[5]) {
99 /* a = */ 1.0,
100 /* b = */ 10.0,
101 /* c = */ 2.5,
102 /* d = */ -1.0,
103 /* e = */ 0.0
104 });
105 break;
107 s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 4, (double[5]) {
108 /* γ = */ 2.4,
109 /* a = */ 1/1.055,
110 /* b = */ 1 - 1/1.055,
111 /* c = */ 1/12.92,
112 /* d = */ 12.92 * 0.0031308,
113 });
114 break;
116 s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 2, (double[3]) {
117 /* γ = */ 2.6,
118 /* a = */ pow(52.37/48.0, 1/2.6),
119 /* b = */ 0.0
120 });
121 break;
122
123 /* Can't be represented using the existing parametric tone curves.
124 * FIXME: use cmsBuildTabulatedToneCurveFloat instead */
129 case AVCOL_TRC_V_LOG:
131
132 default:
133 return AVERROR_INVALIDDATA;
134 }
135
136 if (!s->curves[trc])
137 return AVERROR(ENOMEM);
138
139done:
140 *out_curve = s->curves[trc];
141 return 0;
142}
143
145 enum AVColorPrimaries color_prim,
146 enum AVColorTransferCharacteristic color_trc,
147 cmsHPROFILE *out_profile)
148{
149 cmsToneCurve *tonecurve;
150 const AVColorPrimariesDesc *prim;
151 int ret;
152
153 if (!(prim = av_csp_primaries_desc_from_id(color_prim)))
154 return AVERROR_INVALIDDATA;
155 if ((ret = get_curve(s, color_trc, &tonecurve)) < 0)
156 return ret;
157
158 *out_profile = cmsCreateRGBProfileTHR(s->ctx,
159 &(cmsCIExyY) { av_q2d(prim->wp.x), av_q2d(prim->wp.y), 1.0 },
160 &(cmsCIExyYTRIPLE) {
161 .Red = { av_q2d(prim->prim.r.x), av_q2d(prim->prim.r.y), 1.0 },
162 .Green = { av_q2d(prim->prim.g.x), av_q2d(prim->prim.g.y), 1.0 },
163 .Blue = { av_q2d(prim->prim.b.x), av_q2d(prim->prim.b.y), 1.0 },
164 },
165 (cmsToneCurve *[3]) { tonecurve, tonecurve, tonecurve }
166 );
167
168 return *out_profile == NULL ? AVERROR(ENOMEM) : 0;
169}
170
172{
173 cmsUInt32Number size;
174 AVBufferRef *buf;
175
176 if (!cmsSaveProfileToMem(profile, NULL, &size))
177 return AVERROR_EXTERNAL;
178
179 buf = av_buffer_alloc(size);
180 if (!buf)
181 return AVERROR(ENOMEM);
182
183 if (!cmsSaveProfileToMem(profile, buf->data, &size) || size != buf->size) {
184 av_buffer_unref(&buf);
185 return AVERROR_EXTERNAL;
186 }
187
189 av_buffer_unref(&buf);
190 return AVERROR(ENOMEM);
191 }
192
193 return 0;
194}
195
196static av_always_inline void XYZ_xy(cmsCIEXYZ XYZ, AVCIExy *xy)
197{
198 double k = 1.0 / (XYZ.X + XYZ.Y + XYZ.Z);
199 xy->x = av_d2q(k * XYZ.X, 100000);
200 xy->y = av_d2q(k * XYZ.Y, 100000);
201}
202
204{
205 AVRational diff = av_sub_q(r1, r2);
206 /* denominator assumed to be positive */
207 return av_make_q(abs(diff.num), diff.den);
208}
209
210static const AVCIExy wp_d50 = { {3457, 10000}, {3585, 10000} }; /* CIE D50 */
211
213{
214 cmsCIEXYZ *white, fixed;
215 AVCIExy wpxy;
216 AVRational diff, z;
217 if (!profile)
218 return 0;
219
220 if (cmsGetEncodedICCversion(profile) >= 0x4000000) { // ICC v4
221 switch (cmsGetHeaderRenderingIntent(profile)) {
222 case INTENT_RELATIVE_COLORIMETRIC:
223 case INTENT_ABSOLUTE_COLORIMETRIC: ;
224 /* ICC v4 colorimetric profiles are specified to always use D50
225 * media white point, anything else is a violation of the spec.
226 * Sadly, such profiles are incredibly common (Apple...), so make
227 * an effort to fix them. */
228 if (!(white = cmsReadTag(profile, cmsSigMediaWhitePointTag)))
229 return AVERROR_INVALIDDATA;
230 XYZ_xy(*white, &wpxy);
231 diff = av_add_q(abs_sub_q(wpxy.x, wp_d50.x), abs_sub_q(wpxy.y, wp_d50.y));
232 if (av_cmp_q(diff, av_make_q(1, 1000)) > 0) {
233 av_log(s->avctx, AV_LOG_WARNING, "Invalid colorimetric ICCv4 "
234 "profile media white point tag (expected %.4f %.4f, "
235 "got %.4f %.4f)\n",
236 av_q2d(wp_d50.x), av_q2d(wp_d50.y),
237 av_q2d(wpxy.x), av_q2d(wpxy.y));
238 /* x+y+z = 1 */
239 z = av_sub_q(av_sub_q(av_make_q(1, 1), wp_d50.x), wp_d50.y);
240 fixed.X = av_q2d(av_div_q(wp_d50.x, wp_d50.y)) * white->Y;
241 fixed.Y = white->Y;
242 fixed.Z = av_q2d(av_div_q(z, wp_d50.y)) * white->Y;
243 if (!cmsWriteTag(profile, cmsSigMediaWhitePointTag, &fixed))
244 return AVERROR_EXTERNAL;
245 }
246 break;
247 default: break;
248 }
249 }
250
251 return 0;
252}
253
255 AVColorPrimariesDesc *out_primaries)
256{
257 static const uint8_t testprimaries[4][3] = {
258 { 0xFF, 0, 0 }, /* red */
259 { 0, 0xFF, 0 }, /* green */
260 { 0, 0, 0xFF }, /* blue */
261 { 0xFF, 0xFF, 0xFF }, /* white */
262 };
263
264 AVWhitepointCoefficients *wp = &out_primaries->wp;
265 AVPrimaryCoefficients *prim = &out_primaries->prim;
266 cmsFloat64Number prev_adapt;
267 cmsHPROFILE xyz;
268 cmsHTRANSFORM tf;
269 cmsCIEXYZ dst[4];
270
271 xyz = cmsCreateXYZProfileTHR(s->ctx);
272 if (!xyz)
273 return AVERROR(ENOMEM);
274
275 /* We need to use an unadapted observer to get the raw values */
276 prev_adapt = cmsSetAdaptationStateTHR(s->ctx, 0.0);
277 tf = cmsCreateTransformTHR(s->ctx, profile, TYPE_RGB_8, xyz, TYPE_XYZ_DBL,
278 INTENT_ABSOLUTE_COLORIMETRIC,
279 /* Note: These flags mostly don't do anything
280 * anyway, but specify them regardless */
281 cmsFLAGS_NOCACHE |
282 cmsFLAGS_NOOPTIMIZE |
283 cmsFLAGS_LOWRESPRECALC |
284 cmsFLAGS_GRIDPOINTS(2));
285 cmsSetAdaptationStateTHR(s->ctx, prev_adapt);
286 cmsCloseProfile(xyz);
287 if (!tf) {
288 av_log(s->avctx, AV_LOG_ERROR, "Invalid ICC profile (e.g. CMYK)\n");
289 return AVERROR_INVALIDDATA;
290 }
291
292 cmsDoTransform(tf, testprimaries, dst, 4);
293 cmsDeleteTransform(tf);
294 XYZ_xy(dst[0], &prim->r);
295 XYZ_xy(dst[1], &prim->g);
296 XYZ_xy(dst[2], &prim->b);
297 XYZ_xy(dst[3], wp);
298 return 0;
299}
300
302 enum AVColorTransferCharacteristic *out_trc)
303{
304 /* 8-bit linear grayscale ramp */
305 static const uint8_t testramp[16][3] = {
306 { 1, 1, 1}, /* avoid exact zero due to log100 etc. */
307 { 17, 17, 17},
308 { 34, 34, 34},
309 { 51, 51, 51},
310 { 68, 68, 68},
311 { 85, 85, 85},
312 { 02, 02, 02},
313 {119, 119, 119},
314 {136, 136, 136},
315 {153, 153, 153},
316 {170, 170, 170},
317 {187, 187, 187},
318 {204, 204, 204},
319 {221, 221, 221},
320 {238, 238, 238},
321 {255, 255, 255},
322 };
323
324 double dst[FF_ARRAY_ELEMS(testramp)];
325
326 for (enum AVColorTransferCharacteristic trc = 0; trc < AVCOL_TRC_NB; trc++) {
327 cmsToneCurve *tonecurve;
328 cmsHPROFILE ref;
329 cmsHTRANSFORM tf;
330 double delta = 0.0;
331 if (get_curve(s, trc, &tonecurve) < 0)
332 continue;
333
334 ref = cmsCreateGrayProfileTHR(s->ctx, cmsD50_xyY(), tonecurve);
335 if (!ref)
336 return AVERROR(ENOMEM);
337
338 tf = cmsCreateTransformTHR(s->ctx, profile, TYPE_RGB_8, ref, TYPE_GRAY_DBL,
339 INTENT_RELATIVE_COLORIMETRIC,
340 cmsFLAGS_NOCACHE | cmsFLAGS_NOOPTIMIZE);
341 cmsCloseProfile(ref);
342 if (!tf) {
343 av_log(s->avctx, AV_LOG_ERROR, "Invalid ICC profile (e.g. CMYK)\n");
344 return AVERROR_INVALIDDATA;
345 }
346
347 cmsDoTransform(tf, testramp, dst, FF_ARRAY_ELEMS(dst));
348 cmsDeleteTransform(tf);
349
350 for (int i = 0; i < FF_ARRAY_ELEMS(dst); i++)
351 delta += fabs(testramp[i][0] / 255.0 - dst[i]);
352 if (delta < 0.01) {
353 *out_trc = trc;
354 return 0;
355 }
356 }
357
358 *out_trc = AVCOL_TRC_UNSPECIFIED;
359 return 0;
360}
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition dsp.h:87
static AVFormatContext * ctx
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define s(width, name)
Definition cbs_vp9.c:198
#define XYZ(X, Y, Z)
#define NULL
Definition coverity.c:32
Colorspace value utility functions for libavutil.
#define abs(x)
static __device__ float fabs(float a)
static AVFrame * frame
void av_buffer_unref(AVBufferRef **buf)
Free a given reference and automatically free the buffer if there are no more references to it.
Definition buffer.c:139
AVBufferRef * av_buffer_alloc(size_t size)
Allocate an AVBuffer of the given size using av_malloc().
Definition buffer.c:77
#define AVERROR_PATCHWELCOME
Not yet implemented in FFmpeg, patches welcome.
Definition error.h:64
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition error.h:59
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition error.h:61
#define AVERROR(e)
Definition error.h:45
AVFrameSideData * av_frame_new_side_data_from_buf(AVFrame *frame, enum AVFrameSideDataType type, AVBufferRef *buf)
Add a new side data to a frame from an existing AVBufferRef.
Definition frame.c:638
@ AV_FRAME_DATA_ICC_PROFILE
The data contains an ICC profile as an opaque octet buffer following the format described by ISO 1507...
Definition frame.h:144
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition log.h:216
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
AVCIExy AVWhitepointCoefficients
Struct defining white point location in terms of CIE 1931 chromaticity x and y.
Definition csp.h:72
const AVColorPrimariesDesc * av_csp_primaries_desc_from_id(enum AVColorPrimaries prm)
Retrieves a complete gamut description from an enum constant describing the color primaries.
Definition csp.c:95
AVRational av_add_q(AVRational b, AVRational c)
Add two rationals.
Definition rational.c:93
static AVRational av_make_q(int num, int den)
Create an AVRational.
Definition rational.h:71
AVRational av_d2q(double d, int max)
Convert a double precision floating point number to a rational.
Definition rational.c:110
static double av_q2d(AVRational a)
Convert an AVRational to a double.
Definition rational.h:104
static int av_cmp_q(AVRational a, AVRational b)
Compare two rationals.
Definition rational.h:89
AVRational av_sub_q(AVRational b, AVRational c)
Subtract one rational from another.
Definition rational.c:101
AVRational av_div_q(AVRational b, AVRational c)
Divide one rational by another.
Definition rational.c:88
#define fixed(width, name, value)
Definition cbs_apv.c:75
int ff_icc_profile_attach(FFIccContext *s, cmsHPROFILE profile, AVFrame *frame)
Attach an ICC profile to a frame.
Definition fflcms2.c:171
int ff_icc_profile_generate(FFIccContext *s, enum AVColorPrimaries color_prim, enum AVColorTransferCharacteristic color_trc, cmsHPROFILE *out_profile)
Generate an ICC profile for a given combination of color primaries and transfer function.
Definition fflcms2.c:144
void ff_icc_context_uninit(FFIccContext *s)
Definition fflcms2.c:42
static void log_cb(cmsContext ctx, cmsUInt32Number error, const char *str)
Definition fflcms2.c:24
static const AVCIExy wp_d50
Definition fflcms2.c:210
static int get_curve(FFIccContext *s, enum AVColorTransferCharacteristic trc, cmsToneCurve **out_curve)
Definition fflcms2.c:51
static av_always_inline void XYZ_xy(cmsCIEXYZ XYZ, AVCIExy *xy)
Definition fflcms2.c:196
int ff_icc_profile_sanitize(FFIccContext *s, cmsHPROFILE profile)
Sanitize an ICC profile to try and fix badly broken values.
Definition fflcms2.c:212
static av_always_inline AVRational abs_sub_q(AVRational r1, AVRational r2)
Definition fflcms2.c:203
int ff_icc_profile_read_primaries(FFIccContext *s, cmsHPROFILE profile, AVColorPrimariesDesc *out_primaries)
Read the color primaries and white point coefficients encoded by an ICC profile, and return the raw v...
Definition fflcms2.c:254
int ff_icc_context_init(FFIccContext *s, void *avctx)
Initializes an FFIccContext.
Definition fflcms2.c:30
int ff_icc_profile_detect_transfer(FFIccContext *s, cmsHPROFILE profile, enum AVColorTransferCharacteristic *out_trc)
Attempt detecting the transfer characteristic that best approximates the transfer function encoded by...
Definition fflcms2.c:301
Various functions for dealing with ICC profiles.
#define av_always_inline
Definition attributes.h:72
int profile
Definition mxfenc.c:2299
AVColorPrimaries
Chromaticity coordinates of the source primaries.
Definition pixfmt.h:642
AVColorTransferCharacteristic
Color Transfer Characteristic.
Definition pixfmt.h:672
@ AVCOL_TRC_SMPTE170M
also ITU-R BT601-6 525 or 625 / ITU-R BT1358 525 or 625 / ITU-R BT1700 NTSC
Definition pixfmt.h:679
@ AVCOL_TRC_SMPTE2084
SMPTE ST 2084 for 10-, 12-, 14- and 16-bit systems.
Definition pixfmt.h:689
@ AVCOL_TRC_GAMMA22
also ITU-R BT470M / ITU-R BT1700 625 PAL & SECAM
Definition pixfmt.h:677
@ AVCOL_TRC_V_LOG
Definition pixfmt.h:698
@ AVCOL_TRC_BT1361_ECG
ITU-R BT1361 Extended Colour Gamut.
Definition pixfmt.h:685
@ AVCOL_TRC_SMPTE240M
Definition pixfmt.h:680
@ AVCOL_TRC_LOG
"Logarithmic transfer characteristic (100:1 range)"
Definition pixfmt.h:682
@ AVCOL_TRC_IEC61966_2_4
IEC 61966-2-4.
Definition pixfmt.h:684
@ AVCOL_TRC_LINEAR
"Linear transfer characteristics"
Definition pixfmt.h:681
@ AVCOL_TRC_GAMMA28
also ITU-R BT470BG
Definition pixfmt.h:678
@ AVCOL_TRC_ARIB_STD_B67
ARIB STD-B67, known as "Hybrid log-gamma".
Definition pixfmt.h:693
@ AVCOL_TRC_SMPTE428
SMPTE ST 428-1.
Definition pixfmt.h:691
@ AVCOL_TRC_LOG_SQRT
"Logarithmic transfer characteristic (100 * Sqrt(10) : 1 range)"
Definition pixfmt.h:683
@ AVCOL_TRC_BT2020_12
ITU-R BT2020 for 12-bit system.
Definition pixfmt.h:688
@ AVCOL_TRC_IEC61966_2_1
IEC 61966-2-1 (sRGB or sYCC)
Definition pixfmt.h:686
@ AVCOL_TRC_BT2020_10
ITU-R BT2020 for 10-bit system.
Definition pixfmt.h:687
@ AVCOL_TRC_UNSPECIFIED
Definition pixfmt.h:675
@ AVCOL_TRC_BT709
also ITU-R BT1361
Definition pixfmt.h:674
@ AVCOL_TRC_NB
Not part of ABI.
Definition pixfmt.h:694
#define FF_ARRAY_ELEMS(a)
A reference to a data buffer.
Definition buffer.h:82
uint8_t * data
The data buffer.
Definition buffer.h:90
size_t size
Size of data in bytes.
Definition buffer.h:94
Struct containing chromaticity x and y values for the standard CIE 1931 chromaticity definition.
Definition csp.h:56
AVRational x
Definition csp.h:57
AVRational y
Definition csp.h:57
Struct that contains both white point location and primaries location, providing the complete descrip...
Definition csp.h:78
AVWhitepointCoefficients wp
Definition csp.h:79
AVPrimaryCoefficients prim
Definition csp.h:80
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
Struct defining the red, green, and blue primary locations in terms of CIE 1931 chromaticity x and y.
Definition csp.h:64
Rational number (pair of numerator and denominator).
Definition rational.h:58
#define av_log(a,...)
static void error(const char *err)
static int ref[MAX_W *MAX_W]
int size
static av_always_inline int diff(const struct color_info *a, const struct color_info *b, const int trans_thresh)
static const Curve curves[]
float delta