Refactored to move ACES and general colorspace generation into their own files
[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 pprint
12 import shutil
13 import string
14 import sys
15
16 import PyOpenColorIO as ocio
17
18 import aces_ocio.create_aces_colorspaces as aces
19 import aces_ocio.create_arri_colorspaces as arri
20 import aces_ocio.create_canon_colorspaces as canon
21 import aces_ocio.create_red_colorspaces as red
22 import aces_ocio.create_sony_colorspaces as sony
23 import aces_ocio.create_general_colorspaces as general
24
25 from aces_ocio.generate_lut import (
26     generate_1d_LUT_from_CTL,
27     generate_3d_LUT_from_CTL,
28     write_SPI_1d)
29 from aces_ocio.process import Process
30 from aces_ocio.utilities import ColorSpace, mat44_from_mat33, sanitize_path, compact
31
32 __author__ = 'ACES Developers'
33 __copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers'
34 __license__ = ''
35 __maintainer__ = 'ACES Developers'
36 __email__ = 'aces@oscars.org'
37 __status__ = 'Production'
38
39 __all__ = ['ACES_OCIO_CTL_DIRECTORY_ENVIRON',
40            'ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON',
41            'set_config_default_roles',
42            'write_config',
43            'generate_OCIO_transform',
44            'create_config',
45            'generate_LUTs',
46            'generate_baked_LUTs',
47            'create_config_dir',
48            'get_transform_info',
49            'get_ODT_info',
50            'get_LMT_info',
51            'create_ACES_config',
52            'main']
53
54 ACES_OCIO_CTL_DIRECTORY_ENVIRON = 'ACES_OCIO_CTL_DIRECTORY'
55 ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON = 'ACES_OCIO_CONFIGURATION_DIRECTORY'
56
57
58 def set_config_default_roles(config,
59                              color_picking='',
60                              color_timing='',
61                              compositing_log='',
62                              data='',
63                              default='',
64                              matte_paint='',
65                              reference='',
66                              scene_linear='',
67                              texture_paint=''):
68     """
69     Sets given *OCIO* configuration default roles.
70
71     Parameters
72     ----------
73     config : config
74         *OCIO* configuration.
75     color_picking : str or unicode
76         Color picking role title.
77     color_timing : str or unicode
78         Color timing role title.
79     compositing_log : str or unicode
80         Compositing log role title.
81     data : str or unicode
82         Data role title.
83     default : str or unicode
84         Default role title.
85     matte_paint : str or unicode
86         Matte painting role title.
87     reference : str or unicode
88         Reference role title.
89     scene_linear : str or unicode
90         Scene linear role title.
91     texture_paint : str or unicode
92         Texture painting role title.
93
94     Returns
95     -------
96     bool
97          Definition success.
98     """
99
100     if color_picking:
101         config.setRole(ocio.Constants.ROLE_COLOR_PICKING, color_picking)
102     if color_timing:
103         config.setRole(ocio.Constants.ROLE_COLOR_TIMING, color_timing)
104     if compositing_log:
105         config.setRole(ocio.Constants.ROLE_COMPOSITING_LOG, compositing_log)
106     if data:
107         config.setRole(ocio.Constants.ROLE_DATA, data)
108     if default:
109         config.setRole(ocio.Constants.ROLE_DEFAULT, default)
110     if matte_paint:
111         config.setRole(ocio.Constants.ROLE_MATTE_PAINT, matte_paint)
112     if reference:
113         config.setRole(ocio.Constants.ROLE_REFERENCE, reference)
114     if scene_linear:
115         config.setRole(ocio.Constants.ROLE_SCENE_LINEAR, scene_linear)
116     if texture_paint:
117         config.setRole(ocio.Constants.ROLE_TEXTURE_PAINT, texture_paint)
118
119     return True
120
121
122 def write_config(config, config_path, sanity_check=True):
123     """
124     Writes the configuration to given path.
125
126     Parameters
127     ----------
128     parameter : type
129         Parameter description.
130
131     Returns
132     -------
133     type
134          Return value description.
135     """
136
137     if sanity_check:
138         try:
139             config.sanityCheck()
140         except Exception, e:
141             print e
142             print 'Configuration was not written due to a failed Sanity Check'
143             return
144
145     with open(config_path, mode='w') as fp:
146         fp.write(config.serialize())
147
148
149 def generate_OCIO_transform(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     interpolation_options = {
165         'linear': ocio.Constants.INTERP_LINEAR,
166         'nearest': ocio.Constants.INTERP_NEAREST,
167         'tetrahedral': ocio.Constants.INTERP_TETRAHEDRAL
168     }
169     direction_options = {
170         'forward': ocio.Constants.TRANSFORM_DIR_FORWARD,
171         'inverse': ocio.Constants.TRANSFORM_DIR_INVERSE
172     }
173
174     ocio_transforms = []
175
176     for transform in transforms:
177
178         # lutFile transform
179         if transform['type'] == 'lutFile':
180             ocio_transform = ocio.FileTransform(
181                 src=transform['path'],
182                 interpolation=interpolation_options[
183                     transform['interpolation']],
184                 direction=direction_options[transform['direction']])
185             ocio_transforms.append(ocio_transform)
186         
187         # matrix transform
188         elif transform['type'] == 'matrix':
189             ocio_transform = ocio.MatrixTransform()
190             # MatrixTransform member variables can't be initialized directly.
191             # Each must be set individually.
192             ocio_transform.setMatrix(transform['matrix'])
193
194             if 'offset' in transform:
195                 ocio_transform.setOffset(transform['offset'])
196
197             if 'direction' in transform:
198                 ocio_transform.setDirection(
199                     direction_options[transform['direction']])
200
201             ocio_transforms.append(ocio_transform)
202
203         # exponent transform
204         elif transform['type'] == 'exponent':
205             ocio_transform = ocio.ExponentTransform()
206             ocio_transform.setValue(transform['value'])
207             ocio_transforms.append(ocio_transform)
208
209         # log transform
210         elif transform['type'] == 'log':
211             ocio_transform = ocio.LogTransform(
212                 base=transform['base'],
213                 direction=direction_options[transform['direction']])
214
215             ocio_transforms.append(ocio_transform)
216
217         # color space transform
218         elif transform['type'] == 'colorspace':
219             ocio_transform = ocio.ColorSpaceTransform( src=transform['src'],
220                 dst=transform['dst'],
221                 direction=direction_options['forward'] )
222             ocio_transforms.append(ocio_transform)
223
224         # unknown type
225         else:
226             print("Ignoring unknown transform type : %s" % transform['type'])
227
228     if len(ocio_transforms) > 1:
229         group_transform = ocio.GroupTransform()
230         for transform in ocio_transforms:
231             group_transform.push_back(transform)
232         transform = group_transform
233     else:
234         transform = ocio_transforms[0]
235
236     return transform
237
238 def add_colorspace_alias(config, reference_colorspace, colorspace, colorspace_alias_names):
239     """
240     Object description.
241
242     Parameters
243     ----------
244     parameter : type
245         Parameter description.
246
247     Returns
248     -------
249     type
250          Return value description.
251     """
252
253     for alias_name in colorspace_alias_names:
254         if alias_name == colorspace.name.lower():
255             return
256
257         print( "Adding alias colorspace space %s, alias to %s" % (
258             alias_name, colorspace.name))
259
260         compact_family_name = "Aliases"
261
262         ocio_colorspace_alias = ocio.ColorSpace(
263             name=alias_name,
264             bitDepth=colorspace.bit_depth,
265             description=colorspace.description,
266             equalityGroup=colorspace.equality_group,
267             family=compact_family_name,
268             isData=colorspace.is_data,
269             allocation=colorspace.allocation_type,
270             allocationVars=colorspace.allocation_vars)
271
272         if colorspace.to_reference_transforms != []:
273             print("Generating To-Reference transforms")
274             ocio_transform = generate_OCIO_transform([{
275                 'type': 'colorspace',
276                 'src': colorspace.name,
277                 'dst': reference_colorspace.name,
278                 'direction': 'forward'
279                 }])
280             ocio_colorspace_alias.setTransform(
281                 ocio_transform,
282                 ocio.Constants.COLORSPACE_DIR_TO_REFERENCE)
283
284         if colorspace.from_reference_transforms != []:
285             print("Generating From-Reference transforms")
286             ocio_transform = generate_OCIO_transform([{
287                 'type': 'colorspace',
288                 'src': reference_colorspace.name,
289                 'dst': colorspace.name,
290                 'direction': 'forward'
291                 }])
292             ocio_colorspace_alias.setTransform(
293                 ocio_transform,
294                 ocio.Constants.COLORSPACE_DIR_FROM_REFERENCE)
295
296         config.addColorSpace(ocio_colorspace_alias)
297
298
299 def create_config(config_data, nuke=False):
300     """
301     Object description.
302
303     Parameters
304     ----------
305     parameter : type
306         Parameter description.
307
308     Returns
309     -------
310     type
311          Return value description.
312     """
313
314     # Creating the *OCIO* configuration.
315     config = ocio.Config()
316
317     # Setting configuration overall values.
318     config.setDescription('An ACES config generated from python')
319     config.setSearchPath('luts')
320
321     # Defining the reference colorspace.
322     reference_data = config_data['referenceColorSpace']
323     print('Adding the reference color space : %s' % reference_data.name)
324
325     reference = ocio.ColorSpace(
326         name=reference_data.name,
327         bitDepth=reference_data.bit_depth,
328         description=reference_data.description,
329         equalityGroup=reference_data.equality_group,
330         family=reference_data.family,
331         isData=reference_data.is_data,
332         allocation=reference_data.allocation_type,
333         allocationVars=reference_data.allocation_vars)
334
335     config.addColorSpace(reference)
336
337     # Add alias
338     if reference_data.aliases != []:
339         add_colorspace_alias(config, reference_data,
340             reference_data, reference_data.aliases)
341
342     print("")
343
344     # Creating the remaining colorspaces.
345     for colorspace in sorted(config_data['colorSpaces']):
346         print('Creating new color space : %s' % colorspace.name)
347
348         ocio_colorspace = ocio.ColorSpace(
349             name=colorspace.name,
350             bitDepth=colorspace.bit_depth,
351             description=colorspace.description,
352             equalityGroup=colorspace.equality_group,
353             family=colorspace.family,
354             isData=colorspace.is_data,
355             allocation=colorspace.allocation_type,
356             allocationVars=colorspace.allocation_vars)
357
358         if colorspace.to_reference_transforms:
359             print('Generating To-Reference transforms')
360             ocio_transform = generate_OCIO_transform(
361                 colorspace.to_reference_transforms)
362             ocio_colorspace.setTransform(
363                 ocio_transform,
364                 ocio.Constants.COLORSPACE_DIR_TO_REFERENCE)
365
366         if colorspace.from_reference_transforms:
367             print('Generating From-Reference transforms')
368             ocio_transform = generate_OCIO_transform(
369                 colorspace.from_reference_transforms)
370             ocio_colorspace.setTransform(
371                 ocio_transform,
372                 ocio.Constants.COLORSPACE_DIR_FROM_REFERENCE)
373
374         config.addColorSpace(ocio_colorspace)
375
376         #
377         # Add alias to normal colorspace, using compact name
378         #
379         if colorspace.aliases != []:
380             add_colorspace_alias(config, reference_data, 
381                 colorspace, colorspace.aliases)
382
383         print('')
384
385     # Defining the *views* and *displays*.
386     displays = []
387     views = []
388
389     # Defining a *generic* *display* and *view* setup.
390     if not nuke:
391         for display, view_list in config_data['displays'].iteritems():
392             for view_name, colorspace in view_list.iteritems():
393                 config.addDisplay(display, view_name, colorspace.name)
394                 if not (view_name in views):
395                     views.append(view_name)
396             displays.append(display)
397
398     # Defining the *Nuke* specific set of *views* and *displays*.
399     else:
400         for display, view_list in config_data['displays'].iteritems():
401             for view_name, colorspace in view_list.iteritems():
402                 if view_name == 'Output Transform':
403                     view_name = 'View'
404                     config.addDisplay(display, view_name, colorspace.name)
405                     if not (view_name in views):
406                         views.append(view_name)
407             displays.append(display)
408
409         linear_display_space_name = config_data['linearDisplaySpace'].name
410         log_display_space_name = config_data['logDisplaySpace'].name
411
412         config.addDisplay('linear', 'View', linear_display_space_name)
413         displays.append('linear')
414         config.addDisplay('log', 'View', log_display_space_name)
415         displays.append('log')
416
417     # Setting the active *displays* and *views*.
418     config.setActiveDisplays(','.join(sorted(displays)))
419     config.setActiveViews(','.join(views))
420
421     set_config_default_roles(
422         config,
423         color_picking=reference.getName(),
424         color_timing=reference.getName(),
425         compositing_log=reference.getName(),
426         data=reference.getName(),
427         default=reference.getName(),
428         matte_paint=reference.getName(),
429         reference=reference.getName(),
430         scene_linear=reference.getName(),
431         texture_paint=reference.getName())
432
433     config.sanityCheck()
434
435     return config
436
437 def generate_LUTs(odt_info,
438                   lmt_info,
439                   shaper_name,
440                   aces_CTL_directory,
441                   lut_directory,
442                   lut_resolution_1d=4096,
443                   lut_resolution_3d=64,
444                   cleanup=True):
445     """
446     Object description.
447
448     Parameters
449     ----------
450     parameter : type
451         Parameter description.
452
453     Returns
454     -------
455     dict
456          Colorspaces and transforms converting between those colorspaces and
457          the reference colorspace, *ACES*.
458     """
459
460     print('generateLUTs - begin')
461     config_data = {}
462
463     # Initialize a few variables
464     config_data['displays'] = {}
465     config_data['colorSpaces'] = []
466
467     # -------------------------------------------------------------------------
468     # *ACES Color Spaces*
469     # -------------------------------------------------------------------------
470
471     # *ACES* colorspaces
472     (aces_reference,
473      aces_colorspaces, 
474      aces_displays,
475      aces_log_display_space) = aces.create_colorspaces(aces_CTL_directory,
476                                                        lut_directory, 
477                                                        lut_resolution_1d,
478                                                        lut_resolution_3d,
479                                                        lmt_info,
480                                                        odt_info,
481                                                        shaper_name,
482                                                        cleanup)
483
484     config_data['referenceColorSpace'] = aces_reference
485
486     for cs in aces_colorspaces:
487         config_data['colorSpaces'].append(cs)
488
489     for name, data in aces_displays.iteritems():
490         config_data['displays'][name] = data
491
492     config_data['linearDisplaySpace'] = aces_reference
493     config_data['logDisplaySpace'] = aces_log_display_space
494
495     # -------------------------------------------------------------------------
496     # *Camera Input Transforms*
497     # -------------------------------------------------------------------------
498
499     # *Log-C* to *ACES*.
500     arri_colorSpaces = arri.create_colorspaces(lut_directory,
501                                                lut_resolution_1d)
502     for cs in arri_colorSpaces:
503         config_data['colorSpaces'].append(cs)
504
505     # *Canon-Log* to *ACES*.
506     canon_colorspaces = canon.create_colorspaces(lut_directory,
507                                                  lut_resolution_1d)
508     for cs in canon_colorspaces:
509         config_data['colorSpaces'].append(cs)
510
511     # *RED* colorspaces to *ACES*.
512     red_colorspaces = red.create_colorspaces(lut_directory, 
513                                              lut_resolution_1d)
514     for cs in red_colorspaces:
515         config_data['colorSpaces'].append(cs)
516
517     # *S-Log* to *ACES*.
518     sony_colorSpaces = sony.create_colorspaces(lut_directory,
519                                                lut_resolution_1d)
520     for cs in sony_colorSpaces:
521         config_data['colorSpaces'].append(cs)
522
523     # -------------------------------------------------------------------------
524     # General Color Spaces
525     # -------------------------------------------------------------------------
526     general_colorSpaces = general.create_colorspaces(lut_directory,
527                                                      lut_resolution_1d,
528                                                      lut_resolution_3d)
529     for cs in general_colorSpaces:
530         config_data['colorSpaces'].append(cs)
531
532     print('generateLUTs - end')
533     return config_data
534
535
536 def generate_baked_LUTs(odt_info,
537                         shaper_name,
538                         baked_directory,
539                         config_path,
540                         lut_resolution_1d,
541                         lut_resolution_3d,
542                         lut_resolution_shaper=1024):
543     """
544     Object description.
545
546     Parameters
547     ----------
548     parameter : type
549         Parameter description.
550
551     Returns
552     -------
553     type
554          Return value description.
555     """
556
557     odt_info_C = dict(odt_info)
558     for odt_CTL_name, odt_values in odt_info.iteritems():
559         if odt_CTL_name in ['Academy.Rec2020_100nits_dim.a1.0.0',
560                             'Academy.Rec709_100nits_dim.a1.0.0',
561                             'Academy.Rec709_D60sim_100nits_dim.a1.0.0']:
562             odt_name = odt_values['transformUserName']
563
564             odt_values_legal = dict(odt_values)
565             odt_values_legal['transformUserName'] = '%s - Legal' % odt_name
566             odt_info_C['%s - Legal' % odt_CTL_name] = odt_values_legal
567
568             odt_values_full = dict(odt_values)
569             odt_values_full['transformUserName'] = '%s - Full' % odt_name
570             odt_info_C['%s - Full' % odt_CTL_name] = odt_values_full
571
572             del (odt_info_C[odt_CTL_name])
573
574     for odt_CTL_name, odt_values in odt_info_C.iteritems():
575         odt_prefix = odt_values['transformUserNamePrefix']
576         odt_name = odt_values['transformUserName']
577
578         # *Photoshop*
579         for input_space in ['ACEScc', 'ACESproxy']:
580             args = ['--iconfig', config_path,
581                     '-v',
582                     '--inputspace', input_space]
583             args += ['--outputspace', '%s' % odt_name]
584             args += ['--description',
585                      '%s - %s for %s data' % (odt_prefix,
586                                               odt_name,
587                                               input_space)]
588             args += ['--shaperspace', shaper_name,
589                      '--shapersize', str(lut_resolution_shaper)]
590             args += ['--cubesize', str(lut_resolution_3d)]
591             args += ['--format',
592                      'icc',
593                      os.path.join(baked_directory,
594                                   'photoshop',
595                                   '%s for %s.icc' % (odt_name, input_space))]
596
597             bake_LUT = Process(description='bake a LUT',
598                                cmd='ociobakelut',
599                                args=args)
600             bake_LUT.execute()
601
602         # *Flame*, *Lustre*
603         for input_space in ['ACEScc', 'ACESproxy']:
604             args = ['--iconfig', config_path,
605                     '-v',
606                     '--inputspace', input_space]
607             args += ['--outputspace', '%s' % odt_name]
608             args += ['--description',
609                      '%s - %s for %s data' % (
610                          odt_prefix, odt_name, input_space)]
611             args += ['--shaperspace', shaper_name,
612                      '--shapersize', str(lut_resolution_shaper)]
613             args += ['--cubesize', str(lut_resolution_3d)]
614
615             fargs = ['--format',
616                      'flame',
617                      os.path.join(
618                          baked_directory,
619                          'flame',
620                          '%s for %s Flame.3dl' % (odt_name, input_space))]
621             bake_LUT = Process(description='bake a LUT',
622                                cmd='ociobakelut',
623                                args=(args + fargs))
624             bake_LUT.execute()
625
626             largs = ['--format',
627                      'lustre',
628                      os.path.join(
629                          baked_directory,
630                          'lustre',
631                          '%s for %s Lustre.3dl' % (odt_name, input_space))]
632             bake_LUT = Process(description='bake a LUT',
633                                cmd='ociobakelut',
634                                args=(args + largs))
635             bake_LUT.execute()
636
637         # *Maya*, *Houdini*
638         for input_space in ['ACEScg', 'ACES2065-1']:
639             args = ['--iconfig', config_path,
640                     '-v',
641                     '--inputspace', input_space]
642             args += ['--outputspace', '%s' % odt_name]
643             args += ['--description',
644                      '%s - %s for %s data' % (
645                          odt_prefix, odt_name, input_space)]
646             if input_space == 'ACEScg':
647                 lin_shaper_name = '%s - AP1' % shaper_name
648             else:
649                 lin_shaper_name = shaper_name
650             args += ['--shaperspace', lin_shaper_name,
651                      '--shapersize', str(lut_resolution_shaper)]
652
653             args += ['--cubesize', str(lut_resolution_3d)]
654
655             margs = ['--format',
656                      'cinespace',
657                      os.path.join(
658                          baked_directory,
659                          'maya',
660                          '%s for %s Maya.csp' % (odt_name, input_space))]
661             bake_LUT = Process(description='bake a LUT',
662                                cmd='ociobakelut',
663                                args=(args + margs))
664             bake_LUT.execute()
665
666             hargs = ['--format',
667                      'houdini',
668                      os.path.join(
669                          baked_directory,
670                          'houdini',
671                          '%s for %s Houdini.lut' % (odt_name, input_space))]
672             bake_LUT = Process(description='bake a LUT',
673                                cmd='ociobakelut',
674                                args=(args + hargs))
675             bake_LUT.execute()
676
677
678 def create_config_dir(config_directory, bake_secondary_LUTs):
679     """
680     Object description.
681
682     Parameters
683     ----------
684     parameter : type
685         Parameter description.
686
687     Returns
688     -------
689     type
690          Return value description.
691     """
692
693     lut_directory = os.path.join(config_directory, 'luts')
694     dirs = [config_directory, lut_directory]
695     if bake_secondary_LUTs:
696         dirs.extend([os.path.join(config_directory, 'baked'),
697                      os.path.join(config_directory, 'baked', 'flame'),
698                      os.path.join(config_directory, 'baked', 'photoshop'),
699                      os.path.join(config_directory, 'baked', 'houdini'),
700                      os.path.join(config_directory, 'baked', 'lustre'),
701                      os.path.join(config_directory, 'baked', 'maya')])
702
703     for d in dirs:
704         not os.path.exists(d) and os.mkdir(d)
705
706     return lut_directory
707
708 def get_transform_info(ctl_transform):
709     """
710     Object description.
711
712     Parameters
713     ----------
714     parameter : type
715         Parameter description.
716
717     Returns
718     -------
719     type
720          Return value description.
721     """
722
723     with open(ctl_transform, 'rb') as fp:
724         lines = fp.readlines()
725
726     # Retrieving the *transform ID* and *User Name*.
727     transform_id = lines[1][3:].split('<')[1].split('>')[1].strip()
728     transform_user_name = '-'.join(
729         lines[2][3:].split('<')[1].split('>')[1].split('-')[1:]).strip()
730     transform_user_name_prefix = (
731         lines[2][3:].split('<')[1].split('>')[1].split('-')[0].strip())
732
733     return transform_id, transform_user_name, transform_user_name_prefix
734
735
736 def get_ODT_info(aces_CTL_directory):
737     """
738     Object description.
739
740     For versions after WGR9.
741
742     Parameters
743     ----------
744     parameter : type
745         Parameter description.
746
747     Returns
748     -------
749     type
750          Return value description.
751     """
752
753     # TODO: Investigate usage of *files_walker* definition here.
754     # Credit to *Alex Fry* for the original approach here.
755     odt_dir = os.path.join(aces_CTL_directory, 'odt')
756     all_odt = []
757     for dir_name, subdir_list, file_list in os.walk(odt_dir):
758         for fname in file_list:
759             all_odt.append((os.path.join(dir_name, fname)))
760
761     odt_CTLs = [x for x in all_odt if
762                 ('InvODT' not in x) and (os.path.split(x)[-1][0] != '.')]
763
764     odts = {}
765
766     for odt_CTL in odt_CTLs:
767         odt_tokens = os.path.split(odt_CTL)
768
769         # Handling nested directories.
770         odt_path_tokens = os.path.split(odt_tokens[-2])
771         odt_dir = odt_path_tokens[-1]
772         while odt_path_tokens[-2][-3:] != 'odt':
773             odt_path_tokens = os.path.split(odt_path_tokens[-2])
774             odt_dir = os.path.join(odt_path_tokens[-1], odt_dir)
775
776         # Building full name,
777         transform_CTL = odt_tokens[-1]
778         odt_name = string.join(transform_CTL.split('.')[1:-1], '.')
779
780         # Finding id, user name and user name prefix.
781         (transform_ID,
782          transform_user_name,
783          transform_user_name_prefix) = get_transform_info(
784             os.path.join(aces_CTL_directory, 'odt', odt_dir, transform_CTL))
785
786         # Finding inverse.
787         transform_CTL_inverse = 'InvODT.%s.ctl' % odt_name
788         if not os.path.exists(
789                 os.path.join(odt_tokens[-2], transform_CTL_inverse)):
790             transform_CTL_inverse = None
791
792         # Add to list of ODTs
793         odts[odt_name] = {}
794         odts[odt_name]['transformCTL'] = os.path.join(odt_dir, transform_CTL)
795         if transform_CTL_inverse is not None:
796             odts[odt_name]['transformCTLInverse'] = os.path.join(
797                 odt_dir, transform_CTL_inverse)
798
799         odts[odt_name]['transformID'] = transform_ID
800         odts[odt_name]['transformUserNamePrefix'] = transform_user_name_prefix
801         odts[odt_name]['transformUserName'] = transform_user_name
802
803         forward_CTL = odts[odt_name]['transformCTL']
804
805         print('ODT : %s' % odt_name)
806         print('\tTransform ID               : %s' % transform_ID)
807         print('\tTransform User Name Prefix : %s' % transform_user_name_prefix)
808         print('\tTransform User Name        : %s' % transform_user_name)
809         print('\tForward ctl                : %s' % forward_CTL)
810         if 'transformCTLInverse' in odts[odt_name]:
811             inverse_CTL = odts[odt_name]['transformCTLInverse']
812             print('\tInverse ctl                : %s' % inverse_CTL)
813         else:
814             print('\tInverse ctl                : %s' % 'None')
815
816     print('\n')
817
818     return odts
819
820
821 def get_LMT_info(aces_CTL_directory):
822     """
823     Object description.
824
825     For versions after WGR9.
826
827     Parameters
828     ----------
829     parameter : type
830         Parameter description.
831
832     Returns
833     -------
834     type
835          Return value description.
836     """
837
838     # TODO: Investigate refactoring with previous definition.
839
840     # Credit to Alex Fry for the original approach here
841     lmt_dir = os.path.join(aces_CTL_directory, 'lmt')
842     all_lmt = []
843     for dir_name, subdir_list, file_list in os.walk(lmt_dir):
844         for fname in file_list:
845             all_lmt.append((os.path.join(dir_name, fname)))
846
847     lmt_CTLs = [x for x in all_lmt if
848                 ('InvLMT' not in x) and ('README' not in x) and (
849                     os.path.split(x)[-1][0] != '.')]
850
851     lmts = {}
852
853     for lmt_CTL in lmt_CTLs:
854         lmt_tokens = os.path.split(lmt_CTL)
855
856         # Handlimg nested directories.
857         lmt_path_tokens = os.path.split(lmt_tokens[-2])
858         lmt_dir = lmt_path_tokens[-1]
859         while lmt_path_tokens[-2][-3:] != 'ctl':
860             lmt_path_tokens = os.path.split(lmt_path_tokens[-2])
861             lmt_dir = os.path.join(lmt_path_tokens[-1], lmt_dir)
862
863         # Building full name.
864         transform_CTL = lmt_tokens[-1]
865         lmt_name = string.join(transform_CTL.split('.')[1:-1], '.')
866
867         # Finding id, user name and user name prefix.
868         (transform_ID,
869          transform_user_name,
870          transform_user_name_prefix) = get_transform_info(
871             os.path.join(aces_CTL_directory, lmt_dir, transform_CTL))
872
873         # Finding inverse.
874         transform_CTL_inverse = 'InvLMT.%s.ctl' % lmt_name
875         if not os.path.exists(
876                 os.path.join(lmt_tokens[-2], transform_CTL_inverse)):
877             transform_CTL_inverse = None
878
879         lmts[lmt_name] = {}
880         lmts[lmt_name]['transformCTL'] = os.path.join(lmt_dir, transform_CTL)
881         if transform_CTL_inverse is not None:
882             lmts[lmt_name]['transformCTLInverse'] = os.path.join(
883                 lmt_dir, transform_CTL_inverse)
884
885         lmts[lmt_name]['transformID'] = transform_ID
886         lmts[lmt_name]['transformUserNamePrefix'] = transform_user_name_prefix
887         lmts[lmt_name]['transformUserName'] = transform_user_name
888
889         forward_CTL = lmts[lmt_name]['transformCTL']
890
891         print('LMT : %s' % lmt_name)
892         print('\tTransform ID               : %s' % transform_ID)
893         print('\tTransform User Name Prefix : %s' % transform_user_name_prefix)
894         print('\tTransform User Name        : %s' % transform_user_name)
895         print('\t Forward ctl               : %s' % forward_CTL)
896         if 'transformCTLInverse' in lmts[lmt_name]:
897             inverse_CTL = lmts[lmt_name]['transformCTLInverse']
898             print('\t Inverse ctl                : %s' % inverse_CTL)
899         else:
900             print('\t Inverse ctl                : %s' % 'None')
901
902     print('\n')
903
904     return lmts
905
906
907 def create_ACES_config(aces_CTL_directory,
908                        config_directory,
909                        lut_resolution_1d=4096,
910                        lut_resolution_3d=64,
911                        bake_secondary_LUTs=True,
912                        cleanup=True):
913     """
914     Creates the ACES configuration.
915
916     Parameters
917     ----------
918     parameter : type
919         Parameter description.
920
921     Returns
922     -------
923     type
924          Return value description.
925     """
926
927     lut_directory = create_config_dir(config_directory, bake_secondary_LUTs)
928
929     odt_info = get_ODT_info(aces_CTL_directory)
930     lmt_info = get_LMT_info(aces_CTL_directory)
931
932     shaper_name = 'Output Shaper'
933     config_data = generate_LUTs(odt_info,
934                                 lmt_info,
935                                 shaper_name,
936                                 aces_CTL_directory,
937                                 lut_directory,
938                                 lut_resolution_1d,
939                                 lut_resolution_3d,
940                                 cleanup)
941
942     print('Creating "generic" config')
943     config = create_config(config_data)
944     print('\n\n\n')
945
946     write_config(config,
947                  os.path.join(config_directory, 'config.ocio'))
948
949     print('Creating "Nuke" config')
950     nuke_config = create_config(config_data, nuke=True)
951     print('\n\n\n')
952
953     write_config(nuke_config,
954                  os.path.join(config_directory, 'nuke_config.ocio'))
955
956     if bake_secondary_LUTs:
957         generate_baked_LUTs(odt_info,
958                             shaper_name,
959                             os.path.join(config_directory, 'baked'),
960                             os.path.join(config_directory, 'config.ocio'),
961                             lut_resolution_1d,
962                             lut_resolution_3d,
963                             lut_resolution_1d)
964
965     return True
966
967
968 def main():
969     """
970     Object description.
971
972     Parameters
973     ----------
974     parameter : type
975         Parameter description.
976
977     Returns
978     -------
979     type
980          Return value description.
981     """
982
983     import optparse
984
985     p = optparse.OptionParser(description='An OCIO config generation script',
986                               prog='createACESConfig',
987                               version='createACESConfig 0.1',
988                               usage='%prog [options]')
989     p.add_option('--acesCTLDir', '-a', default=os.environ.get(
990         ACES_OCIO_CTL_DIRECTORY_ENVIRON, None))
991     p.add_option('--configDir', '-c', default=os.environ.get(
992         ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON, None))
993     p.add_option('--lutResolution1d', default=4096)
994     p.add_option('--lutResolution3d', default=64)
995     p.add_option('--dontBakeSecondaryLUTs', action='store_true')
996     p.add_option('--keepTempImages', action='store_true')
997
998     options, arguments = p.parse_args()
999
1000     aces_CTL_directory = options.acesCTLDir
1001     config_directory = options.configDir
1002     lut_resolution_1d = int(options.lutResolution1d)
1003     lut_resolution_3d = int(options.lutResolution3d)
1004     bake_secondary_LUTs = not options.dontBakeSecondaryLUTs
1005     cleanup_temp_images = not options.keepTempImages
1006
1007     # TODO: Investigate the following statements.
1008     try:
1009         args_start = sys.argv.index('--') + 1
1010         args = sys.argv[args_start:]
1011     except:
1012         args_start = len(sys.argv) + 1
1013         args = []
1014
1015     print('command line : \n%s\n' % ' '.join(sys.argv))
1016
1017     assert aces_CTL_directory is not None, (
1018         'process: No "{0}" environment variable defined or no "ACES CTL" '
1019         'directory specified'.format(
1020             ACES_OCIO_CTL_DIRECTORY_ENVIRON))
1021
1022     assert config_directory is not None, (
1023         'process: No "{0}" environment variable defined or no configuration '
1024         'directory specified'.format(
1025             ACES_OCIO_CONFIGURATION_DIRECTORY_ENVIRON))
1026
1027     return create_ACES_config(aces_CTL_directory,
1028                               config_directory,
1029                               lut_resolution_1d,
1030                               lut_resolution_3d,
1031                               bake_secondary_LUTs,
1032                               cleanup_temp_images)
1033
1034
1035 if __name__ == '__main__':
1036     main()