Add module headers doctoring skeletons.
[OpenColorIO-Configs.git] / aces_1.0.0 / python / aces_ocio / create_aces_config.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 """
5 Defines objects creating the *ACES* configuration.
6 """
7
8 import math
9 import numpy
10 import os
11 import shutil
12 import string
13 import sys
14
15 # TODO: This restores the capability of running the script without having
16 # added the package to PYTHONPATH, this is ugly and should ideally replaced by
17 # dedicated executable in a /bin directory.
18 sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
19
20 import PyOpenColorIO as OCIO
21
22 import aces_ocio.createARRIColorSpaces as arri
23 import aces_ocio.createCanonColorSpaces as canon
24 import aces_ocio.createREDColorSpaces as red
25 import aces_ocio.createSonyColorSpaces as sony
26 import aces_ocio.generateLUT as genlut
27 from aces_ocio.process import Process
28 from aces_ocio.util import ColorSpace, mat44FromMat33
29
30 __author__ = 'ACES Developers'
31 __copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers'
32 __license__ = ''
33 __maintainer__ = 'ACES Developers'
34 __email__ = 'aces@oscars.org'
35 __status__ = 'Production'
36
37 __all__ = ['ACES_OCIO_CTL_DIRECTORY_ENVIRON',
38            'ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON',
39            'setConfigDefaultRoles',
40            'writeConfig',
41            'generateOCIOTransform',
42            'createConfig',
43            'generateLUTs',
44            'generateBakedLUTs',
45            'createConfigDir',
46            'getTransformInfo',
47            'getODTInfo',
48            'getLMTInfo',
49            'createACESConfig',
50            'main']
51
52 ACES_OCIO_CTL_DIRECTORY_ENVIRON = 'ACES_OCIO_CTL_DIRECTORY'
53 ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON = 'ACES_OCIO_CONFIGURATION_DIRECTORY'
54
55
56 def setConfigDefaultRoles(config,
57                           color_picking="",
58                           color_timing="",
59                           compositing_log="",
60                           data="",
61                           default="",
62                           matte_paint="",
63                           reference="",
64                           scene_linear="",
65                           texture_paint=""):
66     """
67     Sets given *OCIO* configuration default roles.
68
69     Parameters
70     ----------
71     config : config
72         *OCIO* configuration.
73     color_picking : str or unicode
74         Color picking role title.
75     color_timing : str or unicode
76         Color timing role title.
77     compositing_log : str or unicode
78         Compositing log role title.
79     data : str or unicode
80         Data role title.
81     default : str or unicode
82         Default role title.
83     matte_paint : str or unicode
84         Matte painting role title.
85     reference : str or unicode
86         Reference role title.
87     scene_linear : str or unicode
88         Scene linear role title.
89     texture_paint : str or unicode
90         Texture painting role title.
91
92     Returns
93     -------
94     bool
95          Definition success.
96     """
97
98     if color_picking:
99         config.setRole(OCIO.Constants.ROLE_COLOR_PICKING, color_picking)
100     if color_timing:
101         config.setRole(OCIO.Constants.ROLE_COLOR_TIMING, color_timing)
102     if compositing_log:
103         config.setRole(OCIO.Constants.ROLE_COMPOSITING_LOG, compositing_log)
104     if data:
105         config.setRole(OCIO.Constants.ROLE_DATA, data)
106     if default:
107         config.setRole(OCIO.Constants.ROLE_DEFAULT, default)
108     if matte_paint:
109         config.setRole(OCIO.Constants.ROLE_MATTE_PAINT, matte_paint)
110     if reference:
111         config.setRole(OCIO.Constants.ROLE_REFERENCE, reference)
112     if scene_linear:
113         config.setRole(OCIO.Constants.ROLE_SCENE_LINEAR, scene_linear)
114     if texture_paint:
115         config.setRole(OCIO.Constants.ROLE_TEXTURE_PAINT, texture_paint)
116
117     return True
118
119
120 def writeConfig(config, configPath, sanityCheck=True):
121     """
122     Writes the configuration to given path.
123
124     Parameters
125     ----------
126     parameter : type
127         Parameter description.
128
129     Returns
130     -------
131     type
132          Return value description.
133     """
134
135     if sanityCheck:
136         try:
137             config.sanityCheck()
138         except Exception, e:
139             print e
140             print "Configuration was not written due to a failed Sanity Check"
141             return
142             # sys.exit()
143
144     fileHandle = open(configPath, mode='w')
145     fileHandle.write(config.serialize())
146     fileHandle.close()
147
148
149 def generateOCIOTransform(transforms):
150     """
151     Object description.
152
153     Parameters
154     ----------
155     parameter : type
156         Parameter description.
157
158     Returns
159     -------
160     type
161          Return value description.
162     """
163
164     # print("Generating transforms")
165
166     interpolationOptions = {
167         'linear': OCIO.Constants.INTERP_LINEAR,
168         'nearest': OCIO.Constants.INTERP_NEAREST,
169         'tetrahedral': OCIO.Constants.INTERP_TETRAHEDRAL
170     }
171     directionOptions = {
172         'forward': OCIO.Constants.TRANSFORM_DIR_FORWARD,
173         'inverse': OCIO.Constants.TRANSFORM_DIR_INVERSE
174     }
175
176     ocioTransforms = []
177
178     for transform in transforms:
179         if transform['type'] == 'lutFile':
180             ocioTransform = OCIO.FileTransform(
181                 src=transform['path'],
182                 interpolation=interpolationOptions[transform['interpolation']],
183                 direction=directionOptions[transform['direction']])
184             ocioTransforms.append(ocioTransform)
185         elif transform['type'] == 'matrix':
186             ocioTransform = OCIO.MatrixTransform()
187             # MatrixTransform member variables can't be initialized directly.
188             # Each must be set individually.
189             ocioTransform.setMatrix(transform['matrix'])
190
191             if 'offset' in transform:
192                 ocioTransform.setOffset(transform['offset'])
193
194             if 'direction' in transform:
195                 ocioTransform.setDirection(
196                     directionOptions[transform['direction']])
197
198             ocioTransforms.append(ocioTransform)
199         elif transform['type'] == 'exponent':
200             ocioTransform = OCIO.ExponentTransform()
201             ocioTransform.setValue(transform['value'])
202             ocioTransforms.append(ocioTransform)
203         elif transform['type'] == 'log':
204             ocioTransform = OCIO.LogTransform(
205                 base=transform['base'],
206                 direction=directionOptions[transform['direction']])
207
208             ocioTransforms.append(ocioTransform)
209         else:
210             print("Ignoring unknown transform type : %s" % transform['type'])
211
212     # Build a group transform if necessary
213     if len(ocioTransforms) > 1:
214         transformG = OCIO.GroupTransform()
215         for transform in ocioTransforms:
216             transformG.push_back(transform)
217         transform = transformG
218
219     # Or take the first transform from the list
220     else:
221         transform = ocioTransforms[0]
222
223     return transform
224
225
226 def createConfig(configData, nuke=False):
227     """
228     Object description.
229
230     Parameters
231     ----------
232     parameter : type
233         Parameter description.
234
235     Returns
236     -------
237     type
238          Return value description.
239     """
240
241     # Create the config
242     config = OCIO.Config()
243
244     #
245     # Set config wide values
246     #
247     config.setDescription("An ACES config generated from python")
248     config.setSearchPath("luts")
249
250     #
251     # Define the reference color space
252     #
253     referenceData = configData['referenceColorSpace']
254     print("Adding the reference color space : %s" % referenceData.name)
255
256     # Create a color space
257     reference = OCIO.ColorSpace(
258         name=referenceData.name,
259         bitDepth=referenceData.bitDepth,
260         description=referenceData.description,
261         equalityGroup=referenceData.equalityGroup,
262         family=referenceData.family,
263         isData=referenceData.isData,
264         allocation=referenceData.allocationType,
265         allocationVars=referenceData.allocationVars)
266
267     # Add to config
268     config.addColorSpace(reference)
269
270     #
271     # Create the rest of the color spaces
272     #
273     for colorspace in sorted(configData['colorSpaces']):
274         print("Creating new color space : %s" % colorspace.name)
275
276         ocioColorspace = OCIO.ColorSpace(
277             name=colorspace.name,
278             bitDepth=colorspace.bitDepth,
279             description=colorspace.description,
280             equalityGroup=colorspace.equalityGroup,
281             family=colorspace.family,
282             isData=colorspace.isData,
283             allocation=colorspace.allocationType,
284             allocationVars=colorspace.allocationVars)
285
286         if colorspace.toReferenceTransforms != []:
287             print("Generating To-Reference transforms")
288             ocioTransform = generateOCIOTransform(
289                 colorspace.toReferenceTransforms)
290             ocioColorspace.setTransform(
291                 ocioTransform,
292                 OCIO.Constants.COLORSPACE_DIR_TO_REFERENCE)
293
294         if colorspace.fromReferenceTransforms != []:
295             print("Generating From-Reference transforms")
296             ocioTransform = generateOCIOTransform(
297                 colorspace.fromReferenceTransforms)
298             ocioColorspace.setTransform(
299                 ocioTransform,
300                 OCIO.Constants.COLORSPACE_DIR_FROM_REFERENCE)
301
302         config.addColorSpace(ocioColorspace)
303
304         print("")
305
306     #
307     # Define the views and displays
308     #
309     displays = []
310     views = []
311
312     # Generic display and view setup
313     if not nuke:
314         for display, viewList in configData['displays'].iteritems():
315             for viewName, colorspace in viewList.iteritems():
316                 config.addDisplay(display, viewName, colorspace.name)
317                 if not (viewName in views):
318                     views.append(viewName)
319             displays.append(display)
320     # A Nuke specific set of views and displays
321     #
322     # XXX
323     # A few names: Output Transform, ACES, ACEScc, are hard-coded here.
324     # Would be better to automate.
325     #
326     else:
327         for display, viewList in configData['displays'].iteritems():
328             for viewName, colorspace in viewList.iteritems():
329                 if (viewName == 'Output Transform'):
330                     viewName = 'View'
331                     config.addDisplay(display, viewName, colorspace.name)
332                     if not (viewName in views):
333                         views.append(viewName)
334             displays.append(display)
335
336         config.addDisplay('linear', 'View', 'ACES2065-1')
337         displays.append('linear')
338         config.addDisplay('log', 'View', 'ACEScc')
339         displays.append('log')
340
341     # Set active displays and views
342     config.setActiveDisplays(','.join(sorted(displays)))
343     config.setActiveViews(','.join(views))
344
345     #
346     # Need to generalize this at some point
347     #
348
349     # Add Default Roles
350     setConfigDefaultRoles(config,
351                           color_picking=reference.getName(),
352                           color_timing=reference.getName(),
353                           compositing_log=reference.getName(),
354                           data=reference.getName(),
355                           default=reference.getName(),
356                           matte_paint=reference.getName(),
357                           reference=reference.getName(),
358                           scene_linear=reference.getName(),
359                           texture_paint=reference.getName())
360
361     # Check to make sure we didn't screw something up
362     config.sanityCheck()
363
364     return config
365
366
367 def generateLUTs(odtInfo,
368                  lmtInfo,
369                  shaperName,
370                  acesCTLReleaseDir,
371                  lutDir,
372                  lutResolution1d=4096,
373                  lutResolution3d=64,
374                  cleanup=True):
375     """
376     Object description.
377
378     Parameters
379     ----------
380     parameter : type
381         Parameter description.
382
383     Returns
384     -------
385     dict
386          Colorspaces and transforms converting between those colorspaces and
387          the reference colorspace, *ACES*.
388     """
389
390     print("generateLUTs - begin")
391     configData = {}
392
393     #
394     # Define the reference color space
395     #
396     ACES = ColorSpace('ACES2065-1')
397     ACES.description = (
398         'The Academy Color Encoding System reference color space')
399     ACES.equalityGroup = ''
400     ACES.family = 'ACES'
401     ACES.isData = False
402     ACES.allocationType = OCIO.Constants.ALLOCATION_LG2
403     ACES.allocationVars = [-15, 6]
404
405     configData['referenceColorSpace'] = ACES
406
407     #
408     # Define the displays
409     #
410     configData['displays'] = {}
411
412     #
413     # Define the other color spaces
414     #
415     configData['colorSpaces'] = []
416
417     # Matrix converting ACES AP1 primaries to AP0
418     acesAP1toAP0 = [0.6954522414, 0.1406786965, 0.1638690622,
419                     0.0447945634, 0.8596711185, 0.0955343182,
420                     -0.0055258826, 0.0040252103, 1.0015006723]
421
422     # Matrix converting ACES AP0 primaries to XYZ
423     acesAP0toXYZ = [0.9525523959, 0.0000000000, 0.0000936786,
424                     0.3439664498, 0.7281660966, -0.0721325464,
425                     0.0000000000, 0.0000000000, 1.0088251844]
426
427     #
428     # ACEScc
429     #
430     def createACEScc(name='ACEScc',
431                      minValue=0.0,
432                      maxValue=1.0,
433                      inputScale=1.0):
434         cs = ColorSpace(name)
435         cs.description = "The %s color space" % name
436         cs.equalityGroup = ''
437         cs.family = 'ACES'
438         cs.isData = False
439
440         ctls = [
441             '%s/ACEScc/ACEScsc.ACEScc_to_ACES.a1.0.0.ctl' % acesCTLReleaseDir,
442             # This transform gets back to the AP1 primaries
443             # Useful as the 1d LUT is only covering the transfer function
444             # The primaries switch is covered by the matrix below
445             '%s/ACEScg/ACEScsc.ACES_to_ACEScg.a1.0.0.ctl' % acesCTLReleaseDir
446         ]
447         lut = "%s_to_ACES.spi1d" % name
448
449         # Remove spaces and parentheses
450         lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_')
451
452         genlut.generate1dLUTFromCTL(
453             lutDir + "/" + lut,
454             ctls,
455             lutResolution1d,
456             'float',
457             inputScale,
458             1.0,
459             {},
460             cleanup,
461             acesCTLReleaseDir,
462             minValue,
463             maxValue)
464
465         cs.toReferenceTransforms = []
466         cs.toReferenceTransforms.append({
467             'type': 'lutFile',
468             'path': lut,
469             'interpolation': 'linear',
470             'direction': 'forward'
471         })
472
473         # AP1 primaries to AP0 primaries
474         cs.toReferenceTransforms.append({
475             'type': 'matrix',
476             'matrix': mat44FromMat33(acesAP1toAP0),
477             'direction': 'forward'
478         })
479
480         cs.fromReferenceTransforms = []
481         return cs
482
483     ACEScc = createACEScc()
484     configData['colorSpaces'].append(ACEScc)
485
486     #
487     # ACESproxy
488     #
489     def createACESProxy(name='ACESproxy'):
490         cs = ColorSpace(name)
491         cs.description = "The %s color space" % name
492         cs.equalityGroup = ''
493         cs.family = 'ACES'
494         cs.isData = False
495
496         ctls = [
497             '%s/ACESproxy/ACEScsc.ACESproxy10i_to_ACES.a1.0.0.ctl' % (
498                 acesCTLReleaseDir),
499             # This transform gets back to the AP1 primaries
500             # Useful as the 1d LUT is only covering the transfer function
501             # The primaries switch is covered by the matrix below
502             '%s/ACEScg/ACEScsc.ACES_to_ACEScg.a1.0.0.ctl' % acesCTLReleaseDir
503         ]
504         lut = "%s_to_aces.spi1d" % name
505
506         # Remove spaces and parentheses
507         lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_')
508
509         genlut.generate1dLUTFromCTL(
510             lutDir + "/" + lut,
511             ctls,
512             lutResolution1d,
513             'uint16',
514             64.0,
515             1.0,
516             {},
517             cleanup,
518             acesCTLReleaseDir)
519
520         cs.toReferenceTransforms = []
521         cs.toReferenceTransforms.append({
522             'type': 'lutFile',
523             'path': lut,
524             'interpolation': 'linear',
525             'direction': 'forward'
526         })
527
528         # AP1 primaries to AP0 primaries
529         cs.toReferenceTransforms.append({
530             'type': 'matrix',
531             'matrix': mat44FromMat33(acesAP1toAP0),
532             'direction': 'forward'
533         })
534
535         cs.fromReferenceTransforms = []
536         return cs
537
538     ACESproxy = createACESProxy()
539     configData['colorSpaces'].append(ACESproxy)
540
541     #
542     # ACEScg
543     #
544     def createACEScg(name='ACEScg'):
545         cs = ColorSpace(name)
546         cs.description = "The %s color space" % name
547         cs.equalityGroup = ''
548         cs.family = 'ACES'
549         cs.isData = False
550
551         cs.toReferenceTransforms = []
552
553         # AP1 primaries to AP0 primaries
554         cs.toReferenceTransforms.append({
555             'type': 'matrix',
556             'matrix': mat44FromMat33(acesAP1toAP0),
557             'direction': 'forward'
558         })
559
560         cs.fromReferenceTransforms = []
561         return cs
562
563     ACEScg = createACEScg()
564     configData['colorSpaces'].append(ACEScg)
565
566     #
567     # ADX
568     #
569     def createADX(bitdepth=10, name='ADX'):
570         name = "%s%s" % (name, bitdepth)
571         cs = ColorSpace(name)
572         cs.description = "%s color space - used for film scans" % name
573         cs.equalityGroup = ''
574         cs.family = 'ADX'
575         cs.isData = False
576
577         if bitdepth == 10:
578             cs.bitDepth = bitDepth = OCIO.Constants.BIT_DEPTH_UINT10
579             adx_to_cdd = [1023.0 / 500.0, 0.0, 0.0, 0.0,
580                           0.0, 1023.0 / 500.0, 0.0, 0.0,
581                           0.0, 0.0, 1023.0 / 500.0, 0.0,
582                           0.0, 0.0, 0.0, 1.0]
583             offset = [-95.0 / 500.0, -95.0 / 500.0, -95.0 / 500.0, 0.0]
584         elif bitdepth == 16:
585             cs.bitDepth = bitDepth = OCIO.Constants.BIT_DEPTH_UINT16
586             adx_to_cdd = [65535.0 / 8000.0, 0.0, 0.0, 0.0,
587                           0.0, 65535.0 / 8000.0, 0.0, 0.0,
588                           0.0, 0.0, 65535.0 / 8000.0, 0.0,
589                           0.0, 0.0, 0.0, 1.0]
590             offset = [-1520.0 / 8000.0, -1520.0 / 8000.0, -1520.0 / 8000.0,
591                       0.0]
592
593         cs.toReferenceTransforms = []
594
595         # Convert from ADX to Channel-Dependent Density
596         cs.toReferenceTransforms.append({
597             'type': 'matrix',
598             'matrix': adx_to_cdd,
599             'offset': offset,
600             'direction': 'forward'
601         })
602
603         # Convert from Channel-Dependent Density to Channel-Independent Density
604         cs.toReferenceTransforms.append({
605             'type': 'matrix',
606             'matrix': [0.75573, 0.22197, 0.02230, 0,
607                        0.05901, 0.96928, -0.02829, 0,
608                        0.16134, 0.07406, 0.76460, 0,
609                        0.0, 0.0, 0.0, 1.0],
610             'direction': 'forward'
611         })
612
613         # Copied from Alex Fry's adx_cid_to_rle.py
614         def createCIDtoRLELUT():
615             def interpolate1D(x, xp, fp):
616                 return numpy.interp(x, xp, fp)
617
618             LUT_1D_xp = [-0.190000000000000,
619                          0.010000000000000,
620                          0.028000000000000,
621                          0.054000000000000,
622                          0.095000000000000,
623                          0.145000000000000,
624                          0.220000000000000,
625                          0.300000000000000,
626                          0.400000000000000,
627                          0.500000000000000,
628                          0.600000000000000]
629
630             LUT_1D_fp = [-6.000000000000000,
631                          -2.721718645000000,
632                          -2.521718645000000,
633                          -2.321718645000000,
634                          -2.121718645000000,
635                          -1.921718645000000,
636                          -1.721718645000000,
637                          -1.521718645000000,
638                          -1.321718645000000,
639                          -1.121718645000000,
640                          -0.926545676714876]
641
642             REF_PT = ((7120.0 - 1520.0) / 8000.0 * (100.0 / 55.0) -
643                       math.log(0.18, 10.0))
644
645             def cid_to_rle(x):
646                 if x <= 0.6:
647                     return interpolate1D(x, LUT_1D_xp, LUT_1D_fp)
648                 return (100.0 / 55.0) * x - REF_PT
649
650             def Fit(value, fromMin, fromMax, toMin, toMax):
651                 if fromMin == fromMax:
652                     raise ValueError("fromMin == fromMax")
653                 return (value - fromMin) / (fromMax - fromMin) * (
654                     toMax - toMin) + toMin
655
656             NUM_SAMPLES = 2 ** 12
657             RANGE = (-0.19, 3.0)
658             data = []
659             for i in xrange(NUM_SAMPLES):
660                 x = i / (NUM_SAMPLES - 1.0)
661                 x = Fit(x, 0.0, 1.0, RANGE[0], RANGE[1])
662                 data.append(cid_to_rle(x))
663
664             lut = 'ADX_CID_to_RLE.spi1d'
665             genlut.writeSPI1D(lutDir + "/" + lut, RANGE[0], RANGE[1], data,
666                               NUM_SAMPLES, 1)
667
668             return lut
669
670         # Convert Channel Independent Density values to Relative Log Exposure
671         # values.
672         lut = createCIDtoRLELUT()
673         cs.toReferenceTransforms.append({
674             'type': 'lutFile',
675             'path': lut,
676             'interpolation': 'linear',
677             'direction': 'forward'
678         })
679
680         # Convert Relative Log Exposure values to Relative Exposure values
681         cs.toReferenceTransforms.append({
682             'type': 'log',
683             'base': 10,
684             'direction': 'inverse'
685         })
686
687         # Convert Relative Exposure values to ACES values
688         cs.toReferenceTransforms.append({
689             'type': 'matrix',
690             'matrix': [0.72286, 0.12630, 0.15084, 0,
691                        0.11923, 0.76418, 0.11659, 0,
692                        0.01427, 0.08213, 0.90359, 0,
693                        0.0, 0.0, 0.0, 1.0],
694             'direction': 'forward'
695         })
696
697         cs.fromReferenceTransforms = []
698         return cs
699
700     ADX10 = createADX(bitdepth=10)
701     configData['colorSpaces'].append(ADX10)
702
703     ADX16 = createADX(bitdepth=16)
704     configData['colorSpaces'].append(ADX16)
705
706     #
707     # Camera Input Transforms
708     #
709
710     # RED color spaces to ACES
711     redColorSpaces = red.createColorSpaces(lutDir, lutResolution1d)
712     for cs in redColorSpaces:
713         configData['colorSpaces'].append(cs)
714
715     # Canon-Log to ACES
716     canonColorSpaces = canon.createColorSpaces(lutDir, lutResolution1d)
717     for cs in canonColorSpaces:
718         configData['colorSpaces'].append(cs)
719
720     # SLog to ACES
721     sonyColorSpaces = sony.createColorSpaces(lutDir, lutResolution1d)
722     for cs in sonyColorSpaces:
723         configData['colorSpaces'].append(cs)
724
725     # LogC to ACES
726     arriColorSpaces = arri.createColorSpaces(lutDir, lutResolution1d)
727     for cs in arriColorSpaces:
728         configData['colorSpaces'].append(cs)
729
730     #
731     # Generic log transform
732     #
733     def createGenericLog(name='log',
734                          minValue=0.0,
735                          maxValue=1.0,
736                          inputScale=1.0,
737                          middleGrey=0.18,
738                          minExposure=-6.0,
739                          maxExposure=6.5,
740                          lutResolution1d=lutResolution1d):
741         cs = ColorSpace(name)
742         cs.description = "The %s color space" % name
743         cs.equalityGroup = name
744         cs.family = 'Utility'
745         cs.isData = False
746
747         ctls = [
748             '%s/utilities/ACESlib.OCIO_shaper_log2_to_lin_param.a1.0.0.ctl' % (
749                 acesCTLReleaseDir)
750         ]
751         lut = "%s_to_aces.spi1d" % name
752
753         # Remove spaces and parentheses
754         lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_')
755
756         genlut.generate1dLUTFromCTL(
757             lutDir + "/" + lut,
758             ctls,
759             lutResolution1d,
760             'float',
761             inputScale,
762             1.0,
763             {
764                 'middleGrey': middleGrey,
765                 'minExposure': minExposure,
766                 'maxExposure': maxExposure
767             },
768             cleanup,
769             acesCTLReleaseDir,
770             minValue,
771             maxValue)
772
773         cs.toReferenceTransforms = []
774         cs.toReferenceTransforms.append({
775             'type': 'lutFile',
776             'path': lut,
777             'interpolation': 'linear',
778             'direction': 'forward'
779         })
780
781         cs.fromReferenceTransforms = []
782         return cs
783
784     #
785     # ACES LMTs
786     #
787     def createACESLMT(lmtName,
788                       lmtValues,
789                       shaperInfo,
790                       lutResolution1d=1024,
791                       lutResolution3d=64,
792                       cleanup=True):
793         cs = ColorSpace("%s" % lmtName)
794         cs.description = "The ACES Look Transform: %s" % lmtName
795         cs.equalityGroup = ''
796         cs.family = 'Look'
797         cs.isData = False
798
799         import pprint
800
801         pprint.pprint(lmtValues)
802
803         #
804         # Generate the shaper transform
805         #
806         (shaperName,
807          shaperToACESCTL,
808          shaperFromACESCTL,
809          shaperInputScale,
810          shaperParams) = shaperInfo
811
812         shaperLut = "%s_to_aces.spi1d" % shaperName
813         if (not os.path.exists(lutDir + "/" + shaperLut)):
814             ctls = [
815                 shaperToACESCTL % acesCTLReleaseDir
816             ]
817
818             # Remove spaces and parentheses
819             shaperLut = shaperLut.replace(
820                 ' ', '_').replace(')', '_').replace('(', '_')
821
822             genlut.generate1dLUTFromCTL(
823                 lutDir + "/" + shaperLut,
824                 ctls,
825                 lutResolution1d,
826                 'float',
827                 1.0 / shaperInputScale,
828                 1.0,
829                 shaperParams,
830                 cleanup,
831                 acesCTLReleaseDir)
832
833         shaperOCIOTransform = {
834             'type': 'lutFile',
835             'path': shaperLut,
836             'interpolation': 'linear',
837             'direction': 'inverse'
838         }
839
840         #
841         # Generate the forward transform
842         #
843         cs.fromReferenceTransforms = []
844
845         if 'transformCTL' in lmtValues:
846             ctls = [
847                 shaperToACESCTL % acesCTLReleaseDir,
848                 '%s/%s' % (acesCTLReleaseDir, lmtValues['transformCTL'])
849             ]
850             lut = "%s.%s.spi3d" % (shaperName, lmtName)
851
852             # Remove spaces and parentheses
853             lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_')
854
855             genlut.generate3dLUTFromCTL(
856                 lutDir + "/" + lut,
857                 ctls,
858                 lutResolution3d,
859                 'float',
860                 1.0 / shaperInputScale,
861                 1.0,
862                 shaperParams,
863                 cleanup,
864                 acesCTLReleaseDir)
865
866             cs.fromReferenceTransforms.append(shaperOCIOTransform)
867             cs.fromReferenceTransforms.append({
868                 'type': 'lutFile',
869                 'path': lut,
870                 'interpolation': 'tetrahedral',
871                 'direction': 'forward'
872             })
873
874         #
875         # Generate the inverse transform
876         #
877         cs.toReferenceTransforms = []
878
879         if 'transformCTLInverse' in lmtValues:
880             ctls = [
881                 '%s/%s' % (
882                     acesCTLReleaseDir, odtValues['transformCTLInverse']),
883                 shaperFromACESCTL % acesCTLReleaseDir
884             ]
885             lut = "Inverse.%s.%s.spi3d" % (odtName, shaperName)
886
887             # Remove spaces and parentheses
888             lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_')
889
890             genlut.generate3dLUTFromCTL(
891                 lutDir + "/" + lut,
892                 ctls,
893                 lutResolution3d,
894                 'half',
895                 1.0,
896                 shaperInputScale,
897                 shaperParams,
898                 cleanup,
899                 acesCTLReleaseDir)
900
901             cs.toReferenceTransforms.append({
902                 'type': 'lutFile',
903                 'path': lut,
904                 'interpolation': 'tetrahedral',
905                 'direction': 'forward'
906             })
907
908             shaperInverse = shaperOCIOTransform.copy()
909             shaperInverse['direction'] = 'forward'
910             cs.toReferenceTransforms.append(shaperInverse)
911
912         return cs
913
914     #
915     # LMT Shaper
916     #
917
918     lmtLutResolution1d = max(4096, lutResolution1d)
919     lmtLutResolution3d = max(65, lutResolution3d)
920
921     # Log 2 shaper
922     lmtShaperName = 'LMT Shaper'
923     lmtParams = {
924         'middleGrey': 0.18,
925         'minExposure': -10.0,
926         'maxExposure': 6.5
927     }
928     lmtShaper = createGenericLog(name=lmtShaperName,
929                                  middleGrey=lmtParams['middleGrey'],
930                                  minExposure=lmtParams['minExposure'],
931                                  maxExposure=lmtParams['maxExposure'],
932                                  lutResolution1d=lmtLutResolution1d)
933     configData['colorSpaces'].append(lmtShaper)
934
935     shaperInputScale_genericLog2 = 1.0
936
937     # Log 2 shaper name and CTL transforms bundled up
938     lmtShaperData = [
939         lmtShaperName,
940         '%s/utilities/ACESlib.OCIO_shaper_log2_to_lin_param.a1.0.0.ctl',
941         '%s/utilities/ACESlib.OCIO_shaper_lin_to_log2_param.a1.0.0.ctl',
942         shaperInputScale_genericLog2,
943         lmtParams
944     ]
945
946     sortedLMTs = sorted(lmtInfo.iteritems(), key=lambda x: x[1])
947     print(sortedLMTs)
948     for lmt in sortedLMTs:
949         (lmtName, lmtValues) = lmt
950         cs = createACESLMT(
951             lmtValues['transformUserName'],
952             lmtValues,
953             lmtShaperData,
954             lmtLutResolution1d,
955             lmtLutResolution3d,
956             cleanup)
957         configData['colorSpaces'].append(cs)
958
959     #
960     # ACES RRT with the supplied ODT
961     #
962     def createACESRRTplusODT(odtName,
963                              odtValues,
964                              shaperInfo,
965                              lutResolution1d=1024,
966                              lutResolution3d=64,
967                              cleanup=True):
968         cs = ColorSpace("%s" % odtName)
969         cs.description = "%s - %s Output Transform" % (
970             odtValues['transformUserNamePrefix'], odtName)
971         cs.equalityGroup = ''
972         cs.family = 'Output'
973         cs.isData = False
974
975         import pprint
976
977         pprint.pprint(odtValues)
978
979         #
980         # Generate the shaper transform
981         #
982         # if 'shaperCTL' in odtValues:
983         (shaperName,
984          shaperToACESCTL,
985          shaperFromACESCTL,
986          shaperInputScale,
987          shaperParams) = shaperInfo
988
989         if 'legalRange' in odtValues:
990             shaperParams['legalRange'] = odtValues['legalRange']
991         else:
992             shaperParams['legalRange'] = 0
993
994         shaperLut = "%s_to_aces.spi1d" % shaperName
995         if (not os.path.exists(lutDir + "/" + shaperLut)):
996             ctls = [
997                 shaperToACESCTL % acesCTLReleaseDir
998             ]
999
1000             # Remove spaces and parentheses
1001             shaperLut = shaperLut.replace(
1002                 ' ', '_').replace(')', '_').replace('(', '_')
1003
1004             genlut.generate1dLUTFromCTL(
1005                 lutDir + "/" + shaperLut,
1006                 ctls,
1007                 lutResolution1d,
1008                 'float',
1009                 1.0 / shaperInputScale,
1010                 1.0,
1011                 shaperParams,
1012                 cleanup,
1013                 acesCTLReleaseDir)
1014
1015         shaperOCIOTransform = {
1016             'type': 'lutFile',
1017             'path': shaperLut,
1018             'interpolation': 'linear',
1019             'direction': 'inverse'
1020         }
1021
1022         #
1023         # Generate the forward transform
1024         #
1025         cs.fromReferenceTransforms = []
1026
1027         if 'transformLUT' in odtValues:
1028             # Copy into the lut dir
1029             transformLUTFileName = os.path.basename(odtValues['transformLUT'])
1030             lut = lutDir + "/" + transformLUTFileName
1031             shutil.copy(odtValues['transformLUT'], lut)
1032
1033             cs.fromReferenceTransforms.append(shaperOCIOTransform)
1034             cs.fromReferenceTransforms.append({
1035                 'type': 'lutFile',
1036                 'path': transformLUTFileName,
1037                 'interpolation': 'tetrahedral',
1038                 'direction': 'forward'
1039             })
1040         elif 'transformCTL' in odtValues:
1041             # shaperLut
1042
1043             ctls = [
1044                 shaperToACESCTL % acesCTLReleaseDir,
1045                 '%s/rrt/RRT.a1.0.0.ctl' % acesCTLReleaseDir,
1046                 '%s/odt/%s' % (acesCTLReleaseDir, odtValues['transformCTL'])
1047             ]
1048             lut = "%s.RRT.a1.0.0.%s.spi3d" % (shaperName, odtName)
1049
1050             # Remove spaces and parentheses
1051             lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_')
1052
1053             genlut.generate3dLUTFromCTL(lutDir + "/" + lut,
1054                                         # shaperLUT,
1055                                         ctls,
1056                                         lutResolution3d,
1057                                         'float',
1058                                         1.0 / shaperInputScale,
1059                                         1.0,
1060                                         shaperParams,
1061                                         cleanup,
1062                                         acesCTLReleaseDir)
1063
1064             cs.fromReferenceTransforms.append(shaperOCIOTransform)
1065             cs.fromReferenceTransforms.append({
1066                 'type': 'lutFile',
1067                 'path': lut,
1068                 'interpolation': 'tetrahedral',
1069                 'direction': 'forward'
1070             })
1071
1072         #
1073         # Generate the inverse transform
1074         #
1075         cs.toReferenceTransforms = []
1076
1077         if 'transformLUTInverse' in odtValues:
1078             # Copy into the lut dir
1079             transformLUTInverseFileName = os.path.basename(
1080                 odtValues['transformLUTInverse'])
1081             lut = lutDir + "/" + transformLUTInverseFileName
1082             shutil.copy(odtValues['transformLUTInverse'], lut)
1083
1084             cs.toReferenceTransforms.append({
1085                 'type': 'lutFile',
1086                 'path': transformLUTInverseFileName,
1087                 'interpolation': 'tetrahedral',
1088                 'direction': 'forward'
1089             })
1090
1091             shaperInverse = shaperOCIOTransform.copy()
1092             shaperInverse['direction'] = 'forward'
1093             cs.toReferenceTransforms.append(shaperInverse)
1094         elif 'transformCTLInverse' in odtValues:
1095             ctls = [
1096                 '%s/odt/%s' % (
1097                     acesCTLReleaseDir, odtValues['transformCTLInverse']),
1098                 '%s/rrt/InvRRT.a1.0.0.ctl' % acesCTLReleaseDir,
1099                 shaperFromACESCTL % acesCTLReleaseDir
1100             ]
1101             lut = "InvRRT.a1.0.0.%s.%s.spi3d" % (odtName, shaperName)
1102
1103             # Remove spaces and parentheses
1104             lut = lut.replace(' ', '_').replace(')', '_').replace('(', '_')
1105
1106             genlut.generate3dLUTFromCTL(
1107                 lutDir + "/" + lut,
1108                 # None,
1109                 ctls,
1110                 lutResolution3d,
1111                 'half',
1112                 1.0,
1113                 shaperInputScale,
1114                 shaperParams,
1115                 cleanup,
1116                 acesCTLReleaseDir)
1117
1118             cs.toReferenceTransforms.append({
1119                 'type': 'lutFile',
1120                 'path': lut,
1121                 'interpolation': 'tetrahedral',
1122                 'direction': 'forward'
1123             })
1124
1125             shaperInverse = shaperOCIOTransform.copy()
1126             shaperInverse['direction'] = 'forward'
1127             cs.toReferenceTransforms.append(shaperInverse)
1128
1129         return cs
1130
1131     #
1132     # RRT/ODT shaper options
1133     #
1134     shaperData = {}
1135
1136     # Log 2 shaper
1137     log2ShaperName = shaperName
1138     log2Params = {
1139         'middleGrey': 0.18,
1140         'minExposure': -6.0,
1141         'maxExposure': 6.5
1142     }
1143     log2Shaper = createGenericLog(name=log2ShaperName,
1144                                   middleGrey=log2Params['middleGrey'],
1145                                   minExposure=log2Params['minExposure'],
1146                                   maxExposure=log2Params['maxExposure'])
1147     configData['colorSpaces'].append(log2Shaper)
1148
1149     shaperInputScale_genericLog2 = 1.0
1150
1151     # Log 2 shaper name and CTL transforms bundled up
1152     log2ShaperData = [
1153         log2ShaperName,
1154         '%s/utilities/ACESlib.OCIO_shaper_log2_to_lin_param.a1.0.0.ctl',
1155         '%s/utilities/ACESlib.OCIO_shaper_lin_to_log2_param.a1.0.0.ctl',
1156         shaperInputScale_genericLog2,
1157         log2Params
1158     ]
1159
1160     shaperData[log2ShaperName] = log2ShaperData
1161
1162     #
1163     # Shaper that also includes the AP1 primaries
1164     # - Needed for some LUT baking steps
1165     #
1166     log2ShaperAP1 = createGenericLog(name=log2ShaperName,
1167                                      middleGrey=log2Params['middleGrey'],
1168                                      minExposure=log2Params['minExposure'],
1169                                      maxExposure=log2Params['maxExposure'])
1170     log2ShaperAP1.name = "%s - AP1" % log2ShaperAP1.name
1171     # AP1 primaries to AP0 primaries
1172     log2ShaperAP1.toReferenceTransforms.append({
1173         'type': 'matrix',
1174         'matrix': mat44FromMat33(acesAP1toAP0),
1175         'direction': 'forward'
1176     })
1177     configData['colorSpaces'].append(log2ShaperAP1)
1178
1179     #
1180     # Choose your shaper
1181     #
1182     rrtShaperName = log2ShaperName
1183     rrtShaper = log2ShaperData
1184
1185     #
1186     # RRT + ODT Combinations
1187     #
1188     sortedOdts = sorted(odtInfo.iteritems(), key=lambda x: x[1])
1189     print(sortedOdts)
1190     for odt in sortedOdts:
1191         (odtName, odtValues) = odt
1192
1193         # Have to handle ODTs that can generate either legal or full output
1194         if odtName in ['Academy.Rec2020_100nits_dim.a1.0.0',
1195                        'Academy.Rec709_100nits_dim.a1.0.0',
1196                        'Academy.Rec709_D60sim_100nits_dim.a1.0.0']:
1197             odtNameLegal = '%s - Legal' % odtValues['transformUserName']
1198         else:
1199             odtNameLegal = odtValues['transformUserName']
1200
1201         odtLegal = odtValues.copy()
1202         odtLegal['legalRange'] = 1
1203
1204         cs = createACESRRTplusODT(
1205             odtNameLegal,
1206             odtLegal,
1207             rrtShaper,
1208             lutResolution1d,
1209             lutResolution3d,
1210             cleanup)
1211         configData['colorSpaces'].append(cs)
1212
1213         # Create a display entry using this color space
1214         configData['displays'][odtNameLegal] = {
1215             'Linear': ACES,
1216             'Log': ACEScc,
1217             'Output Transform': cs}
1218
1219         if odtName in ['Academy.Rec2020_100nits_dim.a1.0.0',
1220                        'Academy.Rec709_100nits_dim.a1.0.0',
1221                        'Academy.Rec709_D60sim_100nits_dim.a1.0.0']:
1222             print("Generating full range ODT for %s" % odtName)
1223
1224             odtNameFull = "%s - Full" % odtValues['transformUserName']
1225             odtFull = odtValues.copy()
1226             odtFull['legalRange'] = 0
1227
1228             csFull = createACESRRTplusODT(
1229                 odtNameFull,
1230                 odtFull,
1231                 rrtShaper,
1232                 lutResolution1d,
1233                 lutResolution3d,
1234                 cleanup)
1235             configData['colorSpaces'].append(csFull)
1236
1237             # Create a display entry using this color space
1238             configData['displays'][odtNameFull] = {
1239                 'Linear': ACES,
1240                 'Log': ACEScc,
1241                 'Output Transform': csFull}
1242
1243     #
1244     # Generic Matrix transform
1245     #
1246     def createGenericMatrix(name='matrix',
1247                             fromReferenceValues=[],
1248                             toReferenceValues=[]):
1249         cs = ColorSpace(name)
1250         cs.description = "The %s color space" % name
1251         cs.equalityGroup = name
1252         cs.family = 'Utility'
1253         cs.isData = False
1254
1255         cs.toReferenceTransforms = []
1256         if toReferenceValues != []:
1257             for matrix in toReferenceValues:
1258                 cs.toReferenceTransforms.append({
1259                     'type': 'matrix',
1260                     'matrix': mat44FromMat33(matrix),
1261                     'direction': 'forward'
1262                 })
1263
1264         cs.fromReferenceTransforms = []
1265         if fromReferenceValues != []:
1266             for matrix in fromReferenceValues:
1267                 cs.fromReferenceTransforms.append({
1268                     'type': 'matrix',
1269                     'matrix': mat44FromMat33(matrix),
1270                     'direction': 'forward'
1271                 })
1272
1273         return cs
1274
1275     cs = createGenericMatrix('XYZ', fromReferenceValues=[acesAP0toXYZ])
1276     configData['colorSpaces'].append(cs)
1277
1278     cs = createGenericMatrix('Linear - AP1', toReferenceValues=[acesAP1toAP0])
1279     configData['colorSpaces'].append(cs)
1280
1281     # ACES to Linear, P3D60 primaries
1282     xyzToP3D60 = [2.4027414142, -0.8974841639, -0.3880533700,
1283                   -0.8325796487, 1.7692317536, 0.0237127115,
1284                   0.0388233815, -0.0824996856, 1.0363685997]
1285
1286     cs = createGenericMatrix('Linear - P3-D60',
1287                              fromReferenceValues=[acesAP0toXYZ, xyzToP3D60])
1288     configData['colorSpaces'].append(cs)
1289
1290     # ACES to Linear, P3D60 primaries
1291     xyzToP3DCI = [2.7253940305, -1.0180030062, -0.4401631952,
1292                   -0.7951680258, 1.6897320548, 0.0226471906,
1293                   0.0412418914, -0.0876390192, 1.1009293786]
1294
1295     cs = createGenericMatrix('Linear - P3-DCI',
1296                              fromReferenceValues=[acesAP0toXYZ, xyzToP3DCI])
1297     configData['colorSpaces'].append(cs)
1298
1299     # ACES to Linear, Rec 709 primaries
1300     xyzToRec709 = [3.2409699419, -1.5373831776, -0.4986107603,
1301                    -0.9692436363, 1.8759675015, 0.0415550574,
1302                    0.0556300797, -0.2039769589, 1.0569715142]
1303
1304     cs = createGenericMatrix('Linear - Rec.709',
1305                              fromReferenceValues=[acesAP0toXYZ, xyzToRec709])
1306     configData['colorSpaces'].append(cs)
1307
1308     # ACES to Linear, Rec 2020 primaries
1309     xyzToRec2020 = [1.7166511880, -0.3556707838, -0.2533662814,
1310                     -0.6666843518, 1.6164812366, 0.0157685458,
1311                     0.0176398574, -0.0427706133, 0.9421031212]
1312
1313     cs = createGenericMatrix('Linear - Rec.2020',
1314                              fromReferenceValues=[acesAP0toXYZ, xyzToRec2020])
1315     configData['colorSpaces'].append(cs)
1316
1317     print("generateLUTs - end")
1318     return configData
1319
1320
1321 def generateBakedLUTs(odtInfo,
1322                       shaperName,
1323                       bakedDir,
1324                       configPath,
1325                       lutResolution1d,
1326                       lutResolution3d,
1327                       lutResolutionShaper=1024):
1328     """
1329     Object description.
1330
1331     Parameters
1332     ----------
1333     parameter : type
1334         Parameter description.
1335
1336     Returns
1337     -------
1338     type
1339          Return value description.
1340     """
1341
1342     # Add the legal and full variations into this list
1343     odtInfoC = dict(odtInfo)
1344     for odtCTLName, odtValues in odtInfo.iteritems():
1345         if odtCTLName in ['Academy.Rec2020_100nits_dim.a1.0.0',
1346                           'Academy.Rec709_100nits_dim.a1.0.0',
1347                           'Academy.Rec709_D60sim_100nits_dim.a1.0.0']:
1348             odtName = odtValues["transformUserName"]
1349
1350             odtValuesLegal = dict(odtValues)
1351             odtValuesLegal["transformUserName"] = "%s - Legal" % odtName
1352             odtInfoC["%s - Legal" % odtCTLName] = odtValuesLegal
1353
1354             odtValuesFull = dict(odtValues)
1355             odtValuesFull["transformUserName"] = "%s - Full" % odtName
1356             odtInfoC["%s - Full" % odtCTLName] = odtValuesFull
1357
1358             del (odtInfoC[odtCTLName])
1359
1360     for odtCTLName, odtValues in odtInfoC.iteritems():
1361         odtPrefix = odtValues["transformUserNamePrefix"]
1362         odtName = odtValues["transformUserName"]
1363
1364         # For Photoshop
1365         for inputspace in ["ACEScc", "ACESproxy"]:
1366             args = ["--iconfig", configPath, "-v", "--inputspace", inputspace]
1367             args += ["--outputspace", "%s" % odtName]
1368             args += ["--description",
1369                      "%s - %s for %s data" % (odtPrefix, odtName, inputspace)]
1370             args += ["--shaperspace", shaperName, "--shapersize",
1371                      str(lutResolutionShaper)]
1372             args += ["--cubesize", str(lutResolution3d)]
1373             args += ["--format", "icc", "%s/photoshop/%s for %s.icc" % (
1374                 bakedDir, odtName, inputspace)]
1375
1376             bakeLUT = Process(description="bake a LUT", cmd="ociobakelut",
1377                               args=args)
1378             bakeLUT.execute()
1379
1380             # For Flame, Lustre
1381         for inputspace in ["ACEScc", "ACESproxy"]:
1382             args = ["--iconfig", configPath, "-v", "--inputspace", inputspace]
1383             args += ["--outputspace", "%s" % odtName]
1384             args += ["--description",
1385                      "%s - %s for %s data" % (odtPrefix, odtName, inputspace)]
1386             args += ["--shaperspace", shaperName, "--shapersize",
1387                      str(lutResolutionShaper)]
1388             args += ["--cubesize", str(lutResolution3d)]
1389
1390             fargs = ["--format", "flame", "%s/flame/%s for %s Flame.3dl" % (
1391                 bakedDir, odtName, inputspace)]
1392             bakeLUT = Process(description="bake a LUT", cmd="ociobakelut",
1393                               args=(args + fargs))
1394             bakeLUT.execute()
1395
1396             largs = ["--format", "lustre", "%s/lustre/%s for %s Lustre.3dl" % (
1397                 bakedDir, odtName, inputspace)]
1398             bakeLUT = Process(description="bake a LUT", cmd="ociobakelut",
1399                               args=(args + largs))
1400             bakeLUT.execute()
1401
1402         # For Maya, Houdini
1403         for inputspace in ["ACEScg", "ACES2065-1"]:
1404             args = ["--iconfig", configPath, "-v", "--inputspace", inputspace]
1405             args += ["--outputspace", "%s" % odtName]
1406             args += ["--description",
1407                      "%s - %s for %s data" % (odtPrefix, odtName, inputspace)]
1408             if inputspace == 'ACEScg':
1409                 linShaperName = "%s - AP1" % shaperName
1410             else:
1411                 linShaperName = shaperName
1412             args += ["--shaperspace", linShaperName, "--shapersize",
1413                      str(lutResolutionShaper)]
1414
1415             args += ["--cubesize", str(lutResolution3d)]
1416
1417             margs = ["--format", "cinespace", "%s/maya/%s for %s Maya.csp" % (
1418                 bakedDir, odtName, inputspace)]
1419             bakeLUT = Process(description="bake a LUT", cmd="ociobakelut",
1420                               args=(args + margs))
1421             bakeLUT.execute()
1422
1423             hargs = ["--format", "houdini",
1424                      "%s/houdini/%s for %s Houdini.lut" % (
1425                          bakedDir, odtName, inputspace)]
1426             bakeLUT = Process(description="bake a LUT", cmd="ociobakelut",
1427                               args=(args + hargs))
1428             bakeLUT.execute()
1429
1430
1431 def createConfigDir(configDir, bakeSecondaryLUTs):
1432     """
1433     Object description.
1434
1435     Parameters
1436     ----------
1437     parameter : type
1438         Parameter description.
1439
1440     Returns
1441     -------
1442     type
1443          Return value description.
1444     """
1445
1446     dirs = [configDir, "%s/luts" % configDir]
1447     if bakeSecondaryLUTs:
1448         dirs.extend(["%s/baked" % configDir,
1449                      "%s/baked/flame" % configDir,
1450                      "%s/baked/photoshop" % configDir,
1451                      "%s/baked/houdini" % configDir,
1452                      "%s/baked/lustre" % configDir,
1453                      "%s/baked/maya" % configDir])
1454
1455     for d in dirs:
1456         if not os.path.exists(d):
1457             os.mkdir(d)
1458
1459
1460 def getTransformInfo(ctlTransform):
1461     """
1462     Object description.
1463
1464     Parameters
1465     ----------
1466     parameter : type
1467         Parameter description.
1468
1469     Returns
1470     -------
1471     type
1472          Return value description.
1473     """
1474
1475     fp = open(ctlTransform, 'rb')
1476
1477     # Read lines
1478     lines = fp.readlines()
1479
1480     # Grab transform ID and User Name
1481     transformID = lines[1][3:].split('<')[1].split('>')[1].lstrip().rstrip()
1482     # print(transformID)
1483     transformUserName = '-'.join(
1484         lines[2][3:].split('<')[1].split('>')[1].split('-')[
1485         1:]).lstrip().rstrip()
1486     transformUserNamePrefix = \
1487         lines[2][3:].split('<')[1].split('>')[1].split('-')[
1488             0].lstrip().rstrip()
1489     # print(transformUserName)
1490     fp.close()
1491
1492     return transformID, transformUserName, transformUserNamePrefix
1493
1494
1495 def getODTInfo(acesCTLReleaseDir):
1496     """
1497     Object description.
1498
1499     For versions after WGR9.
1500
1501     Parameters
1502     ----------
1503     parameter : type
1504         Parameter description.
1505
1506     Returns
1507     -------
1508     type
1509          Return value description.
1510     """
1511
1512     # Credit to Alex Fry for the original approach here
1513     odtDir = os.path.join(acesCTLReleaseDir, "odt")
1514     allodt = []
1515     for dirName, subdirList, fileList in os.walk(odtDir):
1516         for fname in fileList:
1517             allodt.append((os.path.join(dirName, fname)))
1518
1519     odtCTLs = [x for x in allodt if
1520                ("InvODT" not in x) and (os.path.split(x)[-1][0] != '.')]
1521
1522     # print odtCTLs
1523
1524     odts = {}
1525
1526     for odtCTL in odtCTLs:
1527         odtTokens = os.path.split(odtCTL)
1528         # print(odtTokens)
1529
1530         # Handle nested directories
1531         odtPathTokens = os.path.split(odtTokens[-2])
1532         odtDir = odtPathTokens[-1]
1533         while odtPathTokens[-2][-3:] != 'odt':
1534             odtPathTokens = os.path.split(odtPathTokens[-2])
1535             odtDir = os.path.join(odtPathTokens[-1], odtDir)
1536
1537         # Build full name
1538         # print("odtDir : %s" % odtDir)
1539         transformCTL = odtTokens[-1]
1540         # print(transformCTL)
1541         odtName = string.join(transformCTL.split('.')[1:-1], '.')
1542         # print(odtName)
1543
1544         # Find id, user name and user name prefix
1545         (transformID, transformUserName,
1546          transformUserNamePrefix) = getTransformInfo(
1547             "%s/odt/%s/%s" % (acesCTLReleaseDir, odtDir, transformCTL))
1548
1549         # Find inverse
1550         transformCTLInverse = "InvODT.%s.ctl" % odtName
1551         if not os.path.exists(
1552                 os.path.join(odtTokens[-2], transformCTLInverse)):
1553             transformCTLInverse = None
1554         # print(transformCTLInverse)
1555
1556         # Add to list of ODTs
1557         odts[odtName] = {}
1558         odts[odtName]['transformCTL'] = os.path.join(odtDir, transformCTL)
1559         if transformCTLInverse != None:
1560             odts[odtName]['transformCTLInverse'] = os.path.join(
1561                 odtDir, transformCTLInverse)
1562
1563         odts[odtName]['transformID'] = transformID
1564         odts[odtName]['transformUserNamePrefix'] = transformUserNamePrefix
1565         odts[odtName]['transformUserName'] = transformUserName
1566
1567         print("ODT : %s" % odtName)
1568         print("\tTransform ID               : %s" % transformID)
1569         print("\tTransform User Name Prefix : %s" % transformUserNamePrefix)
1570         print("\tTransform User Name        : %s" % transformUserName)
1571         print("\tForward ctl                : %s" % (
1572             odts[odtName]['transformCTL']))
1573         if 'transformCTLInverse' in odts[odtName]:
1574             print("\tInverse ctl                : %s" % (
1575                 odts[odtName]['transformCTLInverse']))
1576         else:
1577             print("\tInverse ctl                : %s" % "None")
1578
1579     print("\n")
1580
1581     return odts
1582
1583
1584 def getLMTInfo(acesCTLReleaseDir):
1585     """
1586     Object description.
1587
1588     For versions after WGR9.
1589
1590     Parameters
1591     ----------
1592     parameter : type
1593         Parameter description.
1594
1595     Returns
1596     -------
1597     type
1598          Return value description.
1599     """
1600
1601     # Credit to Alex Fry for the original approach here
1602     lmtDir = os.path.join(acesCTLReleaseDir, "lmt")
1603     alllmt = []
1604     for dirName, subdirList, fileList in os.walk(lmtDir):
1605         for fname in fileList:
1606             alllmt.append((os.path.join(dirName, fname)))
1607
1608     lmtCTLs = [x for x in alllmt if
1609                ("InvLMT" not in x) and ("README" not in x) and (
1610                    os.path.split(x)[-1][0] != '.')]
1611
1612     # print lmtCTLs
1613
1614     lmts = {}
1615
1616     for lmtCTL in lmtCTLs:
1617         lmtTokens = os.path.split(lmtCTL)
1618         # print(lmtTokens)
1619
1620         # Handle nested directories
1621         lmtPathTokens = os.path.split(lmtTokens[-2])
1622         lmtDir = lmtPathTokens[-1]
1623         while lmtPathTokens[-2][-3:] != 'ctl':
1624             lmtPathTokens = os.path.split(lmtPathTokens[-2])
1625             lmtDir = os.path.join(lmtPathTokens[-1], lmtDir)
1626
1627         # Build full name
1628         # print("lmtDir : %s" % lmtDir)
1629         transformCTL = lmtTokens[-1]
1630         # print(transformCTL)
1631         lmtName = string.join(transformCTL.split('.')[1:-1], '.')
1632         # print(lmtName)
1633
1634         # Find id, user name and user name prefix
1635         (transformID, transformUserName,
1636          transformUserNamePrefix) = getTransformInfo(
1637             "%s/%s/%s" % (acesCTLReleaseDir, lmtDir, transformCTL))
1638
1639         # Find inverse
1640         transformCTLInverse = "InvLMT.%s.ctl" % lmtName
1641         if not os.path.exists(
1642                 os.path.join(lmtTokens[-2], transformCTLInverse)):
1643             transformCTLInverse = None
1644         # print(transformCTLInverse)
1645
1646         # Add to list of LMTs
1647         lmts[lmtName] = {}
1648         lmts[lmtName]['transformCTL'] = os.path.join(lmtDir, transformCTL)
1649         if transformCTLInverse != None:
1650             # TODO: Check unresolved *odtName* referemce.
1651             lmts[odtName]['transformCTLInverse'] = os.path.join(
1652                 lmtDir, transformCTLInverse)
1653
1654         lmts[lmtName]['transformID'] = transformID
1655         lmts[lmtName]['transformUserNamePrefix'] = transformUserNamePrefix
1656         lmts[lmtName]['transformUserName'] = transformUserName
1657
1658         print("LMT : %s" % lmtName)
1659         print("\tTransform ID               : %s" % transformID)
1660         print("\tTransform User Name Prefix : %s" % transformUserNamePrefix)
1661         print("\tTransform User Name        : %s" % transformUserName)
1662         print("\t Forward ctl : %s" % lmts[lmtName]['transformCTL'])
1663         if 'transformCTLInverse' in lmts[lmtName]:
1664             print("\t Inverse ctl : %s" % (
1665                 lmts[lmtName]['transformCTLInverse']))
1666         else:
1667             print("\t Inverse ctl : %s" % "None")
1668
1669     print("\n")
1670
1671     return lmts
1672
1673
1674 def createACESConfig(acesCTLReleaseDir,
1675                      configDir,
1676                      lutResolution1d=4096,
1677                      lutResolution3d=64,
1678                      bakeSecondaryLUTs=True,
1679                      cleanup=True):
1680     """
1681     Creates the ACES configuration.
1682
1683     Parameters
1684     ----------
1685     parameter : type
1686         Parameter description.
1687
1688     Returns
1689     -------
1690     type
1691          Return value description.
1692     """
1693
1694     # Get ODT names and CTL paths
1695     odtInfo = getODTInfo(acesCTLReleaseDir)
1696
1697     # Get ODT names and CTL paths
1698     lmtInfo = getLMTInfo(acesCTLReleaseDir)
1699
1700     # Create config dir
1701     createConfigDir(configDir, bakeSecondaryLUTs)
1702
1703     # Generate config data and LUTs for different transforms
1704     lutDir = "%s/luts" % configDir
1705     shaperName = 'Output Shaper'
1706     configData = generateLUTs(odtInfo,
1707                               lmtInfo,
1708                               shaperName,
1709                               acesCTLReleaseDir,
1710                               lutDir,
1711                               lutResolution1d,
1712                               lutResolution3d,
1713                               cleanup)
1714
1715     # Create the config using the generated LUTs
1716     print("Creating generic config")
1717     config = createConfig(configData)
1718     print("\n\n\n")
1719
1720     # Write the config to disk
1721     writeConfig(config, "%s/config.ocio" % configDir)
1722
1723     # Create a config that will work well with Nuke using the previously
1724     # generated LUTs.
1725     print("Creating Nuke-specific config")
1726     nuke_config = createConfig(configData, nuke=True)
1727     print("\n\n\n")
1728
1729     # Write the config to disk
1730     writeConfig(nuke_config, "%s/nuke_config.ocio" % configDir)
1731
1732     # Bake secondary LUTs using the config
1733     if bakeSecondaryLUTs:
1734         generateBakedLUTs(odtInfo,
1735                           shaperName,
1736                           "%s/baked" % configDir,
1737                           "%s/config.ocio" % configDir,
1738                           lutResolution1d,
1739                           lutResolution3d,
1740                           lutResolution1d)
1741
1742     return True
1743
1744
1745 def main():
1746     """
1747     Object description.
1748
1749     Parameters
1750     ----------
1751     parameter : type
1752         Parameter description.
1753
1754     Returns
1755     -------
1756     type
1757          Return value description.
1758     """
1759
1760     import optparse
1761
1762     p = optparse.OptionParser(description='An OCIO config generation script',
1763                               prog='createACESConfig',
1764                               version='createACESConfig 0.1',
1765                               usage='%prog [options]')
1766     p.add_option('--acesCTLDir', '-a', default=os.environ.get(
1767         'ACES_OCIO_CTL_DIRECTORY', None))
1768     p.add_option('--configDir', '-c', default=os.environ.get(
1769         'ACES_OCIO_CONFIGURATION_DIRECTORY', None))
1770     p.add_option('--lutResolution1d', default=4096)
1771     p.add_option('--lutResolution3d', default=64)
1772     p.add_option('--dontBakeSecondaryLUTs', action="store_true")
1773     p.add_option('--keepTempImages', action="store_true")
1774
1775     options, arguments = p.parse_args()
1776
1777     #
1778     # Get options
1779     #
1780     acesCTLDir = options.acesCTLDir
1781     configDir = options.configDir
1782     lutResolution1d = int(options.lutResolution1d)
1783     lutResolution3d = int(options.lutResolution3d)
1784     bakeSecondaryLUTs = not (options.dontBakeSecondaryLUTs)
1785     cleanupTempImages = not (options.keepTempImages)
1786
1787     try:
1788         argsStart = sys.argv.index('--') + 1
1789         args = sys.argv[argsStart:]
1790     except:
1791         argsStart = len(sys.argv) + 1
1792         args = []
1793
1794     print("command line : \n%s\n" % " ".join(sys.argv))
1795
1796     # TODO: Use assertion and mention environment variables.
1797     if not acesCTLDir:
1798         print("process: No ACES CTL directory specified")
1799         return
1800     if not configDir:
1801         print("process: No configuration directory specified")
1802         return
1803     #
1804     # Generate the configuration
1805     #
1806     return createACESConfig(acesCTLDir,
1807                             configDir,
1808                             lutResolution1d,
1809                             lutResolution3d,
1810                             bakeSecondaryLUTs,
1811                             cleanupTempImages)
1812
1813
1814 if __name__ == '__main__':
1815     main()