d45699d6f5dc973d58a6f75d12e3b7458f85ad21
[OpenColorIO-Configs.git] / aces_1.0.0 / python / aces_ocio / colorspaces / general.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 """
5 Implements support for general colorspaces conversions and transfer functions.
6 """
7
8 from __future__ import division
9
10 import array
11 import os
12
13 import PyOpenColorIO as ocio
14
15 import aces_ocio.generate_lut as genlut
16 from aces_ocio.colorspaces import aces
17 from aces_ocio.utilities import ColorSpace, mat44_from_mat33
18
19 __author__ = 'ACES Developers'
20 __copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers'
21 __license__ = ''
22 __maintainer__ = 'ACES Developers'
23 __email__ = 'aces@oscars.org'
24 __status__ = 'Production'
25
26 __all__ = ['create_matrix_colorspace',
27            'create_colorspaces']
28
29
30 # -------------------------------------------------------------------------
31 # *Matrix Transform*
32 # -------------------------------------------------------------------------
33 def create_matrix_colorspace(name='matrix',
34                              from_reference_values=None,
35                              to_reference_values=None,
36                              aliases=None):
37     """
38     Object description.
39
40     Parameters
41     ----------
42     parameter : type
43         Parameter description.
44
45     Returns
46     -------
47     type
48          Return value description.
49     """
50
51     if from_reference_values is None:
52         from_reference_values = []
53
54     if to_reference_values is None:
55         to_reference_values = []
56
57     if aliases is None:
58         aliases = []
59
60     cs = ColorSpace(name)
61     cs.description = 'The %s color space' % name
62     cs.aliases = aliases
63     cs.equality_group = name
64     cs.family = 'Utility'
65     cs.is_data = False
66
67     # A linear space needs allocation variables.
68     cs.allocation_type = ocio.Constants.ALLOCATION_UNIFORM
69     cs.allocation_vars = [0, 1]
70
71     cs.to_reference_transforms = []
72     if to_reference_values:
73         for matrix in to_reference_values:
74             cs.to_reference_transforms.append({
75                 'type': 'matrix',
76                 'matrix': mat44_from_mat33(matrix),
77                 'direction': 'forward'})
78
79     cs.from_reference_transforms = []
80     if from_reference_values:
81         for matrix in from_reference_values:
82             cs.from_reference_transforms.append({
83                 'type': 'matrix',
84                 'matrix': mat44_from_mat33(matrix),
85                 'direction': 'forward'})
86
87     return cs
88
89
90 # -------------------------------------------------------------------------
91 # *Transfer Function Transform*
92 # -------------------------------------------------------------------------
93 def create_transfer_colorspace(name='transfer',
94                                transfer_function_name='transfer_function',
95                                transfer_function=lambda x: x,
96                                lut_directory='/tmp',
97                                lut_resolution_1d=1024,
98                                aliases=None):
99     """
100     Object description.
101
102     Parameters
103     ----------
104     parameter : type
105         Parameter description.
106
107     Returns
108     -------
109     type
110          Return value description.
111     """
112
113     if aliases is None:
114         aliases = []
115
116     cs = ColorSpace(name)
117     cs.description = 'The %s color space' % name
118     cs.aliases = aliases
119     cs.equality_group = name
120     cs.family = 'Utility'
121     cs.is_data = False
122
123     # A linear space needs allocation variables.
124     cs.allocation_type = ocio.Constants.ALLOCATION_UNIFORM
125     cs.allocation_vars = [0, 1]
126
127     # Sampling the transfer function.
128     data = array.array('f', '\0' * lut_resolution_1d * 4)
129     for c in range(lut_resolution_1d):
130         data[c] = transfer_function(c / (lut_resolution_1d - 1))
131
132     # Writing the sampled data to a *LUT*.
133     lut = '%s_to_linear.spi1d' % transfer_function_name
134     genlut.write_SPI_1d(
135         os.path.join(lut_directory, lut),
136         0,
137         1,
138         data,
139         lut_resolution_1d,
140         1)
141
142     # Creating the *to_reference* transforms.
143     cs.to_reference_transforms = []
144     cs.to_reference_transforms.append({
145         'type': 'lutFile',
146         'path': lut,
147         'interpolation': 'linear',
148         'direction': 'forward'})
149
150     # Creating the *from_reference* transforms.
151     cs.from_reference_transforms = []
152
153     return cs
154
155
156 # -------------------------------------------------------------------------
157 # *Transfer Function + Matrix Transform*
158 # -------------------------------------------------------------------------
159 def create_matrix_plus_transfer_colorspace(
160         name='matrix_plus_transfer',
161         transfer_function_name='transfer_function',
162         transfer_function=lambda x: x,
163         lut_directory='/tmp',
164         lut_resolution_1d=1024,
165         from_reference_values=None,
166         to_reference_values=None,
167         aliases=None):
168     """
169     Object description.
170
171     Parameters
172     ----------
173     parameter : type
174         Parameter description.
175
176     Returns
177     -------
178     type
179          Return value description.
180     """
181
182     if from_reference_values is None:
183         from_reference_values = []
184
185     if to_reference_values is None:
186         to_reference_values = []
187
188     if aliases is None:
189         aliases = []
190
191     cs = ColorSpace(name)
192     cs.description = 'The %s color space' % name
193     cs.aliases = aliases
194     cs.equality_group = name
195     cs.family = 'Utility'
196     cs.is_data = False
197
198     # A linear space needs allocation variables.
199     cs.allocation_type = ocio.Constants.ALLOCATION_UNIFORM
200     cs.allocation_vars = [0, 1]
201
202     # Sampling the transfer function.
203     data = array.array('f', '\0' * lut_resolution_1d * 4)
204     for c in range(lut_resolution_1d):
205         data[c] = transfer_function(c / (lut_resolution_1d - 1))
206
207     # Writing the sampled data to a *LUT*.
208     lut = '%s_to_linear.spi1d' % transfer_function_name
209     genlut.write_SPI_1d(
210         os.path.join(lut_directory, lut),
211         0,
212         1,
213         data,
214         lut_resolution_1d,
215         1)
216
217     # Creating the *to_reference* transforms.
218     cs.to_reference_transforms = []
219     if to_reference_values:
220         cs.to_reference_transforms.append({
221             'type': 'lutFile',
222             'path': lut,
223             'interpolation': 'linear',
224             'direction': 'forward'})
225
226         for matrix in to_reference_values:
227             cs.to_reference_transforms.append({
228                 'type': 'matrix',
229                 'matrix': mat44_from_mat33(matrix),
230                 'direction': 'forward'})
231
232     # Creating the *from_reference* transforms.
233     cs.from_reference_transforms = []
234     if from_reference_values:
235         for matrix in from_reference_values:
236             cs.from_reference_transforms.append({
237                 'type': 'matrix',
238                 'matrix': mat44_from_mat33(matrix),
239                 'direction': 'forward'})
240
241         cs.from_reference_transforms.append({
242             'type': 'lutFile',
243             'path': lut,
244             'interpolation': 'linear',
245             'direction': 'inverse'})
246
247     return cs
248
249
250 # Transfer functions for standard colorspaces.
251 def transfer_function_sRGB_to_linear(v):
252     a = 1.055
253     b = 0.04045
254     d = 12.92
255     g = 2.4
256
257     if v < b:
258         return v / d
259     return pow(((v + (a - 1)) / a), g)
260
261
262 def transfer_function_Rec709_to_linear(v):
263     a = 1.099
264     b = 0.018
265     d = 4.5
266     g = (1.0 / 0.45)
267
268     if v < b * d:
269         return v / d
270
271     return pow(((v + (a - 1)) / a), g)
272
273
274 def transfer_function_Rec2020_10bit_to_linear(v):
275     a = 1.099
276     b = 0.018
277     d = 4.5
278     g = (1.0 / 0.45)
279
280     if v < b * d:
281         return v / d
282
283     return pow(((v + (a - 1)) / a), g)
284
285
286 def transfer_function_Rec2020_12bit_to_linear(v):
287     a = 1.0993
288     b = 0.0181
289     d = 4.5
290     g = (1.0 / 0.45)
291
292     if v < b * d:
293         return v / d
294
295     return pow(((v + (a - 1)) / a), g)
296
297
298 def transfer_function_Rec1886_to_linear(v):
299     g = 2.4
300     Lw = 1
301     Lb = 0
302
303     # Ignoring legal to full scaling for now.
304     # v = (1023.0*v - 64.0)/876.0
305
306     t = pow(Lw, 1.0 / g) - pow(Lb, 1.0 / g)
307     a = pow(t, g)
308     b = pow(Lb, 1.0 / g) / t
309
310     return a * pow(max((v + b), 0.0), g)
311
312
313 def create_colorspaces(lut_directory,
314                        lut_resolution_1d):
315     """
316     Generates the colorspace conversions.
317
318     Parameters
319     ----------
320     parameter : type
321         Parameter description.
322
323     Returns
324     -------
325     type
326          Return value description.
327     """
328
329     colorspaces = []
330
331     # -------------------------------------------------------------------------
332     # XYZ
333     # -------------------------------------------------------------------------
334     cs = create_matrix_colorspace('XYZ-D60',
335                                   to_reference_values=[aces.ACES_XYZ_TO_AP0],
336                                   from_reference_values=[aces.ACES_AP0_TO_XYZ],
337                                   aliases=['lin_xyz_d60'])
338     colorspaces.append(cs)
339
340     # -------------------------------------------------------------------------
341     # P3-D60
342     # -------------------------------------------------------------------------
343     # *ACES* to *Linear*, *P3D60* primaries
344     XYZ_to_P3D60 = [2.4027414142, -0.8974841639, -0.3880533700,
345                     -0.8325796487, 1.7692317536, 0.0237127115,
346                     0.0388233815, -0.0824996856, 1.0363685997]
347
348     cs = create_matrix_colorspace(
349         'Linear - P3-D60',
350         from_reference_values=[aces.ACES_AP0_TO_XYZ, XYZ_to_P3D60],
351         aliases=['lin_p3d60'])
352     colorspaces.append(cs)
353
354     # -------------------------------------------------------------------------
355     # P3-DCI
356     # -------------------------------------------------------------------------
357     # *ACES* to *Linear*, *P3DCI* primaries
358     XYZ_to_P3DCI = [2.7253940305, -1.0180030062, -0.4401631952,
359                     -0.7951680258, 1.6897320548, 0.0226471906,
360                     0.0412418914, -0.0876390192, 1.1009293786]
361
362     cs = create_matrix_colorspace(
363         'Linear - P3-DCI',
364         from_reference_values=[aces.ACES_AP0_TO_XYZ, XYZ_to_P3DCI],
365         aliases=['lin_p3dci'])
366     colorspaces.append(cs)
367
368     # -------------------------------------------------------------------------
369     # sRGB
370     # -------------------------------------------------------------------------
371     # *ACES* to *Linear*, *Rec. 709* primaries.
372     # *sRGB* and *Rec 709* use the same gamut.
373     XYZ_to_Rec709 = [3.2409699419, -1.5373831776, -0.4986107603,
374                      -0.9692436363, 1.8759675015, 0.0415550574,
375                      0.0556300797, -0.2039769589, 1.0569715142]
376
377     cs = create_matrix_colorspace(
378         'Linear - sRGB',
379         from_reference_values=[aces.ACES_AP0_TO_XYZ, XYZ_to_Rec709],
380         aliases=['lin_srgb'])
381     colorspaces.append(cs)
382
383     # *Linear* to *sRGB* Transfer Function*
384     cs = create_transfer_colorspace(
385         'Curve - sRGB',
386         'sRGB',
387         transfer_function_sRGB_to_linear,
388         lut_directory,
389         lut_resolution_1d,
390         aliases=['crv_srgb'])
391     colorspaces.append(cs)
392
393     # *ACES* to *sRGB* Primaries + Transfer Function*
394     cs = create_matrix_plus_transfer_colorspace(
395         'sRGB',
396         'sRGB',
397         transfer_function_sRGB_to_linear,
398         lut_directory,
399         lut_resolution_1d,
400         from_reference_values=[aces.ACES_AP0_TO_XYZ, XYZ_to_Rec709],
401         aliases=['srgb'])
402     colorspaces.append(cs)
403
404     # -------------------------------------------------------------------------
405     # Rec 709
406     # -------------------------------------------------------------------------
407     # *ACES* to *Linear*, *Rec. 709* primaries
408     XYZ_to_Rec709 = [3.2409699419, -1.5373831776, -0.4986107603,
409                      -0.9692436363, 1.8759675015, 0.0415550574,
410                      0.0556300797, -0.2039769589, 1.0569715142]
411
412     cs = create_matrix_colorspace(
413         'Linear - Rec.709',
414         from_reference_values=[aces.ACES_AP0_TO_XYZ, XYZ_to_Rec709],
415         aliases=['lin_rec709'])
416     colorspaces.append(cs)
417
418     # *Linear* to *Rec. 709* Transfer Function*
419     cs = create_transfer_colorspace(
420         'Curve - Rec.709',
421         'rec709',
422         transfer_function_Rec709_to_linear,
423         lut_directory,
424         lut_resolution_1d,
425         aliases=['crv_rec709'])
426     colorspaces.append(cs)
427
428     # *ACES* to *Rec. 709* Primaries + Transfer Function*
429     cs = create_matrix_plus_transfer_colorspace(
430         'Rec.709 - Camera',
431         'rec709',
432         transfer_function_Rec709_to_linear,
433         lut_directory,
434         lut_resolution_1d,
435         from_reference_values=[aces.ACES_AP0_TO_XYZ, XYZ_to_Rec709],
436         aliases=['rec709_camera'])
437     colorspaces.append(cs)
438
439     # -------------------------------------------------------------------------
440     # Rec 2020
441     # -------------------------------------------------------------------------
442     # *ACES* to *Linear*, *Rec. 2020* primaries
443     XYZ_to_Rec2020 = [1.7166511880, -0.3556707838, -0.2533662814,
444                       -0.6666843518, 1.6164812366, 0.0157685458,
445                       0.0176398574, -0.0427706133, 0.9421031212]
446
447     cs = create_matrix_colorspace(
448         'Linear - Rec.2020',
449         from_reference_values=[aces.ACES_AP0_TO_XYZ, XYZ_to_Rec2020],
450         aliases=['lin_rec2020'])
451     colorspaces.append(cs)
452
453     # *Linear* to *Rec. 2020 10 bit* Transfer Function*
454     cs = create_transfer_colorspace(
455         'Curve - Rec.2020',
456         'rec2020',
457         transfer_function_Rec2020_10bit_to_linear,
458         lut_directory,
459         lut_resolution_1d,
460         aliases=['crv_rec2020'])
461     colorspaces.append(cs)
462
463     # *ACES* to *Rec. 2020 10 bit* Primaries + Transfer Function*
464     cs = create_matrix_plus_transfer_colorspace(
465         'Rec.2020 - Camera',
466         'rec2020',
467         transfer_function_Rec2020_10bit_to_linear,
468         lut_directory,
469         lut_resolution_1d,
470         from_reference_values=[aces.ACES_AP0_TO_XYZ, XYZ_to_Rec2020],
471         aliases=['rec2020_camera'])
472     colorspaces.append(cs)
473
474     # -------------------------------------------------------------------------
475     # Rec 1886
476     # -------------------------------------------------------------------------
477     # *Linear* to *Rec.1886* Transfer Function*
478     cs = create_transfer_colorspace(
479         'Curve - Rec.1886',
480         'rec1886',
481         transfer_function_Rec1886_to_linear,
482         lut_directory,
483         lut_resolution_1d,
484         aliases=['crv_rec1886'])
485     colorspaces.append(cs)
486
487     # *ACES* to *sRGB* Primaries + Transfer Function*
488     cs = create_matrix_plus_transfer_colorspace(
489         'Rec.709 - Display',
490         'rec1886',
491         transfer_function_Rec1886_to_linear,
492         lut_directory,
493         lut_resolution_1d,
494         from_reference_values=[aces.ACES_AP0_TO_XYZ, XYZ_to_Rec709],
495         aliases=['rec709_display'])
496     colorspaces.append(cs)
497
498     # *ACES* to *sRGB* Primaries + Transfer Function*
499     cs = create_matrix_plus_transfer_colorspace(
500         'Rec.2020 - Display',
501         'rec1886',
502         transfer_function_Rec1886_to_linear,
503         lut_directory,
504         lut_resolution_1d,
505         from_reference_values=[aces.ACES_AP0_TO_XYZ, XYZ_to_Rec2020],
506         aliases=['rec2020_display'])
507     colorspaces.append(cs)
508
509     # -------------------------------------------------------------------------
510     # ProPhoto
511     # -------------------------------------------------------------------------
512     # *ACES* to *Linear*, *Pro Photo* primaries
513     AP0_to_RIMM = [1.2412367771, -0.1685692287, -0.0726675484,
514                    0.0061203066, 1.083151174, -0.0892714806,
515                    -0.0032853314, 0.0099796402, 0.9933056912]
516
517     cs = create_matrix_colorspace(
518         'Linear - RIMM ROMM (ProPhoto)',
519         from_reference_values=[AP0_to_RIMM],
520         aliases=['lin_prophoto', 'lin_rimm'])
521     colorspaces.append(cs)
522
523     # -------------------------------------------------------------------------
524     # Adobe RGB
525     # -------------------------------------------------------------------------
526     # *ACES* to *Linear*, *Adobe RGB* primaries
527     AP0_to_ADOBERGB = [1.7245603168, -0.4199935942, -0.3045667227,
528                        -0.2764799142, 1.3727190877, -0.0962391734,
529                        -0.0261255258, -0.0901747807, 1.1163003065]
530
531     cs = create_matrix_colorspace(
532         'Linear - Adobe RGB',
533         from_reference_values=[AP0_to_ADOBERGB],
534         aliases=['lin_adobergb'])
535     colorspaces.append(cs)
536
537     # -------------------------------------------------------------------------
538     # Adobe Wide Gamut RGB
539     # -------------------------------------------------------------------------
540     # *ACES* to *Linear*, *Adobe Wide Gamut RGB* primaries
541     AP0_to_ADOBERGB = [1.3809814778, -0.1158594573, -0.2651220205,
542                        0.0057015535, 1.0402949043, -0.0459964578,
543                        -0.0038908746, -0.0597091815, 1.0636000561]
544
545     cs = create_matrix_colorspace(
546         'Linear - Adobe Wide Gamut RGB',
547         from_reference_values=[AP0_to_ADOBERGB],
548         aliases=['lin_adobewidegamutrgb'])
549     colorspaces.append(cs)
550
551     return colorspaces
552
553
554 def create_raw():
555     # *Raw* utility space
556     name = 'Raw'
557     raw = ColorSpace(name)
558     raw.description = 'The %s color space' % name
559     raw.aliases = ['raw']
560     raw.equality_group = name
561     raw.family = 'Utility'
562     raw.is_data = True
563
564     return raw