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 cmsDeleteContext(s->ctx);
47 memset(s, 0, sizeof(*s));
48}
49
51 cmsToneCurve **out_curve)
52{
53 if ((unsigned)trc < AVCOL_TRC_NB && s->curves[trc])
54 goto done;
55
56 switch (trc) {
58 s->curves[trc] = cmsBuildGamma(s->ctx, 1.0);
59 break;
61 s->curves[trc] = cmsBuildGamma(s->ctx, 2.2);
62 break;
64 s->curves[trc] = cmsBuildGamma(s->ctx, 2.8);
65 break;
66 case AVCOL_TRC_BT709:
70 s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 4, (double[5]) {
71 /* γ = */ 1/0.45,
72 /* a = */ 1/1.099296826809442,
73 /* b = */ 1 - 1/1.099296826809442,
74 /* c = */ 1/4.5,
75 /* d = */ 4.5 * 0.018053968510807,
76 });
77 break;
79 s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 4, (double[5]) {
80 /* γ = */ 1/0.45,
81 /* a = */ 1/1.1115,
82 /* b = */ 1 - 1/1.1115,
83 /* c = */ 1/4.0,
84 /* d = */ 4.0 * 0.0228,
85 });
86 break;
87 case AVCOL_TRC_LOG:
88 s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 8, (double[5]) {
89 /* a = */ 1.0,
90 /* b = */ 10.0,
91 /* c = */ 2.0,
92 /* d = */ -1.0,
93 /* e = */ 0.0
94 });
95 break;
97 s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 8, (double[5]) {
98 /* a = */ 1.0,
99 /* b = */ 10.0,
100 /* c = */ 2.5,
101 /* d = */ -1.0,
102 /* e = */ 0.0
103 });
104 break;
106 s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 4, (double[5]) {
107 /* γ = */ 2.4,
108 /* a = */ 1/1.055,
109 /* b = */ 1 - 1/1.055,
110 /* c = */ 1/12.92,
111 /* d = */ 12.92 * 0.0031308,
112 });
113 break;
115 s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 2, (double[3]) {
116 /* γ = */ 2.6,
117 /* a = */ pow(52.37/48.0, 1/2.6),
118 /* b = */ 0.0
119 });
120 break;
121
122 /* Can't be represented using the existing parametric tone curves.
123 * FIXME: use cmsBuildTabulatedToneCurveFloat instead */
128 case AVCOL_TRC_V_LOG:
130
131 default:
132 return AVERROR_INVALIDDATA;
133 }
134
135 if (!s->curves[trc])
136 return AVERROR(ENOMEM);
137
138done:
139 *out_curve = s->curves[trc];
140 return 0;
141}
142
144 enum AVColorPrimaries color_prim,
145 enum AVColorTransferCharacteristic color_trc,
146 cmsHPROFILE *out_profile)
147{
148 cmsToneCurve *tonecurve;
149 const AVColorPrimariesDesc *prim;
150 int ret;
151
152 if (!(prim = av_csp_primaries_desc_from_id(color_prim)))
153 return AVERROR_INVALIDDATA;
154 if ((ret = get_curve(s, color_trc, &tonecurve)) < 0)
155 return ret;
156
157 *out_profile = cmsCreateRGBProfileTHR(s->ctx,
158 &(cmsCIExyY) { av_q2d(prim->wp.x), av_q2d(prim->wp.y), 1.0 },
159 &(cmsCIExyYTRIPLE) {
160 .Red = { av_q2d(prim->prim.r.x), av_q2d(prim->prim.r.y), 1.0 },
161 .Green = { av_q2d(prim->prim.g.x), av_q2d(prim->prim.g.y), 1.0 },
162 .Blue = { av_q2d(prim->prim.b.x), av_q2d(prim->prim.b.y), 1.0 },
163 },
164 (cmsToneCurve *[3]) { tonecurve, tonecurve, tonecurve }
165 );
166
167 return *out_profile == NULL ? AVERROR(ENOMEM) : 0;
168}
169
171{
172 cmsUInt32Number size;
173 AVBufferRef *buf;
174
175 if (!cmsSaveProfileToMem(profile, NULL, &size))
176 return AVERROR_EXTERNAL;
177
178 buf = av_buffer_alloc(size);
179 if (!buf)
180 return AVERROR(ENOMEM);
181
182 if (!cmsSaveProfileToMem(profile, buf->data, &size) || size != buf->size) {
183 av_buffer_unref(&buf);
184 return AVERROR_EXTERNAL;
185 }
186
188 av_buffer_unref(&buf);
189 return AVERROR(ENOMEM);
190 }
191
192 return 0;
193}
194
195static av_always_inline void XYZ_xy(cmsCIEXYZ XYZ, AVCIExy *xy)
196{
197 double k = 1.0 / (XYZ.X + XYZ.Y + XYZ.Z);
198 xy->x = av_d2q(k * XYZ.X, 100000);
199 xy->y = av_d2q(k * XYZ.Y, 100000);
200}
201
203{
204 AVRational diff = av_sub_q(r1, r2);
205 /* denominator assumed to be positive */
206 return av_make_q(abs(diff.num), diff.den);
207}
208
209static const AVCIExy wp_d50 = { {3457, 10000}, {3585, 10000} }; /* CIE D50 */
210
212{
213 cmsCIEXYZ *white, fixed;
214 AVCIExy wpxy;
215 AVRational diff, z;
216 if (!profile)
217 return 0;
218
219 if (cmsGetEncodedICCversion(profile) >= 0x4000000) { // ICC v4
220 switch (cmsGetHeaderRenderingIntent(profile)) {
221 case INTENT_RELATIVE_COLORIMETRIC:
222 case INTENT_ABSOLUTE_COLORIMETRIC: ;
223 /* ICC v4 colorimetric profiles are specified to always use D50
224 * media white point, anything else is a violation of the spec.
225 * Sadly, such profiles are incredibly common (Apple...), so make
226 * an effort to fix them. */
227 if (!(white = cmsReadTag(profile, cmsSigMediaWhitePointTag)))
228 return AVERROR_INVALIDDATA;
229 XYZ_xy(*white, &wpxy);
230 diff = av_add_q(abs_sub_q(wpxy.x, wp_d50.x), abs_sub_q(wpxy.y, wp_d50.y));
231 if (av_cmp_q(diff, av_make_q(1, 1000)) > 0) {
232 av_log(s->avctx, AV_LOG_WARNING, "Invalid colorimetric ICCv4 "
233 "profile media white point tag (expected %.4f %.4f, "
234 "got %.4f %.4f)\n",
235 av_q2d(wp_d50.x), av_q2d(wp_d50.y),
236 av_q2d(wpxy.x), av_q2d(wpxy.y));
237 /* x+y+z = 1 */
238 z = av_sub_q(av_sub_q(av_make_q(1, 1), wp_d50.x), wp_d50.y);
239 fixed.X = av_q2d(av_div_q(wp_d50.x, wp_d50.y)) * white->Y;
240 fixed.Y = white->Y;
241 fixed.Z = av_q2d(av_div_q(z, wp_d50.y)) * white->Y;
242 if (!cmsWriteTag(profile, cmsSigMediaWhitePointTag, &fixed))
243 return AVERROR_EXTERNAL;
244 }
245 break;
246 default: break;
247 }
248 }
249
250 return 0;
251}
252
254 AVColorPrimariesDesc *out_primaries)
255{
256 static const uint8_t testprimaries[4][3] = {
257 { 0xFF, 0, 0 }, /* red */
258 { 0, 0xFF, 0 }, /* green */
259 { 0, 0, 0xFF }, /* blue */
260 { 0xFF, 0xFF, 0xFF }, /* white */
261 };
262
263 AVWhitepointCoefficients *wp = &out_primaries->wp;
264 AVPrimaryCoefficients *prim = &out_primaries->prim;
265 cmsFloat64Number prev_adapt;
266 cmsHPROFILE xyz;
267 cmsHTRANSFORM tf;
268 cmsCIEXYZ dst[4];
269
270 xyz = cmsCreateXYZProfileTHR(s->ctx);
271 if (!xyz)
272 return AVERROR(ENOMEM);
273
274 /* We need to use an unadapted observer to get the raw values */
275 prev_adapt = cmsSetAdaptationStateTHR(s->ctx, 0.0);
276 tf = cmsCreateTransformTHR(s->ctx, profile, TYPE_RGB_8, xyz, TYPE_XYZ_DBL,
277 INTENT_ABSOLUTE_COLORIMETRIC,
278 /* Note: These flags mostly don't do anything
279 * anyway, but specify them regardless */
280 cmsFLAGS_NOCACHE |
281 cmsFLAGS_NOOPTIMIZE |
282 cmsFLAGS_LOWRESPRECALC |
283 cmsFLAGS_GRIDPOINTS(2));
284 cmsSetAdaptationStateTHR(s->ctx, prev_adapt);
285 cmsCloseProfile(xyz);
286 if (!tf) {
287 av_log(s->avctx, AV_LOG_ERROR, "Invalid ICC profile (e.g. CMYK)\n");
288 return AVERROR_INVALIDDATA;
289 }
290
291 cmsDoTransform(tf, testprimaries, dst, 4);
292 cmsDeleteTransform(tf);
293 XYZ_xy(dst[0], &prim->r);
294 XYZ_xy(dst[1], &prim->g);
295 XYZ_xy(dst[2], &prim->b);
296 XYZ_xy(dst[3], wp);
297 return 0;
298}
299
301 enum AVColorTransferCharacteristic *out_trc)
302{
303 /* 8-bit linear grayscale ramp */
304 static const uint8_t testramp[16][3] = {
305 { 1, 1, 1}, /* avoid exact zero due to log100 etc. */
306 { 17, 17, 17},
307 { 34, 34, 34},
308 { 51, 51, 51},
309 { 68, 68, 68},
310 { 85, 85, 85},
311 { 02, 02, 02},
312 {119, 119, 119},
313 {136, 136, 136},
314 {153, 153, 153},
315 {170, 170, 170},
316 {187, 187, 187},
317 {204, 204, 204},
318 {221, 221, 221},
319 {238, 238, 238},
320 {255, 255, 255},
321 };
322
323 double dst[FF_ARRAY_ELEMS(testramp)];
324
325 for (enum AVColorTransferCharacteristic trc = 0; trc < AVCOL_TRC_NB; trc++) {
326 cmsToneCurve *tonecurve;
327 cmsHPROFILE ref;
328 cmsHTRANSFORM tf;
329 double delta = 0.0;
330 if (get_curve(s, trc, &tonecurve) < 0)
331 continue;
332
333 ref = cmsCreateGrayProfileTHR(s->ctx, cmsD50_xyY(), tonecurve);
334 if (!ref)
335 return AVERROR(ENOMEM);
336
337 tf = cmsCreateTransformTHR(s->ctx, profile, TYPE_RGB_8, ref, TYPE_GRAY_DBL,
338 INTENT_RELATIVE_COLORIMETRIC,
339 cmsFLAGS_NOCACHE | cmsFLAGS_NOOPTIMIZE);
340 cmsCloseProfile(ref);
341 if (!tf) {
342 av_log(s->avctx, AV_LOG_ERROR, "Invalid ICC profile (e.g. CMYK)\n");
343 return AVERROR_INVALIDDATA;
344 }
345
346 cmsDoTransform(tf, testramp, dst, FF_ARRAY_ELEMS(dst));
347 cmsDeleteTransform(tf);
348
349 for (int i = 0; i < FF_ARRAY_ELEMS(dst); i++)
350 delta += fabs(testramp[i][0] / 255.0 - dst[i]);
351 if (delta < 0.01) {
352 *out_trc = trc;
353 return 0;
354 }
355 }
356
357 *out_trc = AVCOL_TRC_UNSPECIFIED;
358 return 0;
359}
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:170
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:143
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:209
static int get_curve(FFIccContext *s, enum AVColorTransferCharacteristic trc, cmsToneCurve **out_curve)
Definition fflcms2.c:50
static av_always_inline void XYZ_xy(cmsCIEXYZ XYZ, AVCIExy *xy)
Definition fflcms2.c:195
int ff_icc_profile_sanitize(FFIccContext *s, cmsHPROFILE profile)
Sanitize an ICC profile to try and fix badly broken values.
Definition fflcms2.c:211
static av_always_inline AVRational abs_sub_q(AVRational r1, AVRational r2)
Definition fflcms2.c:202
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:253
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:300
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