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