Added the ability to write out 1D and 3D LUTs as CTL code
[OpenColorIO-Configs.git] / aces_1.0.0 / python / aces_ocio / generate_lut.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 """
5 Defines objects to generate various kind of 1d, 2d and 3d LUTs in various file
6 formats.
7 """
8
9 from __future__ import division
10
11 import array
12 import os
13 import sys
14
15 import OpenImageIO as oiio
16
17 from aces_ocio.process import Process
18
19 __author__ = 'ACES Developers'
20 __copyright__ = 'Copyright (C) 2014 - 2015 - ACES Developers'
21 __license__ = ''
22 __maintainer__ = 'ACES Developers'
23 __email__ = 'aces@oscars.org'
24 __status__ = 'Production'
25
26 __all__ = ['generate_1d_LUT_image',
27            'write_SPI_1d',
28            'generate_1d_LUT_from_image',
29            'generate_3d_LUT_image',
30            'generate_3d_LUT_from_image',
31            'apply_CTL_to_image',
32            'convert_bit_depth',
33            'generate_1d_LUT_from_CTL',
34            'correct_LUT_image',
35            'generate_3d_LUT_from_CTL',
36            'main']
37
38
39 def generate_1d_LUT_image(ramp_1d_path,
40                           resolution=1024,
41                           min_value=0,
42                           max_value=1):
43     """
44     Object description.
45
46     Parameters
47     ----------
48     parameter : type
49         Parameter description.
50
51     Returns
52     -------
53     type
54          Return value description.
55     """
56
57     ramp = oiio.ImageOutput.create(ramp_1d_path)
58
59     spec = oiio.ImageSpec()
60     spec.set_format(oiio.FLOAT)
61     # spec.format.basetype = oiio.FLOAT
62     spec.width = resolution
63     spec.height = 1
64     spec.nchannels = 3
65
66     ramp.open(ramp_1d_path, spec, oiio.Create)
67
68     data = array.array('f',
69                        '\0' * spec.width * spec.height * spec.nchannels * 4)
70     for i in range(resolution):
71         value = float(i) / (resolution - 1) * (
72             max_value - min_value) + min_value
73         data[i * spec.nchannels + 0] = value
74         data[i * spec.nchannels + 1] = value
75         data[i * spec.nchannels + 2] = value
76
77     ramp.write_image(spec.format, data)
78     ramp.close()
79
80
81 def write_SPI_1d(filename, 
82                  from_min, 
83                  from_max, 
84                  data, 
85                  entries, 
86                  channels, 
87                  components=3):
88     """
89     Object description.
90
91     Credit to *Alex Fry* for the original single channel version of the spi1d
92     writer.
93
94     Parameters
95     ----------
96     parameter : type
97         Parameter description.
98
99     Returns
100     -------
101     type
102          Return value description.
103     """
104
105     # May want to use fewer components than there are channels in the data
106     # Most commonly used for single channel LUTs
107     components = min(3, components, channels)
108
109     with open(filename, 'w') as fp:
110         fp.write('Version 1\n')
111         fp.write('From %f %f\n' % (from_min, from_max))
112         fp.write('Length %d\n' % entries)
113         fp.write('Components %d\n' % components)
114         fp.write('{\n')
115         for i in range(0, entries):
116             entry = ''
117             for j in range(0, components):
118                 entry = '%s %s' % (entry, data[i * channels + j])
119             fp.write('        %s\n' % entry)
120         fp.write('}\n')
121
122
123 def write_CSP_1d(filename, 
124                  from_min, 
125                  from_max, 
126                  data, 
127                  entries, 
128                  channels, 
129                  components=3):
130     """
131     Object description.
132
133     Parameters
134     ----------
135     parameter : type
136         Parameter description.
137
138     Returns
139     -------
140     type
141          Return value description.
142     """
143
144     # May want to use fewer components than there are channels in the data
145     # Most commonly used for single channel LUTs
146     components = min(3, components, channels)
147
148     with open(filename, 'w') as fp:
149         fp.write('CSPLUTV100\n')
150         fp.write('1D\n')
151         fp.write('\n')
152         fp.write('BEGIN METADATA\n')
153         fp.write('END METADATA\n')
154
155         fp.write('\n')
156
157         fp.write('2\n')
158         fp.write('%f %f\n' % (from_min, from_max))
159         fp.write('0.0 1.0\n')
160         fp.write('2\n')
161         fp.write('%f %f\n' % (from_min, from_max))
162         fp.write('0.0 1.0\n')
163         fp.write('2\n')
164         fp.write('%f %f\n' % (from_min, from_max))
165         fp.write('0.0 1.0\n')
166
167         fp.write('\n')
168
169         fp.write('%d\n' % entries)
170         if components == 1:
171           for i in range(0, entries):
172               entry = ''
173               for j in range(3):
174                   entry = '%s %s' % (entry, data[i * channels])
175               fp.write('%s\n' % entry)
176         else:
177           for i in range(entries):
178               entry = ''
179               for j in range(components):
180                   entry = '%s %s' % (entry, data[i * channels + j])
181               fp.write('%s\n' % entry)
182         fp.write('\n')
183
184 def write_CTL_1d(filename, 
185                  from_min, 
186                  from_max, 
187                  data, 
188                  entries, 
189                  channels, 
190                  components=3):
191     """
192     Object description.
193
194     Parameters
195     ----------
196     parameter : type
197         Parameter description.
198
199     Returns
200     -------
201     type
202          Return value description.
203     """
204
205     # May want to use fewer components than there are channels in the data
206     # Most commonly used for single channel LUTs
207     components = min(3, components, channels)
208
209     with open(filename, 'w') as fp:
210         fp.write('// %d x %d LUT generated by "generate_lut"\n' % (
211           entries, components))
212         fp.write('\n')
213         fp.write('const float min1d = %3.9f;\n' % from_min)
214         fp.write('const float max1d = %3.9f;\n' % from_max)
215         fp.write('\n')
216
217         # Write LUT
218         if components == 1:
219           fp.write('const float lut[] = {\n')
220           for i in range(0, entries):
221               fp.write('%s' % data[i * channels])
222               if i != (entries-1):
223                 fp.write(',')
224               fp.write('\n')
225           fp.write('};\n')
226           fp.write('\n')
227         else:
228           for j in range(components):
229             fp.write('const float lut%d[] = {\n' % j)
230             for i in range(0, entries):
231                 fp.write('%s' % data[i * channels])
232                 if i != (entries-1):
233                   fp.write(',')
234                 fp.write('\n')
235             fp.write('};\n')
236             fp.write('\n')
237
238         fp.write('void main\n')
239         fp.write('(\n')
240         fp.write('  input varying float rIn,\n')
241         fp.write('  input varying float gIn,\n')
242         fp.write('  input varying float bIn,\n')
243         fp.write('  input varying float aIn,\n')
244         fp.write('  output varying float rOut,\n')
245         fp.write('  output varying float gOut,\n')
246         fp.write('  output varying float bOut,\n')
247         fp.write('  output varying float aOut\n')
248         fp.write(')\n')
249         fp.write('{\n')
250         fp.write('  float r = rIn;\n')
251         fp.write('  float g = gIn;\n')
252         fp.write('  float b = bIn;\n')
253         fp.write('\n')
254         fp.write('  // Apply LUT\n')
255         if components == 1:
256           fp.write('  r = lookup1D(lut, min1d, max1d, r);\n')
257           fp.write('  g = lookup1D(lut, min1d, max1d, g);\n')
258           fp.write('  b = lookup1D(lut, min1d, max1d, b);\n')
259         elif components == 3:
260           fp.write('  r = lookup1D(lut0, min1d, max1d, r);\n')
261           fp.write('  g = lookup1D(lut1, min1d, max1d, g);\n')
262           fp.write('  b = lookup1D(lut2, min1d, max1d, b);\n')          
263         fp.write('\n')
264         fp.write('  rOut = r;\n')
265         fp.write('  gOut = g;\n')
266         fp.write('  bOut = b;\n')
267         fp.write('  aOut = aIn;\n')
268         fp.write('}\n')
269
270 def write_1d(filename, 
271              from_min, 
272              from_max, 
273              data, 
274              data_entries, 
275              data_channels, 
276              lut_components=3,
277              format='spi1d'):
278     """
279     Object description.
280
281     Parameters
282     ----------
283     parameter : type
284         Parameter description.
285
286     Returns
287     -------
288     type
289          Return value description.
290     """
291
292     ocioFormatsToExtensions = {'cinespace' : 'csp',
293                                'flame'     : '3dl',
294                                'icc'       : 'icc',
295                                'houdini'   : 'lut',
296                                'lustre'    : '3dl',
297                                'ctl'       : 'ctl'}
298
299     if format in ocioFormatsToExtensions:
300       if ocioFormatsToExtensions[format] == 'csp':
301         write_CSP_1d(filename,
302                      from_min,
303                      from_max,
304                      data,
305                      data_entries,
306                      data_channels,
307                      lut_components)
308       elif ocioFormatsToExtensions[format] == 'ctl':
309         write_CTL_1d(filename,
310                      from_min,
311                      from_max,
312                      data,
313                      data_entries,
314                      data_channels,
315                      lut_components)
316     else:
317       write_SPI_1d(filename,
318                    from_min,
319                    from_max,
320                    data,
321                    data_entries,
322                    data_channels,
323                    lut_components)
324
325 def generate_1d_LUT_from_image(ramp_1d_path,
326                                output_path=None,
327                                min_value=0,
328                                max_value=1,
329                                channels=3,
330                                format='spi1d'):
331     """
332     Object description.
333
334     Parameters
335     ----------
336     parameter : type
337         Parameter description.
338
339     Returns
340     -------
341     type
342          Return value description.
343     """
344
345     if output_path is None:
346         output_path = '%s.%s' % (ramp_1d_path, 'spi1d')
347
348     ramp = oiio.ImageInput.open(ramp_1d_path)
349
350     ramp_spec = ramp.spec()
351     ramp_width = ramp_spec.width
352     ramp_channels = ramp_spec.nchannels
353
354     # Forcibly read data as float, the Python API doesn't handle half-float
355     # well yet.
356     type = oiio.FLOAT
357     ramp_data = ramp.read_image(type)
358
359     write_1d(output_path, min_value, max_value, 
360       ramp_data, ramp_width, ramp_channels, channels, format)
361
362
363 def generate_3d_LUT_image(ramp_3d_path, resolution=32):
364     """
365     Object description.
366
367     Parameters
368     ----------
369     parameter : type
370         Parameter description.
371
372     Returns
373     -------
374     type
375          Return value description.
376     """
377
378     args = ['--generate',
379             '--cubesize',
380             str(resolution),
381             '--maxwidth',
382             str(resolution * resolution),
383             '--output',
384             ramp_3d_path]
385     lut_extract = Process(description='generate a 3d LUT image',
386                           cmd='ociolutimage',
387                           args=args)
388     lut_extract.execute()
389
390
391 def generate_3d_LUT_from_image(ramp_3d_path, 
392                                output_path=None, 
393                                resolution=32,
394                                format='spi3d'):
395     """
396     Object description.
397
398     Parameters
399     ----------
400     parameter : type
401         Parameter description.
402
403     Returns
404     -------
405     type
406          Return value description.
407     """
408
409     if output_path is None:
410         output_path = '%s.%s' % (ramp_3d_path, 'spi3d')
411
412     ocioFormatsToExtensions = {'cinespace' : 'csp',
413                                'flame'     : '3dl',
414                                'icc'       : 'icc',
415                                'houdini'   : 'lut',
416                                'lustre'    : '3dl'}
417
418     if format == 'spi3d' or not (format in ocioFormatsToExtensions):
419       # Extract a spi3d LUT
420       args = ['--extract',
421               '--cubesize',
422               str(resolution),
423               '--maxwidth',
424               str(resolution * resolution),
425               '--input',
426               ramp_3d_path,
427               '--output',
428               output_path]
429       lut_extract = Process(description='extract a 3d LUT',
430                             cmd='ociolutimage',
431                             args=args)
432       lut_extract.execute()
433
434     else:
435       output_path_spi3d = '%s.%s' % (output_path, 'spi3d')
436
437       # Extract a spi3d LUT
438       args = ['--extract',
439               '--cubesize',
440               str(resolution),
441               '--maxwidth',
442               str(resolution * resolution),
443               '--input',
444               ramp_3d_path,
445               '--output',
446               output_path_spi3d]
447       lut_extract = Process(description='extract a 3d LUT',
448                             cmd='ociolutimage',
449                             args=args)
450       lut_extract.execute()
451
452       # Convert to a different format
453       args = ['--lut',
454               output_path_spi3d,
455               '--format',
456               format,
457               output_path]
458       lut_convert = Process(description='convert a 3d LUT',
459                             cmd='ociobakelut',
460                             args=args)
461       lut_convert.execute()
462
463
464 def apply_CTL_to_image(input_image,
465                        output_image,
466                        ctl_paths=None,
467                        input_scale=1,
468                        output_scale=1,
469                        global_params=None,
470                        aces_ctl_directory=None):
471     """
472     Object description.
473
474     Parameters
475     ----------
476     parameter : type
477         Parameter description.
478
479     Returns
480     -------
481     type
482          Return value description.
483     """
484
485     if ctl_paths is None:
486         ctl_paths = []
487     if global_params is None:
488         global_params = {}
489
490     if len(ctl_paths) > 0:
491         ctlenv = os.environ
492         if aces_ctl_directory is not None:
493             if os.path.split(aces_ctl_directory)[1] != 'utilities':
494                 ctl_module_path = os.path.join(aces_ctl_directory, 'utilities')
495             else:
496                 ctl_module_path = aces_ctl_directory
497             ctlenv['CTL_MODULE_PATH'] = ctl_module_path
498
499         args = []
500         for ctl in ctl_paths:
501             args += ['-ctl', ctl]
502         args += ['-force']
503         args += ['-input_scale', str(input_scale)]
504         args += ['-output_scale', str(output_scale)]
505         args += ['-global_param1', 'aIn', '1.0']
506         for key, value in global_params.iteritems():
507             args += ['-global_param1', key, str(value)]
508         args += [input_image]
509         args += [output_image]
510
511         ctlp = Process(description='a ctlrender process',
512                        cmd='ctlrender',
513                        args=args, env=ctlenv)
514
515         ctlp.execute()
516
517
518 def convert_bit_depth(input_image, output_image, depth):
519     """
520     Object description.
521
522     Parameters
523     ----------
524     parameter : type
525         Parameter description.
526
527     Returns
528     -------
529     type
530          Return value description.
531     """
532
533     args = [input_image,
534             '-d',
535             depth,
536             '-o',
537             output_image]
538     convert = Process(description='convert image bit depth',
539                       cmd='oiiotool',
540                       args=args)
541     convert.execute()
542
543
544 def generate_1d_LUT_from_CTL(lut_path,
545                              ctl_paths,
546                              lut_resolution=1024,
547                              identity_LUT_bit_depth='half',
548                              input_scale=1,
549                              output_scale=1,
550                              global_params=None,
551                              cleanup=True,
552                              aces_ctl_directory=None,
553                              min_value=0,
554                              max_value=1,
555                              channels=3,
556                              format='spi1d'):
557     """
558     Object description.
559
560     Parameters
561     ----------
562     parameter : type
563         Parameter description.
564
565     Returns
566     -------
567     type
568          Return value description.
569     """
570
571     if global_params is None:
572         global_params = {}
573
574     lut_path_base = os.path.splitext(lut_path)[0]
575
576     identity_LUT_image_float = '%s.%s.%s' % (lut_path_base, 'float', 'tiff')
577     generate_1d_LUT_image(identity_LUT_image_float,
578                           lut_resolution,
579                           min_value,
580                           max_value)
581
582     if identity_LUT_bit_depth not in ['half', 'float']:
583         identity_LUT_image = '%s.%s.%s' % (lut_path_base, 'uint16', 'tiff')
584         convert_bit_depth(identity_LUT_image_float,
585                           identity_LUT_image,
586                           identity_LUT_bit_depth)
587     else:
588         identity_LUT_image = identity_LUT_image_float
589
590     transformed_LUT_image = '%s.%s.%s' % (lut_path_base, 'transformed', 'exr')
591     apply_CTL_to_image(identity_LUT_image,
592                        transformed_LUT_image,
593                        ctl_paths,
594                        input_scale,
595                        output_scale,
596                        global_params,
597                        aces_ctl_directory)
598
599     generate_1d_LUT_from_image(transformed_LUT_image,
600                                lut_path,
601                                min_value,
602                                max_value,
603                                channels,
604                                format)
605
606     if cleanup:
607         os.remove(identity_LUT_image)
608         if identity_LUT_image != identity_LUT_image_float:
609             os.remove(identity_LUT_image_float)
610         os.remove(transformed_LUT_image)
611
612
613 def correct_LUT_image(transformed_LUT_image,
614                       corrected_LUT_image,
615                       lut_resolution):
616     """
617     Object description.
618
619     Parameters
620     ----------
621     parameter : type
622         Parameter description.
623
624     Returns
625     -------
626     type
627          Return value description.
628     """
629
630     transformed = oiio.ImageInput.open(transformed_LUT_image)
631
632     transformed_spec = transformed.spec()
633     width = transformed_spec.width
634     height = transformed_spec.height
635     channels = transformed_spec.nchannels
636
637     if width != lut_resolution * lut_resolution or height != lut_resolution:
638         print(('Correcting image as resolution is off. '
639                'Found %d x %d. Expected %d x %d') % (
640                   width,
641                   height,
642                   lut_resolution * lut_resolution,
643                   lut_resolution))
644         print('Generating %s' % corrected_LUT_image)
645
646         # Forcibly read data as float, the Python API doesn't handle half-float
647         # well yet.
648         type = oiio.FLOAT
649         source_data = transformed.read_image(type)
650
651         correct = oiio.ImageOutput.create(corrected_LUT_image)
652
653         correct_spec = oiio.ImageSpec()
654         correct_spec.set_format(oiio.FLOAT)
655         correct_spec.width = height
656         correct_spec.height = width
657         correct_spec.nchannels = channels
658
659         correct.open(corrected_LUT_image, correct_spec, oiio.Create)
660
661         dest_data = array.array('f',
662                                 ('\0' * correct_spec.width *
663                                  correct_spec.height *
664                                  correct_spec.nchannels * 4))
665         for j in range(0, correct_spec.height):
666             for i in range(0, correct_spec.width):
667                 for c in range(0, correct_spec.nchannels):
668                     dest_data[(correct_spec.nchannels *
669                                correct_spec.width * j +
670                                correct_spec.nchannels * i + c)] = (
671                         source_data[correct_spec.nchannels *
672                                     correct_spec.width * j +
673                                     correct_spec.nchannels * i + c])
674
675         correct.write_image(correct_spec.format, dest_data)
676         correct.close()
677     else:
678         # shutil.copy(transformedLUTImage, correctedLUTImage)
679         corrected_LUT_image = transformed_LUT_image
680
681     transformed.close()
682
683     return corrected_LUT_image
684
685
686 def generate_3d_LUT_from_CTL(lut_path,
687                              ctl_paths,
688                              lut_resolution=64,
689                              identity_LUT_bit_depth='half',
690                              input_scale=1,
691                              output_scale=1,
692                              global_params=None,
693                              cleanup=True,
694                              aces_ctl_directory=None,
695                              format='spi3d'):
696     """
697     Object description.
698
699     Parameters
700     ----------
701     parameter : type
702         Parameter description.
703
704     Returns
705     -------
706     type
707          Return value description.
708     """
709
710     if global_params is None:
711         global_params = {}
712
713     lut_path_base = os.path.splitext(lut_path)[0]
714
715     identity_LUT_image_float = '%s.%s.%s' % (lut_path_base, 'float', 'tiff')
716     generate_3d_LUT_image(identity_LUT_image_float, lut_resolution)
717
718     if identity_LUT_bit_depth not in ['half', 'float']:
719         identity_LUT_image = '%s.%s.%s' % (lut_path_base,
720                                            identity_LUT_bit_depth,
721                                            'tiff')
722         convert_bit_depth(identity_LUT_image_float,
723                           identity_LUT_image,
724                           identity_LUT_bit_depth)
725     else:
726         identity_LUT_image = identity_LUT_image_float
727
728     transformed_LUT_image = '%s.%s.%s' % (lut_path_base, 'transformed', 'exr')
729     apply_CTL_to_image(identity_LUT_image,
730                        transformed_LUT_image,
731                        ctl_paths,
732                        input_scale,
733                        output_scale,
734                        global_params,
735                        aces_ctl_directory)
736
737     corrected_LUT_image = '%s.%s.%s' % (lut_path_base, 'correct', 'exr')
738     corrected_LUT_image = correct_LUT_image(transformed_LUT_image,
739                                             corrected_LUT_image,
740                                             lut_resolution)
741
742     generate_3d_LUT_from_image(corrected_LUT_image, 
743                                lut_path, 
744                                lut_resolution, 
745                                format)
746
747     if cleanup:
748         os.remove(identity_LUT_image)
749         if identity_LUT_image != identity_LUT_image_float:
750             os.remove(identity_LUT_image_float)
751         os.remove(transformed_LUT_image)
752         if corrected_LUT_image != transformed_LUT_image:
753             os.remove(corrected_LUT_image)
754         if format != 'spi3d':
755             lut_path_spi3d = '%s.%s' % (lut_path, 'spi3d')
756             os.remove(lut_path_spi3d)
757
758 def main():
759     """
760     Object description.
761
762     Parameters
763     ----------
764     parameter : type
765         Parameter description.
766
767     Returns
768     -------
769     type
770          Return value description.
771     """
772
773     import optparse
774
775     p = optparse.OptionParser(
776         description='A utility to generate LUTs from CTL',
777         prog='generateLUT',
778         version='0.01',
779         usage='%prog [options]')
780
781     p.add_option('--lut', '-l', type='string', default='')
782     p.add_option('--format', '-f', type='string', default='')
783     p.add_option('--ctl', '-c', type='string', action='append')
784     p.add_option('--lutResolution1d', '', type='int', default=1024)
785     p.add_option('--lutResolution3d', '', type='int', default=33)
786     p.add_option('--ctlReleasePath', '-r', type='string', default='')
787     p.add_option('--bitDepth', '-b', type='string', default='float')
788     p.add_option('--keepTempImages', '', action='store_true')
789     p.add_option('--minValue', '', type='float', default=0)
790     p.add_option('--maxValue', '', type='float', default=1)
791     p.add_option('--inputScale', '', type='float', default=1)
792     p.add_option('--outputScale', '', type='float', default=1)
793     p.add_option('--ctlRenderParam', '-p', type='string', nargs=2,
794                  action='append')
795
796     p.add_option('--generate1d', '', action='store_true')
797     p.add_option('--generate3d', '', action='store_true')
798
799     options, arguments = p.parse_args()
800
801     lut = options.lut
802     format = options.format
803     ctls = options.ctl
804     lut_resolution_1d = options.lutResolution1d
805     lut_resolution_3d = options.lutResolution3d
806     min_value = options.minValue
807     max_value = options.maxValue
808     input_scale = options.inputScale
809     output_scale = options.outputScale
810     ctl_release_path = options.ctlReleasePath
811     generate_1d = options.generate1d is True
812     generate_3d = options.generate3d is True
813     bit_depth = options.bitDepth
814     cleanup = not options.keepTempImages
815
816     params = {}
817     if options.ctlRenderParam is not None:
818         for param in options.ctlRenderParam:
819             params[param[0]] = float(param[1])
820
821     try:
822         args_start = sys.argv.index('--') + 1
823         args = sys.argv[args_start:]
824     except:
825         args_start = len(sys.argv) + 1
826         args = []
827
828     if generate_1d:
829         print('1D LUT generation options')
830     else:
831         print('3D LUT generation options')
832
833     print('lut                 : %s' % lut)
834     print('format              : %s' % format)
835     print('ctls                : %s' % ctls)
836     print('lut res 1d          : %s' % lut_resolution_1d)
837     print('lut res 3d          : %s' % lut_resolution_3d)
838     print('min value           : %s' % min_value)
839     print('max value           : %s' % max_value)
840     print('input scale         : %s' % input_scale)
841     print('output scale        : %s' % output_scale)
842     print('ctl render params   : %s' % params)
843     print('ctl release path    : %s' % ctl_release_path)
844     print('bit depth of input  : %s' % bit_depth)
845     print('cleanup temp images : %s' % cleanup)
846
847     if generate_1d:
848         generate_1d_LUT_from_CTL(lut,
849                                  ctls,
850                                  lut_resolution_1d,
851                                  bit_depth,
852                                  input_scale,
853                                  output_scale,
854                                  params,
855                                  cleanup,
856                                  ctl_release_path,
857                                  min_value,
858                                  max_value,
859                                  format=format)
860
861     elif generate_3d:
862         generate_3d_LUT_from_CTL(lut,
863                                  ctls,
864                                  lut_resolution_3d,
865                                  bit_depth,
866                                  input_scale,
867                                  output_scale,
868                                  params,
869                                  cleanup,
870                                  ctl_release_path,
871                                  format=format)
872     else:
873         print(('\n\nNo LUT generated. '
874                'You must choose either 1D or 3D LUT generation\n\n'))
875
876
877 if __name__ == '__main__':
878     main()
879