first public test version of the GH4 branch
[OpenColorIO-Configs.git] / aces_1.0.1 / python / aces_ocio / colorspaces / panasonic_gh4.py
diff --git a/aces_1.0.1/python/aces_ocio/colorspaces/panasonic_gh4.py b/aces_1.0.1/python/aces_ocio/colorspaces/panasonic_gh4.py
new file mode 100644 (file)
index 0000000..b5cdf28
--- /dev/null
@@ -0,0 +1,507 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Implements support for *Panasonic GH4* colorspaces conversions and transfer
+functions based on empirical data.
+"""
+from panasonic_gh4_data import *
+
+import numpy as np
+from scipy import interpolate
+import matplotlib.pyplot as plt
+import math, sys
+
+import array
+import os
+
+import PyOpenColorIO as ocio
+
+import aces_ocio.generate_lut as genlut
+from aces_ocio.utilities import ColorSpace
+
+__author__ = 'Martin Schitter'
+__copyright__ = 'Copyright (C) 2015 - Martin Schitter'
+__license__ = 'GNU General Public License'
+__maintainer__ = 'Martin Schitter'
+__email__ = 'ms+aces@mur.at'
+__status__ = 'Experimental'
+
+__all__ = ['create_gh4_style',
+           'create_colorspaces']
+styles = {
+    'Scenery': [0.0079310344827586213, 0.017241379310344827,
+    0.053448275862068968, 0.27517241379310342, 0.7501724137931034,
+    1.3010344827586207, 2.1315517241379309, 3.0501724137931037,
+    4.3113793103448277, 5.8546551724137927, 7.9153448275862068,
+    10.296551724137931, 13.276896551724137, 16.769310344827588,
+    21.641896551724138, 27.386034482758621, 34.62258620689655,
+    43.353448275862071, 53.682931034482756, 65.670000000000002,
+    80.268275862068961, 102.43344827586206, 116.53896551724138,
+    134.62586206896552, 156.84931034482759, 170.11293103448276,
+    184.20775862068965, 196.58258620689656, 207.89155172413794,
+    217.53241379310344, 227.18224137931034, 236.08120689655172,
+    244.85396551724139, 252.55293103448275, 254.95758620689656,
+    255.0, 255.0, 255.0, 255.0, 255.0],
+    
+    'Natural': [0.032758620689655175, 0.048275862068965517,
+    0.16224137931034482, 0.4613793103448276, 1.0034482758620689,
+    1.7708620689655172, 2.6732758620689654, 3.9575862068965519,
+    5.5139655172413793, 7.4403448275862072, 9.6510344827586199,
+    12.639310344827587, 16.452758620689654, 20.901551724137931,
+    26.867241379310343, 34.115862068965519, 43.008448275862072,
+    52.998448275862067, 64.548103448275867, 76.758448275862065,
+    90.370862068965522, 109.64431034482759, 121.71120689655173,
+    137.56103448275863, 157.60396551724139, 169.77672413793104,
+    182.39965517241379, 194.02293103448275, 205.00258620689655,
+    214.89431034482757, 224.41034482758621, 233.6396551724138,
+    242.20258620689654, 250.67862068965516, 254.0, 254.0, 254.0,
+    254.0, 254.0, 254.0],
+    
+    'Vivid': [0.0013793103448275861, 0.0025862068965517241,
+    0.024482758620689656, 0.11120689655172414, 0.16810344827586207,
+    0.79741379310344829, 1.2001724137931034, 1.9437931034482758,
+    2.9256896551724139, 4.0244827586206897, 5.4912068965517244,
+    7.490344827586207, 10.088448275862069, 13.246379310344828,
+    17.292586206896551, 22.015862068965518, 28.217068965517242,
+    35.784310344827588, 44.731379310344828, 56.00931034482759,
+    70.820689655172416, 93.825344827586207, 108.79879310344828,
+    128.2251724137931, 152.32620689655172, 166.44137931034481,
+    181.00758620689655, 193.65810344827585, 205.37741379310344,
+    215.1153448275862, 224.87586206896552, 234.14637931034483,
+    242.44327586206896, 250.86465517241379, 254.98724137931035,
+    255.0, 255.0, 255.0, 255.0, 255.0],
+    
+    'Cinelike-D': [0.21293103448275863, 0.27379310344827584,
+    0.55500000000000005, 1.0448275862068965, 1.5686206896551724,
+    2.7260344827586205, 3.9756896551724137, 5.5051724137931037,
+    7.6129310344827585, 10.310344827586206, 13.517931034482759,
+    17.027586206896551, 21.260862068965519, 25.73741379310345,
+    30.967241379310344, 37.214310344827588, 44.142931034482757,
+    51.230689655172412, 58.622068965517244, 66.37155172413793,
+    75.241034482758621, 88.354310344827582, 96.904655172413797,
+    108.34051724137932, 125.1453448275862, 136.35637931034483,
+    150.76827586206898, 168.86913793103449, 185.40758620689655,
+    200.0305172413793, 213.47120689655173, 227.06482758620689,
+    242.42689655172413, 253.55879310344827, 254.97620689655173,
+    255.0, 255.0, 255.0, 255.0, 255.0],
+    
+    'V-Log': [31.429137931034482, 31.752758620689654,
+    31.977413793103448, 32.531551724137934, 32.976551724137934,
+    33.822586206896553, 34.706206896551727, 36.100344827586206,
+    37.966551724137929, 40.231724137931032, 42.8301724137931,
+    46.389482758620687, 50.373103448275863, 54.91965517241379,
+    60.101724137931036, 65.1351724137931, 70.557586206896545,
+    76.224827586206899, 81.544655172413798, 87.275517241379305,
+    93.204999999999998, 100.89396551724138, 105.2244827586207,
+    111.05396551724138, 118.80051724137931, 123.33396551724138,
+    129.31517241379311, 135.62620689655174, 141.68793103448274,
+    148.09086206896552, 154.73741379310346, 160.67603448275861,
+    167.00999999999999, 173.17275862068965, 179.17706896551724,
+    185.62896551724137, 190.93706896551726, 191.0, 191.0, 191.0],
+    
+    'Standard': [0.019827586206896553, 0.030344827586206897,
+    0.1296551724137931, 0.39862068965517239, 0.84431034482758616,
+    1.5265517241379309, 2.3403448275862071, 3.3762068965517242,
+    4.9127586206896554, 6.6224137931034486, 8.8127586206896549,
+    11.51948275862069, 14.727241379310344, 18.599482758620688,
+    23.895862068965517, 30.157413793103448, 38.333275862068966,
+    47.146724137931038, 57.659999999999997, 68.3953448275862,
+    81.231034482758616, 99.765517241379314, 111.80913793103448,
+    127.22586206896551, 147.36137931034483, 160.73448275862069,
+    174.77620689655171, 188.09258620689656, 200.56137931034482,
+    212.24275862068964, 222.05310344827586, 231.96775862068966,
+    240.85534482758621, 250.1894827586207, 255.0, 255.0, 255.0,
+    255.0, 255.0, 255.0],
+    
+    'BW': [0.012068965517241379, 0.03017241379310345,
+    0.15879310344827585, 0.40379310344827585, 1.039655172413793,
+    1.5532758620689655, 2.5448275862068965, 3.6275862068965519,
+    5.1298275862068969, 7.1593103448275865, 9.1227586206896554,
+    12.219482758620689, 15.602413793103448, 19.667068965517242,
+    25.558793103448277, 32.138103448275864, 40.762413793103448,
+    50.295862068965519, 61.059655172413791, 72.762758620689652,
+    86.235172413793109, 105.61879310344827, 118.42362068965517,
+    134.05362068965516, 155.23034482758621, 168.7403448275862,
+    182.65913793103448, 195.72913793103447, 208.67896551724138,
+    220.05086206896553, 229.83293103448275, 238.34137931034482,
+    246.78810344827585, 254.53086206896552, 255.0, 255.0, 255.0,
+    255.0, 255.0, 255.0],
+    
+    'Cinelike-V': [0.0, 0.0, 0.00034482758620689653,
+    0.017586206896551725, 0.16534482758620689, 0.71965517241379307,
+    1.0924137931034483, 1.8994827586206897, 2.7312068965517242,
+    3.7174137931034483, 5.0798275862068962, 7.0439655172413795,
+    9.4998275862068962, 12.756379310344828, 17.174827586206895,
+    22.375, 29.295000000000002, 37.235344827586204,
+    47.138448275862068, 58.181034482758619, 71.008103448275861,
+    89.295000000000002, 101.09810344827586, 117.49913793103448,
+    138.71793103448275, 152.10293103448276, 166.64051724137931,
+    181.39448275862068, 194.29568965517242, 206.1453448275862,
+    218.05224137931035, 229.84310344827585, 243.2646551724138,
+    254.25103448275863, 254.99310344827586, 255.0, 255.0, 255.0,
+    255.0, 255.0],
+    
+    'Portrait': [0.044999999999999998, 0.10706896551724138,
+    0.21482758620689654, 0.61827586206896556, 1.258448275862069,
+    1.9755172413793103, 2.8977586206896553, 4.243620689655172,
+    5.7982758620689658, 7.8572413793103451, 10.573620689655172,
+    13.566896551724138, 17.62396551724138, 22.091724137931035,
+    28.537068965517243, 36.138103448275864, 45.346551724137932,
+    55.862241379310348, 67.451379310344834, 79.880689655172418,
+    94.139827586206891, 113.69862068965517, 126.13396551724138,
+    141.94724137931036, 161.84068965517241, 173.89344827586206,
+    186.02206896551724, 196.99051724137931, 207.93103448275863,
+    217.54758620689654, 227.05655172413793, 235.96586206896552,
+    245.08810344827586, 253.00948275862069, 253.97603448275862,
+    254.0, 254.0, 254.0, 254.0, 254.0],
+    
+    'Custom': [0.039310344827586205, 0.1, 0.31086206896551727,
+    0.73482758620689659, 1.3484482758620691, 2.046551724137931,
+    3.1565517241379308, 4.5710344827586207, 6.1872413793103451,
+    8.3953448275862073, 10.947068965517241, 14.126724137931035,
+    18.054827586206898, 22.778965517241378, 29.185689655172414,
+    36.938448275862072, 45.952931034482759, 56.380000000000003,
+    67.094310344827591, 79.601724137931029, 93.560000000000002,
+    113.16017241379311, 125.54551724137932, 141.44189655172414,
+    162.21155172413793, 174.12517241379311, 186.3755172413793,
+    199.36862068965516, 211.13103448275862, 221.01068965517243,
+    230.70120689655172, 239.67758620689656, 249.01879310344827,
+    254.96413793103449, 255.0, 255.0, 255.0, 255.0, 255.0, 255.0]
+}
+
+def prepare_photostyle_to_linear(style, corr=None, debug_plot=False):
+
+    # 18% gray card correction
+    corr = 16.5
+    
+    #if not corr:
+    #    if style == 'V-Log':
+    #        corr = 16.5
+    #    else:
+    #        #corr = 17.3
+    #        corr = 16.5
+
+    exp = np.linspace( (corr-39)/3.0, corr/3.0, 40)
+    l = np.power(2, exp) * 0.18
+    vals = np.array(styles[style])
+    vals = vals/255
+
+    # remove visible edges at some exposure values
+    print 'DIR:', dir()
+    for n in [24, 21, 4]:
+        if debug_plot:
+            plt.semilogy(vals[n], l[n], 'bx' )
+        vals = np.delete(vals, n)
+        l = np.delete(l, n)
+
+    # modify equal values to get an invertable monotone function
+    force_monotone = np.linspace(0,0.00001,len(vals))
+    vals = vals+force_monotone
+
+    #interp = interpolate.Akima1DInterpolator(vals, l)
+    interp = interpolate.PchipInterpolator(vals, l, extrapolate=False)
+
+    if debug_plot:
+        plt.semilogy(vals, l, 'b+' )
+        x = np.linspace(0., 1.0, 4096)
+        y = interp(x)
+        plt.semilogy(x, y, 'r.', ms=1 )
+        x = np.linspace(0., 1.0, 256)
+        y = interp(x)
+        plt.semilogy(x, y, 'bo', ms=2 )
+        plt.show()
+
+    return interp
+
+
+def create_gh4_style(gamut,
+                     transfer_function,
+                     lut_directory,
+                     lut_resolution_1d,
+                     aliases,
+                     style=None):
+    """
+    Creates colorspace covering the conversion from GH4 picture styles 
+    to ACES based on empirical data
+
+    Parameters
+    ----------
+    gamut : str
+        The name of the encoding gamut to use.
+    transfer_function : str
+        The name of the transfer function to use
+    lut_directory : str or unicode 
+        The directory to use when generating LUTs
+    lut_resolution_1d : int
+        The resolution of generated 1D LUTs
+    aliases : list of str
+        Aliases for this colorspace
+
+    Returns
+    -------
+    ColorSpace
+    A ColorSpace container class referencing the LUTs, matrices 
+    and identifying information for the requested colorspace.
+    """
+
+    name = 'GH4-%s - GH4-%s-Gamut' % (transfer_function, gamut)
+    if transfer_function == '':
+        name = 'Linear - GH4-%s-Gamut' % gamut
+    if gamut == '':
+        name = 'Curve - GH4-%s' % transfer_function
+
+    cs = ColorSpace(name)
+    cs.description = name
+    cs.aliases = aliases
+    cs.equality_group = ''
+    cs.family = 'Input/Panasonic/GH4'
+    cs.is_data = False
+
+    # A linear space needs allocation variables
+    if transfer_function == '':
+        cs.allocation_type = ocio.Constants.ALLOCATION_LG2
+        cs.allocation_vars = [-8, 6, 0.00390625]
+
+    cs.to_reference_transforms = []
+
+    if transfer_function != '':
+        f = prepare_photostyle_to_linear(transfer_function)
+        
+        #data = array.array('f', '\0' * lut_resolution_1d * 4)
+        #for c in range(lut_resolution_1d):
+        #    data[c] = v_log_to_linear(float(c) / (lut_resolution_1d - 1))
+        dom = np.linspace(0.0, 1.0, lut_resolution_1d)
+        data = f(dom)
+
+        # constant extrapolation
+        data_min = np.nanmin(data)
+        data_max = np.nanmax(data)
+        inds = np.where(np.isnan(data))
+        for i in inds[0]:
+            if i < lut_resolution_1d/2:
+                data[i] = data_min
+                #data[i] = 0.0
+            else:
+                data[i] = data_max
+        
+        lut = 'gh4_%s_to_linear.spi1d' % transfer_function
+        genlut.write_SPI_1d(
+            os.path.join(lut_directory, lut),
+            0.0,
+            1.0,
+            data,
+            lut_resolution_1d,
+            1)
+
+        cs.to_reference_transforms.append({
+            'type': 'lutFile',
+            'path': lut,
+            'interpolation': 'linear',
+            'direction': 'forward'})
+
+    #
+    # matrix calculations based on:
+    # http://www.colour-science.org
+    #
+    
+    if gamut != '':
+
+        # Lin -> XYZ 
+        
+        if gamut == 'V-Log':
+            cs.to_reference_transforms.append({
+                'type': 'matrix',
+                # colour.V_GAMUT_COLOURSPACE.RGB_to_XYZ_matrix
+                'matrix': [0.679644,  0.152211,  0.1186 , 0.,
+                           0.260686,  0.774894, -0.03558, 0.,
+                           -0.00931 , -0.004612,  1.10298, 0.,
+                           0, 0, 0,  1.],
+                 'direction': 'forward'})
+        else:
+            cs.to_reference_transforms.append({
+                'type': 'matrix',
+                # colour.sRGB_COLOURSPACE.RGB_to_XYZ_matrix
+                'matrix': [0.41238656,  0.35759149,  0.18045049, 0.0,
+                           0.21263682,  0.71518298,  0.0721802 , 0.0,
+                           0.01933062,  0.11919716,  0.95037259, 0.0,
+                           0, 0, 0, 1.0],
+                'direction': 'forward'})
+            
+        # D65 -> D50 adaption
+        #
+        # colour.chromatic_adaptation_matrix_VonKries(
+        #    colour.xy_to_XYZ(c.sRGB_COLOURSPACE.whitepoint),
+        #    colour.xy_to_XYZ(colour.ILLUMINANTS[
+        #        'CIE 1931 2 Degree Standard Observer']['D50']), 'CAT02')
+        #
+        
+        cs.to_reference_transforms.append({
+            'type': 'matrix',
+            'matrix': [1.04251352,  0.03083262, -0.05276445, 0.0,
+                       0.0221519 ,  1.00187227, -0.02105432, 0.0,
+                       -0.00116356, -0.00341802,  0.76197255, 0.0,
+                       0, 0, 0, 1.0],
+            'direction': 'forward'})
+
+        if gamut.endswith('XYZ'):
+            cs.from_reference_transforms = []
+            return cs
+
+        if gamut.endswith('Calibrated'):
+            wb = [0.]*16
+            wb[0] = calibration_wb[style][0]
+            wb[5] = calibration_wb[style][1]
+            wb[10] = calibration_wb[style][2]
+            wb[15] = 1.0
+            cs.to_reference_transforms.append({
+                'type': 'matrix',
+                'matrix': wb,
+                'direction': 'forward'})
+            cs.to_reference_transforms.append({
+                'type': 'matrix',
+                'matrix': (calibration_matrices[style][0] + [0.0] +
+                           calibration_matrices[style][1] + [0.0] +
+                           calibration_matrices[style][2] + [0.0] +
+                           [0, 0, 0, 1.0]),
+                'direction': 'forward'})
+
+        # D50 -> ACES WP (~D60)
+
+        # colour.chromatic_adaptation_matrix_VonKries(
+        #   colour.xy_to_XYZ(colour.ILLUMINANTS[
+        #     'CIE 1931 2 Degree Standard Observer']['D50']),
+        #   colour.xy_to_XYZ(colour.V_GAMUT_COLOURSPACE.whitepoint),
+        #   'CAT02')
+        
+        cs.to_reference_transforms.append({
+            'type': 'matrix',
+            'matrix': [ 0.95991641, -0.02931741,  0.06566143, 0.0,
+                        -0.02119542,  0.99887268,  0.02613247, 0.0,
+                        0.00137075,  0.00443593,  1.31260073, 0.0,
+                        0, 0, 0, 1.0],
+            'direction': 'forward'})
+
+        
+        # XYZ -> ACES
+
+        # colour.ACES_2065_1_COLOURSPACE.XYZ_to_RGB_matrix
+        cs.to_reference_transforms.append({
+            'type': 'matrix',
+            'matrix': [1.04981102e+00,   0.00000000e+00,  -9.74845410e-05, 0.,
+                       -4.95903023e-01,  1.37331305e+00,   9.82400365e-02, 0.,
+                       0.00000000e+00,   0.00000000e+00,   9.91252022e-01, 0.,
+                       0, 0, 0,  1.],
+            'direction': 'forward'})
+
+        #
+        # matrix given py panasonic / calculated by nuke
+        # for one step transformation:
+        #
+        
+        # if gamut == 'V-Log':
+        #         cs.to_reference_transforms.append({
+        #             'type': 'matrix',
+        #             'matrix': [0.724383  ,  0.166748 ,  0.108497, 0.,
+        #                        0.021354  ,  0.985138 , -0.006319, 0.,
+        #                        -0.009234 , -0.001043 ,  1.010273, 0.,
+        #                        0, 0, 0,  1.],
+        #              'direction': 'forward'})
+        # elif gamut != '':
+        #         cs.to_reference_transforms.append({
+        #             'type': 'matrix',
+        #             'matrix': [0.43964687, 0.38298151, 0.17737158, 0.0,
+        #                        0.08978026, 0.81343973, 0.09677973, 0.0,
+        #                        0.01754464, 0.11155707, 0.87089837, 0.0,
+        #                        0, 0, 0, 1.0],
+        #             'direction': 'forward'})
+
+    cs.from_reference_transforms = []
+    return cs
+
+
+def create_colorspaces(lut_directory, lut_resolution_1d):
+    """
+    Generates the colorspace conversions.
+
+    Parameters
+    ----------
+    lut_directory : str or unicode 
+        The directory to use when generating LUTs
+    lut_resolution_1d : int
+        The resolution of generated 1D LUTs
+
+    Returns
+    -------
+    list
+         A list of colorspaces for Panasonic cameras and encodings 
+    """
+    
+    colorspaces = []
+    
+    styles_sort = styles.keys()
+    styles_sort.sort()
+    
+    for style in styles_sort: 
+
+        # Full conversion
+        style_1 = create_gh4_style(
+            style,
+            style, 
+            lut_directory,
+            lut_resolution_1d,
+            ['gh4_%s_gh4_gamut' % style.lower().replace('-','_')])
+        colorspaces.append(style_1)
+
+        # Linearization Only
+        style_2 = create_gh4_style(
+            '',
+            style,
+            lut_directory,
+            lut_resolution_1d,
+            ['crv_gh4_%s' % style.lower().replace('-','_')])
+        colorspaces.append(style_2)
+
+        # Linearization and XYZ conversion
+        style_3 = create_gh4_style(
+            '%s-XYZ' % style,
+            style,
+            lut_directory,
+            lut_resolution_1d,
+            ['gh4_%s_xyz' % style.lower().replace('-','_')])
+        colorspaces.append(style_3)
+
+        # Primaries Only
+        style_4 = create_gh4_style(
+            style,
+            '',
+            lut_directory,
+            lut_resolution_1d,
+            ['lin_gh4_%s_gamut' % style.lower().replace('-','_')])
+        colorspaces.append(style_4)
+
+        if style != 'BW':
+            
+            # Full conversion calibrated
+            style_5 = create_gh4_style(
+                '%s-Calibrated' % style,
+                style, 
+                lut_directory,
+                lut_resolution_1d,
+                ['gh4_%s_gh4_calibrated_gamut'% style.lower().replace('-','_')],
+                style)
+            colorspaces.append(style_5)
+
+            # Primaries Only calibrated
+            style_6 = create_gh4_style(
+                '%s-Calibrated' % style,
+                '',
+                lut_directory,
+                lut_resolution_1d,
+                ['lin_gh4_%s_calibrated_gamut'%style.lower().replace('-','_')],
+                style)
+            colorspaces.append(style_6)
+
+            
+    return colorspaces
+