set cut_paste_input [stack 0] CMSTestPattern { inputs 0 name CMSTestPattern1 selected true xpos -62 ypos -287 } Group { name LogConvert_Davinci_Intermediate1 selected true xpos -62 ypos -160 addUserKnob {20 Intermediate_tab l Intermediate} addUserKnob {4 operation M {log2lin lin2log}} } Input { inputs 0 name Input xpos -40 ypos -10 } Dot { name Dot1 xpos -6 ypos 114 } set N324fa000 [stack 0] Expression { expr0 r<=DI_LIN_CUT?r*DI_M:(log(r+DI_A)/log(2)+DI_B)*DI_C expr1 g<=DI_LIN_CUT?g*DI_M:(log(g+DI_A)/log(2)+DI_B)*DI_C expr2 b<=DI_LIN_CUT?b*DI_M:(log(b+DI_A)/log(2)+DI_B)*DI_C channel3 none name lin2log xpos 80 ypos 110 addUserKnob {20 Params} addUserKnob {7 DI_A} DI_A 0.0075 addUserKnob {7 DI_B} DI_B 7 addUserKnob {7 DI_C} DI_C 0.07329248 addUserKnob {7 DI_M} DI_M 10.44426855 addUserKnob {7 DI_LIN_CUT} DI_LIN_CUT 0.00262409 addUserKnob {7 DI_LOG_CUT} DI_LOG_CUT 0.02740668 } push $N324fa000 Expression { expr0 r<=DI_LOG_CUT?r/DI_M:pow(2.0,(r/DI_C)-DI_B)-DI_A expr1 g<=DI_LOG_CUT?g/DI_M:pow(2.0,(g/DI_C)-DI_B)-DI_A expr2 b<=DI_LOG_CUT?b/DI_M:pow(2.0,(b/DI_C)-DI_B)-DI_A channel3 none name log2lin selected true xpos -160 ypos 110 addUserKnob {20 Params} addUserKnob {7 DI_A} DI_A 0.0075 addUserKnob {7 DI_B} DI_B 7 addUserKnob {7 DI_C} DI_C 0.07329248 addUserKnob {7 DI_M} DI_M 10.44426855 addUserKnob {7 DI_LIN_CUT} DI_LIN_CUT 0.00262409 addUserKnob {7 DI_LOG_CUT} DI_LOG_CUT 0.02740668 } Switch { inputs 2 which {{parent.operation}} name Operation xpos -40 ypos 190 } Output { name Output1 xpos -40 ypos 310 } end_group Group { name GamutConvert tile_color 0xcc804eff label "Rec709 to XYZ D65" selected true xpos -62 ypos -76 addUserKnob {20 Params} addUserKnob {35 presets_src l src M {"gamut/ACES AP0" "knobs this \{src_name \"ACES AP0\" src \"0.7347 0.2653 0.0 1.0 0.0001 -0.077 0.32168 0.33767\"\}" "gamut/ACES AP1" "knobs this \{src_name \"ACES AP1\" src \"0.713 0.293 0.165 0.83 0.128 0.044 0.32168 0.33767\"\}" "gamut/Filmlight E-Gamut" "knobs this \{src_name \"Filmlight E-Gamut\" src \"0.8 0.3177 0.18 0.9 0.065 -0.0805 0.3127 0.329\"\}" "gamut/DaVinci Wide Gamut" "knobs this \{src_name \"DaVinci Wide Gamut\" src \"0.8 0.313 0.1682 0.9877 0.079 -0.1155 0.3127 0.329\"\}" gamut/Rec709 "knobs this \{src_name \"Rec709\" src \"0.64 0.33 0.3 0.6 0.15 0.06 0.3127 0.329\"\}" gamut/Rec2020 "knobs this \{src_name \"Rec2020\" src \"0.708 0.292 0.17 0.797 0.131 0.046 0.3127 0.329\"\}" gamut/P3D60 "knobs this \{src_name \"P3D60\" src \"0.68 0.32 0.265 0.69 0.15 0.06 0.321626 0.337737\"\}" gamut/P3D65 "knobs this \{src_name \"P3D65\" src \"0.68 0.32 0.265 0.69 0.15 0.06 0.3127 0.329\"\}" gamut/P3DCI "knobs this \{src_name \"P3DCI\" src \"0.68 0.32 0.265 0.69 0.15 0.06 0.314 0.351\"\}" "gamut/Arri Wide Gamut" "knobs this \{src_name \"Arri Wide Gamut\" src \"0.684 0.313 0.221 0.848 0.0861 -0.102 0.3127 0.329\"\}" "gamut/Arri Wide Gamut 4" "knobs this \{src_name \"Arri Wide Gamut 4\" src \"0.7347 0.2653 0.1424 0.8576 0.0991 -0.0308 0.3127 0.329\"\}" "gamut/RED Wide Gamut RGB" "knobs this \{src_name \"RED Wide Gamut RGB\" src \"0.780308 0.304253 0.121595 1.493994 0.095612 -0.084589 0.3127 0.329\"\}" "gamut/GoPro Protune Native" "knobs this \{src_name \"GoPro Protune Native\" src \"0.69848046 0.19302645 0.32955538 1.02459662 0.10844263 -0.03467857 0.3127 0.329\"\}" "gamut/Canon Cinema Gamut" "knobs this \{src_name \"Canon Cinema Gamut\" src \"0.74 0.27 0.17 1.14 0.08 -0.1 0.3127 0.329\"\}" "gamut/Sony SGamut3" "knobs this \{src_name \"Sony SGamut3\" src \"0.73 0.28 0.14 0.855 0.1 -0.05 0.3127 0.329\"\}" "gamut/Sony SGamut3.Cine" "knobs this \{src_name \"Sony SGamut3.Cine\" src \"0.766 0.275 0.225 0.8 0.089 -0.087 0.3127 0.329\"\}" "gamut/Panasonic V-Gamut" "knobs this \{src_name \"Panasonic V-Gamut\" src \"0.73 0.28 0.165 0.84 0.1 -0.03 0.3127 0.329\"\}" "gamut/DJI D-Gamut" "knobs this \{src_name \"DJI D-Gamut\" src \"0.71 0.31 0.21 0.88 0.09 -0.08 0.3127 0.329\"\}" "gamut/Fujifilm F-Gamut" "knobs this \{src_name \"Fujifilm F-Gamut\" src \"0.708 0.292 0.17 0.797 0.131 0.046 0.3127 0.329\"\}" "gamut/Nikon N-Gamut" "knobs this \{src_name \"Nikon N-Gamut\" src \"0.708 0.292 0.17 0.797 0.131 0.046 0.3127 0.329\"\}" "gamut/Blackmagic Wide Gamut" "knobs this \{src_name \"Blackmagic Wide Gamut\" src \"0.7177215 0.3171181 0.228041 0.861569 0.1005841 -0.0820452 0.3127 0.329\"\}" "gamut/Adobe RGB" "knobs this \{src_name \"Adobe RGB\" src \"0.64 0.33 0.21 0.71 0.15 0.06 0.3127 0.329\"\}" "gamut/Adobe WideGamutRGB" "knobs this \{src_name \"Adobe WideGamutRGB\" src \"0.7347 0.2653 0.1152 0.8264 0.1566 0.0177 0.3457 0.3585\"\}" gamut/ProPhotoRGB "knobs this \{src_name \"ProPhotoRGB\" src \"0.734699 0.265301 0.159597 0.840403 0.036598 0.000105 0.345704 0.358540\"\}"}} addUserKnob {1 src_name l "" -STARTLINE} src_name Rec709 addUserKnob {1 src l " " t "source gamut chromaticities: rgbw"} src "0.64 0.33 0.3 0.6 0.15 0.06 0.3127 0.329" addUserKnob {35 presets_dst l dst M {gamut/XYZ "knobs this \{dst_name \"XYZ\" dst \"\"\}" "gamut/XYZ D65" "knobs this \{dst_name \"XYZ D65\" dst \"1 0 0 1 0 0 0.3127 0.329\"\}" "gamut/ACES AP0" "knobs this \{dst_name \"ACES AP0\" dst \"0.7347 0.2653 0.0 1.0 0.0001 -0.077 0.32168 0.33767\"\}" "gamut/ACES AP1" "knobs this \{dst_name \"ACES AP1\" dst \"0.713 0.293 0.165 0.83 0.128 0.044 0.32168 0.33767\"\}" "gamut/Filmlight E-Gamut" "knobs this \{dst_name \"Filmlight E-Gamut\" dst \"0.8 0.3177 0.18 0.9 0.065 -0.0805 0.3127 0.329\"\}" "gamut/DaVinci Wide Gamut" "knobs this \{dst_name \"DaVinci Wide Gamut\" dst \"0.8 0.313 0.1682 0.9877 0.079 -0.1155 0.3127 0.329\"\}" gamut/Rec709 "knobs this \{dst_name \"Rec709\" dst \"0.64 0.33 0.3 0.6 0.15 0.06 0.3127 0.329\"\}" gamut/Rec2020 "knobs this \{dst_name \"Rec2020\" dst \"0.708 0.292 0.17 0.797 0.131 0.046 0.3127 0.329\"\}" gamut/P3D60 "knobs this \{dst_name \"P3D60\" dst \"0.68 0.32 0.265 0.69 0.15 0.06 0.321626 0.337737\"\}" gamut/P3D65 "knobs this \{dst_name \"P3D65\" dst \"0.68 0.32 0.265 0.69 0.15 0.06 0.3127 0.329\"\}" gamut/P3DCI "knobs this \{dst_name \"P3DCI\" dst \"0.68 0.32 0.265 0.69 0.15 0.06 0.314 0.351\"\}" "gamut/Arri Wide Gamut" "knobs this \{dst_name \"Arri Wide Gamut\" dst \"0.684 0.313 0.221 0.848 0.0861 -0.102 0.3127 0.329\"\}" "gamut/Arri Wide Gamut 4" "knobs this \{dst_name \"Arri Wide Gamut 4\" dst \"0.7347 0.2653 0.1424 0.8576 0.0991 -0.0308 0.3127 0.329\"\}" "gamut/RED Wide Gamut RGB" "knobs this \{dst_name \"RED Wide Gamut RGB\" dst \"0.780308 0.304253 0.121595 1.493994 0.095612 -0.084589 0.3127 0.329\"\}" "gamut/GoPro Protune Native" "knobs this \{dst_name \"GoPro Protune Native\" dst \"0.69848046 0.19302645 0.32955538 1.02459662 0.10844263 -0.03467857 0.3127 0.329\"\}" "gamut/Canon Cinema Gamut" "knobs this \{dst_name \"Canon Cinema Gamut\" dst \"0.74 0.27 0.17 1.14 0.08 -0.1 0.3127 0.329\"\}" "gamut/Sony SGamut3" "knobs this \{dst_name \"Sony SGamut3\" dst \"0.73 0.28 0.14 0.855 0.1 -0.05 0.3127 0.329\"\}" "gamut/Sony SGamut3.Cine" "knobs this \{dst_name \"Sony SGamut3.Cine\" dst \"0.766 0.275 0.225 0.8 0.089 -0.087 0.3127 0.329\"\}" "gamut/Panasonic V-Gamut" "knobs this \{dst_name \"Panasonic V-Gamut\" dst \"0.73 0.28 0.165 0.84 0.1 -0.03 0.3127 0.329\"\}" "gamut/DJI D-Gamut" "knobs this \{dst_name \"DJI D-Gamut\" dst \"0.71 0.31 0.21 0.88 0.09 -0.08 0.3127 0.329\"\}" "gamut/Fujifilm F-Gamut" "knobs this \{dst_name \"Fujifilm F-Gamut\" dst \"0.708 0.292 0.17 0.797 0.131 0.046 0.3127 0.329\"\}" "gamut/Nikon N-Gamut" "knobs this \{dst_name \"Nikon N-Gamut\" dst \"0.708 0.292 0.17 0.797 0.131 0.046 0.3127 0.329\"\}" "gamut/Blackmagic Wide Gamut" "knobs this \{dst_name \"Blackmagic Wide Gamut\" dst \"0.7177215 0.3171181 0.228041 0.861569 0.1005841 -0.0820452 0.3127 0.329\"\}" "gamut/Adobe RGB" "knobs this \{dst_name \"Adobe RGB\" dst \"0.64 0.33 0.21 0.71 0.15 0.06 0.3127 0.329\"\}" "gamut/Adobe WideGamutRGB" "knobs this \{dst_name \"Adobe WideGamutRGB\" dst \"0.7347 0.2653 0.1152 0.8264 0.1566 0.0177 0.3457 0.3585\"\}" gamut/ProPhotoRGB "knobs this \{dst_name \"ProPhotoRGB\" dst \"0.734699 0.265301 0.159597 0.840403 0.036598 0.000105 0.345704 0.358540\"\}"}} addUserKnob {1 dst_name l "" -STARTLINE} dst_name "XYZ D65" addUserKnob {1 dst l " " t "destination gamut chromaticities: rgbw"} dst "1 0 0 1 0 0 0.3127 0.329" addUserKnob {83 cat M {bradford cat02 none}} cat cat02 addUserKnob {26 ""} addUserKnob {6 invert t "invert matrix direction" +STARTLINE} addUserKnob {22 calc l Calculate t "Calculate a 3x3 matrix to convert from source gamut to destination gamut, with chromatic adaptation transform if whitepoints are different." -STARTLINE T "import nuke\n\ndef matmul(m0, m1):\n # multiply two square matrices\n return \[\[sum(a*b for a,b in zip(r, c)) for c in zip(*m1)] for r in m0]\n\ndef vdot(m, v):\n # multiply AxA matrix by Ax1 vector\n return \[sum(x*y for x,y in zip(r,v)) for r in m]\n\ndef transpose(m):\n # transpose matrix m by swapping rows and cols\n return \[list(r) for r in zip(*m)]\n\ndef zeros(l):\n # create square matrix of size l\n return \[\[0.0]*l for i in range(l)]\n\ndef diag(m, v):\n # set diagonal row of matrix m to vector v or float v\n if isinstance(v, float): \n v = \[v]*len(m)\n for p in range(len(m)):\n m\[p]\[p] = v\[p]\n return m\n\ndef identity(l):\n # return identity matrix of size l\n return diag(zeros(3), 1.0)\n\ndef det(m):\n # calculate determinant of 3x3 matrix m\n return m\[0]\[0]*(m\[1]\[1]*m\[2]\[2]-m\[2]\[1]*m\[1]\[2])-m\[0]\[1]*(m\[1]\[0]*m\[2]\[2]-m\[1]\[2]*m\[2]\[0])+m\[0]\[2]*(m\[1]\[0]*m\[2]\[1]-m\[1]\[1]*m\[2]\[0])\n\ndef inv(m):\n # invert 3x3 matrix m\n d = det(m)\n if d == 0.0:\n return m\n i = zeros(3)\n i\[0]\[0] = (m\[1]\[1]*m\[2]\[2]-m\[2]\[1]*m\[1]\[2])/d\n i\[0]\[1] = (m\[0]\[2]*m\[2]\[1]-m\[0]\[1]*m\[2]\[2])/d\n i\[0]\[2] = (m\[0]\[1]*m\[1]\[2]-m\[0]\[2]*m\[1]\[1])/d\n i\[1]\[0] = (m\[1]\[2]*m\[2]\[0]-m\[1]\[0]*m\[2]\[2])/d\n i\[1]\[1] = (m\[0]\[0]*m\[2]\[2]-m\[0]\[2]*m\[2]\[0])/d\n i\[1]\[2] = (m\[1]\[0]*m\[0]\[2]-m\[0]\[0]*m\[1]\[2])/d\n i\[2]\[0] = (m\[1]\[0]*m\[2]\[1]-m\[2]\[0]*m\[1]\[1])/d\n i\[2]\[1] = (m\[2]\[0]*m\[0]\[1]-m\[0]\[0]*m\[2]\[1])/d\n i\[2]\[2] = (m\[0]\[0]*m\[1]\[1]-m\[1]\[0]*m\[0]\[1])/d\n return i\n\ndef flatten(l):\n # flatten multidimensional list into one dimensional list\n return sum(l, \[])\n\ndef npm(ch):\n # Calculate the Normalized Primaries Matrix for the specified chromaticities\n # Adapted from SMPTE Recommended Practice - Derivation of Basic Television Color Equations\n # http://doi.org/10.5594/S9781614821915\n if len(ch) == 8: # handle flat list\n ch = \[\[ch\[0], ch\[1]], \[ch\[2], ch\[3]], \[ch\[4], ch\[5]], \[ch\[6], ch\[7]]]\n for c in ch:\n c.append(1.0-c\[0]-c\[1]) \n P = transpose(\[ch\[0], ch\[1], ch\[2]])\n W = \[ch\[3]\[0] / ch\[3]\[1], 1.0, ch\[3]\[2] / ch\[3]\[1]]\n C = vdot(inv(P), W)\n C = diag(zeros(3), C)\n return matmul(P, C)\n\ndef cat(ws, wd, method='bradford'):\n # Calculate a von Kries style chromatic adaptation transform matrix given xy chromaticities for src and dst white\n # Source: Mark D. Fairchild - 2013 - Color Appearance Models Third Edition p. 181-186\n # Source: Bruce Lindbloom - Chromatic Adaptation - http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html\n if ws == wd: # src and dst are equal, nothing to do\n return identity(3)\n if method == 'bradford':\n mcat = \[\[0.8951, 0.2664, -0.1614], \[-0.7502, 1.7135, 0.0367], \[0.0389, -0.0685, 1.0296]]\n elif method == 'cat02':\n mcat = \[\[0.7328, 0.4296, -0.1624], \[-0.7036, 1.6975, 0.0061], \[0.003, 0.0136, 0.9834]]\n else:\n mcat = identity(3)\n \n def xy_to_XYZ(xy):\n # convert xy chromaticity to XYZ tristimulus with Y=1.0\n return \[xy\[0]/xy\[1], 1.0, (1.0-xy\[0]-xy\[1])/xy\[1]]\n \n sXYZ = xy_to_XYZ(ws)\n dXYZ = xy_to_XYZ(wd)\n \n s_cone_mtx = vdot(mcat, sXYZ)\n d_cone_mtx = vdot(mcat, dXYZ)\n \n smat = diag(zeros(3), \[a/b for a,b in zip(d_cone_mtx, s_cone_mtx)])\n nmtx = matmul(inv(mcat), smat)\n return matmul(nmtx, mcat)\n\ndef wp(ch):\n # return whitepoint of chromaticities array\n return ch\[-2:]\n\ndef is_xyz(ch):\n # test if ch is XYZ\n prims = ch\[:-2]\n return True if prims == \[1, 0, 0, 1, 0, 0] else False\n\ndef calc_mtx(ch0, ch1, method='bradford'):\n # calculate 3x3 matrix to convert gamut ch0 to ch1, with cat\n if not ch1:\n return npm(ch0)\n rgb0_to_xyz = identity(3) if is_xyz(ch0) else npm(ch0)\n rgb1_to_xyz = identity(3) if is_xyz(ch1) else npm(ch1)\n xyz_to_cat = cat(wp(ch0), wp(ch1), method=method)\n rgb0_to_cat = matmul(xyz_to_cat, rgb0_to_xyz)\n rgb0_to_rgb1 = matmul(inv(rgb1_to_xyz), rgb0_to_cat)\n return rgb0_to_rgb1\n\n\n\nnode = nuke.thisNode()\n\ndef get_ch(k):\n # get chromaticities from knob name k\n ch_str = node\[k].getValue()\n if ' ' in ch_str:\n chs = ch_str.split(' ')\n if not len(chs) == 8:\n print('Error: need 8 space-separated float xy coordinates for RGBW')\n return None\n else:\n ch = list()\n for c in chs:\n try:\n ch.append(float(c))\n except ValueError:\n print('Error: could not convert \{0\} to float'.format(c))\n return None\n return ch\n else:\n return None\n\ndef print_mtx(mtx):\n label = node\['label'].getValue()\n \n # round to 12 digits of precision\n mtx_str = \[format(round(v, 12), '.12g') for v in flatten(mtx)]\n \n # print some useful representations of the matrix\n # an ocio matrixtransform\n mtx_str_pad = mtx_str\[0:3]+\['0']+mtx_str\[3:6]+\['0']+mtx_str\[6:9]+\['0']*4+\['1']\n mtx_string = ', '.join(map(str, mtx_str_pad))\n ocio_matrixtransform_string = '! \{\{matrix: \[\{0\}]\}\}'.format(mtx_string)\n print('\{0\} - OCIO MatrixTransform\\n\{1\}\\n'.format(label, ocio_matrixtransform_string))\n \n # an spimtx file\n spimtx_string = '\{0\} 0\\n\{1\} 0\\n\{2\} 0'.format(\n ' '.join(mtx_str\[0:3]),\n ' '.join(mtx_str\[3:6]),\n ' '.join(mtx_str\[6:9]),\n )\n print('\{0\}.spimtx\\n\{1\}\\n'.format(label, spimtx_string))\n\ndef reset():\n # reset matrix\n node\['matrix'].setValue(flatten(identity(3)))\n \ndef calc():\n # gather node info\n src_ch = get_ch('src')\n if not src_ch:\n reset()\n return\n dst_ch = get_ch('dst')\n cat_method = node\['cat'].value()\n mtx = calc_mtx(src_ch, dst_ch, method=cat_method)\n if node\['invert'].getValue():\n mtx = inv(mtx)\n node\['matrix'].setValue(flatten(mtx))\n label = '\{1\} to \{0\}' if node\['invert'].getValue() else '\{0\} to \{1\}'\n label = label.format(node\['src_name'].getValue(), node\['dst_name'].getValue())\n node\['label'].setValue(label)\n if node\['print_mtx'].getValue():\n print_mtx(mtx)\n \ncalc()"} addUserKnob {6 print_mtx l print t "print a few useful representations of the matrix ni the script editor, for OCIO MatrixTransforms, spimtx files, etc" -STARTLINE} addUserKnob {41 matrix T ColorMatrix.matrix} addUserKnob {22 create_colormatrix l "Create ColorMatrix" t "create ColorMatrix node using calculated 3x3 matrix" -STARTLINE T "from __future__ import with_statement\nnode = nuke.thisNode()\nwith nuke.root():\n nukescripts.clear_selection_recursive()\n m = nuke.createNode('ColorMatrix')\n m.setXYpos(node.xpos()-120, node.ypos())\n m\['matrix'].setValue(node\['matrix'].getValue())\n m\['label'].setValue(node\['label'].getValue())"} } Input { inputs 0 name Input xpos -40 ypos 206 } ColorMatrix { matrix { {0.4123907993 0.3575843394 0.1804807884} {0.2126390059 0.7151686788 0.07219231536} {0.01933081872 0.1191947798 0.9505321522} } name ColorMatrix xpos -40 ypos 255 } Output { name Output xpos -40 ypos 302 } end_group BlinkScript { recompileCount 1 ProgramGroup 1 KernelDescription "2 \"XYZ_JMh\" iterate pixelWise 500da4dd91f3554843ad7c1db7ae340971627ae93f07851b537464e383eadfe4 2 \"src\" Read Point \"dst\" Write Point 17 \"invert\" Bool 1 AA== \"compressMode\" Bool 1 AA== \"linear_extension\" Bool 1 AA== \"XYZ_w_scaler\" Float 1 AAAAAA== \"L_A\" Float 1 AAAAAA== \"L_B\" Float 3 AAAAAAAAAAAAAAAAAAAAAA== \"inWhite\" Float 3 AAAAAAAAAAAAAAAAAAAAAA== \"surround\" Int 1 AAAAAA== \"surround_custom\" Float 3 AAAAAAAAAAAAAAAAAAAAAA== \"Y_b\" Float 1 AAAAAA== \"discountIlluminant\" Bool 1 AA== \"HK_mode\" Bool 1 AA== \"catDataSelection\" Int 1 AAAAAA== \"rxy\" Float 2 AAAAAAAAAAA= \"gxy\" Float 2 AAAAAAAAAAA= \"bxy\" Float 2 AAAAAAAAAAA= \"wxy\" Float 2 AAAAAAAAAAA= 17 \"invert\" 1 1 \"compressMode\" 1 1 \"linear_extension\" 1 1 \"XYZ_w_scaler\" 1 1 \"L_A\" 1 1 \"L_B\" 3 1 \"inWhite\" 3 1 \"surround\" 1 1 \"surround_custom\" 3 1 \"Y_b\" 1 1 \"discountIlluminant\" 1 1 \"HK_mode\" 1 1 \"catDataSelection\" 1 1 \"rxy\" 2 1 \"gxy\" 2 1 \"bxy\" 2 1 \"wxy\" 2 1 2 \"CAT_CAT16\" Float 9 1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA \"panlrcm\" Float 9 1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" kernelSource "kernel XYZ_JMh : ImageComputationKernel\n\{\n Image src; // the input image\n Image dst; // the output image\n\n param:\n bool invert;\n bool compressMode;\n bool linear_extension;\n float XYZ_w_scaler;\n float L_A;\n float3 L_B;\n float3 inWhite;\n int surround;\n float3 surround_custom;\n float Y_b;\n bool discountIlluminant;\n bool HK_mode;\n \n // 0 = Stock CAT16\n // 1 = Thomas's custom primaries\n // 2 = live from params below\n int catDataSelection; // original vs modified CAT16 matrix\n // xy coordintes for custom CAT matrix\n float2 rxy;\n float2 gxy;\n float2 bxy;\n float2 wxy;\n\n local:\n float3x3 CAT_CAT16;\n float3x3 panlrcm;\n\n void define() \{\n\n \}\n\n // multiplies a 3D vector with a 3x3 matrix\n float3 vector_dot( float3x3 m, float3 v)\n \{\n float3 r = 1.0f;\n for(int c = 0; c<3; c++)\n \{\n r\[c] = m\[c]\[0]*v.x + m\[c]\[1]*v.y + m\[c]\[2]*v.z;\n \}\n\n return r;\n \}\n\n float degree_of_adaptation(float F, float L_A )\n \{\n float D = F * (1.0f - (1.0f / 3.6f) * exp((-L_A - 42.0f) / 92.0f));\n\n return D;\n \}\n\n float clip(float x, float a, float b)\n \{\n return max(a, min(x, b));\n \}\n\n // \"safe\" power function to avoid NANs or INFs when taking a fractional power of a negative base\n // this one initially returned -pow(abs(b), e) for negative b\n // but this ended up producing undesirable results in some cases\n // so now it just returns 0.0 instead\n float spow( float base, float exponent )\n \{\n float a = base;\n float b = exponent;\n\n if(base < 0.0f && exponent != floor(exponent) )\n \{\n// return 0.0f;\n -pow(-base, exponent); \n \}\n else\n \{\n return pow(base, exponent); \n \}\n \}\n\n // \"safe\" div\n float sdiv( float a, float b )\n \{\n if(b == 0.0f)\n \{\n return 0.0f;\n \}\n else\n \{\n return a / b;\n \}\n \}\n\n float3 float3abs( float3 a )\n \{\n return fabs(a);\n \}\n\n float3 float3spow( float3 base, float exponent )\n \{\n return float3(spow(base.x, exponent), spow(base.y, exponent), spow(base.z, exponent));\n \}\n\n float3 float3sign( float3 v )\n \{\n return float3(sign(v.x), sign(v.y), sign(v.z));\n \}\n\n float3 compress(float3 xyz)\n \{\n \n float x = xyz.x;\n float y = xyz.y;\n float z = xyz.z;\n \n float C = (x+y+z)/3;\n if (C == 0.0f)\n return float3(x,y,z);\n\n float R = sqrt(spow((x-C),2) + spow((y-C),2) + spow((z-C),2)) ;\n // np.sqrt(2/3)\n // 0.816496580927726\n R = R * 0.816496580927726f;\n \n if (R != 0.0f)\n \{\n x = (x-C)/R ;\n y = (y-C)/R ;\n z = (z-C)/R ;\n \}\n \n float r = R/C ;\n float s = -min(x, min(y, z));\n \n float t = 0.0f;\n if (r != 0.0f)\n \{\n t = (0.5f+spow((spow((s-0.5f),2) + spow((sqrt(4.0f/spow(r,2)+1.0f)-1.0f),2)/4.0f),0.5f));\n if (t == 0.0f)\n return float3(xyz.x,xyz.y,xyz.z);\n t = 1.0f/t;\n \}\n \n x = C*x*t + C ;\n y = C*y*t + C ;\n z = C*z*t + C ;\n \n return float3(x,y,z);\n \}\n\nfloat3 uncompress(float3 xyz)\n\{\n\n \n float x = xyz.x;\n float y = xyz.y;\n float z = xyz.z;\n \n float C = (x+y+z)*(1.0f/3.0f) ;\n if (C == 0.0f)\n return float3(x,y,z);\n\n float R = sqrt(spow((x-C),2) + spow((y-C),2) + spow((z-C),2));\n // np.sqrt(2/3)\n // 0.816496580927726\n R = R * 0.816496580927726f;\n \n if (R != 0.0f)\n \{\n x = (x-C)/R ;\n y = (y-C)/R ;\n z = (z-C)/R ;\n \}\n\n float t = R/C ;\n float s = -min(x, min(y, z));\n \n float r = 0.0f;\n if (t != 0.0f)\n \{\n r = sqrt(spow((2*sqrt(spow((1.0f/t-0.5f),2)-spow((s-0.5f),2))+1.0f),2)-1.0f);\n if (r == 0.0f)\n return float3(xyz.x,xyz.y,xyz.z);\n r = 2.0f/r;\n \}\n \n x = C*x*r + C ;\n y = C*y*r + C ;\n z = C*z*r + C ;\n \n return float3(x,y,z);\n\}\n\n float3 post_adaptation_non_linear_response_compression_forward(float3 RGB, float F_L)\n \{\n float3 F_L_RGB = float3spow(F_L * float3abs(RGB) / 100.0f, 0.42f);\n float3 RGB_c = (400.0f * sign(RGB) * F_L_RGB) / (27.13f + F_L_RGB) + 0.1f;\n \n return RGB_c;\n \}\n\n float3 d_post_adaptation_non_linear_response_compression_forward( float3 RGB, float F_L)\n \{\n float3 F_L_RGB = float3spow(F_L * RGB / 100.0f, 0.42f);\n float F_L_100 = spow(F_L / 100.0f, 0.42f);\n \n float3 d_RGB_a = ( 400.0f * ((0.42f * 27.13f) * float3spow(RGB, -0.58f) * F_L_100)/ ( (F_L_RGB + 27.13f) * (F_L_RGB + 27.13f) ));\n\n\n return d_RGB_a;\n \}\n\n float3 post_adaptation_non_linear_response_compression_inverse(float3 RGB,float F_L)\n \{\n\n float3 RGB_p = (float3sign(RGB - 0.1f) * 100.0f / F_L * float3spow((27.13f * float3abs(RGB - 0.1f)) / (400.0f - float3abs(RGB - 0.1f)), 1.0f / 0.42f) );\n return RGB_p;\n \}\n\n // convert radians to degrees\n float degrees( float radians )\n \{\n return radians * 180.0f / PI;\n \}\n\n\n // convert degrees to radians\n float radians( float degrees )\n \{\n return degrees / 180.0f * PI;\n \}\n\n float mod(float a, float N)\n \{\n return a - N*floor(a/N);\n \} \n\n float hue_angle_dependency_Hellwig2022(float h)\n \{\n return float( \\\n -0.160f * cos(h) \\\n + 0.132f * cos(2.0f * h) \\\n - 0.405f * sin(h) \\\n + 0.080f * sin(2.0f * h) \\ \n + 0.792f \\\n );\n\n // return float( -0.160f * cos(h) + 0.132f * cos(2.0f * h) - 0.405f * sin(h) + 0.080f * sin(2.0f * h) + 0.792f );\n \}\n\n float3x3 RGBPrimsToXYZMatrix(float2 rxy, float2 gxy, float2 bxy, float2 wxy,float Y, bool direction)\n \{\n // # given r g b chromaticities and whitepoint, convert RGB colors to XYZ\n // # based on CtlColorSpace.cpp from the CTL source code : 77\n // # param: xy - dict of chromaticity xy coordinates: rxy: float2(x, y) etc\n // # param: Y - luminance of \"white\" - defaults to 1.0\n // # param: inverse - calculate XYZ to RGB instead\n\n float2 r = rxy;\n float2 g = gxy;\n float2 b = bxy;\n float2 w = wxy;\n\n float X = w.x * Y / w.y;\n float Z = (1 - w.x - w.y) * Y / w.y;\n\n // # Scale factors for matrix rows\n float d = r.x * (b.y - g.y) + b.x * (g.y - r.y) + g.x * (r.y - b.y);\n\n float Sr = (X * (b.y - g.y) - \\\n g.x * (Y * (b.y - 1.0f) + \\\n b.y * (X + Z)) + \\\n b.x * (Y * (g.y - 1.0f) + \\\n g.y * (X + Z))) / d ;\n \n float Sg = (X * (r.y - b.y) + \\\n r.x * (Y * (b.y - 1.0f) + \\\n b.y * (X + Z)) - \\\n b.x * (Y * (r.y - 1.0f) + \\\n r.y * (X + Z))) / d ;\n\n float Sb = (X * (g.y - r.y) - \\\n r.x * (Y * (g.y - 1.0f) + \\\n g.y * (X + Z)) + \\\n g.x * (Y * (r.y - 1.0f) + \\\n r.y * (X + Z))) / d ;\n\n // # Assemble the matrix\n float Mdata\[] =\n \{\n Sr * r.x, Sr * r.y, Sr * (1.0f - r.x - r.y),\n Sg * g.x, Sg * g.y, Sg * (1.0f - g.x - g.y),\n Sb * b.x, Sb * b.y, Sb * (1.0f - b.x - b.y),\n \};\n\n float MdataNukeOrder\[] = \{\n Mdata\[0], Mdata\[3], Mdata\[6],\n Mdata\[1], Mdata\[4], Mdata\[7],\n Mdata\[2], Mdata\[5], Mdata\[8],\n \};\n\n float3x3 newMatrix;\n newMatrix.setArray(MdataNukeOrder);\n\n // create inverse matrix\n float3x3 newMatrixInverse = newMatrix.invert();\n\n // return forward or inverse matrix\n if (direction == 0)\n \{\n return newMatrix;\n \}\n else if (direction == 1)\n \{\n return newMatrixInverse;\n \}\n \}\n\n float3 XYZ_to_Hellwig2022_JMh( float3 XYZ, float3 XYZ_w, float L_A, float Y_b, float3 surround, bool discountIlluminant, bool HK_mode)\n \{\n XYZ_w = XYZ_w * XYZ_w_scaler;\n float _X_w = XYZ_w.x ;\n float Y_w = XYZ_w.y ;\n float _Z_w = XYZ_w.z ;\n\n // # Step 0\n // # Converting *CIE XYZ* tristimulus values to sharpened *RGB* values.\n float3x3 MATRIX_16 = CAT_CAT16;\n float3 RGB_w = vector_dot(MATRIX_16, XYZ_w);\n\n // # Computing degree of adaptation :math:`D`.\n float D = clip(degree_of_adaptation(surround.x, L_A), 0.0f, 1.0f);\n if(discountIlluminant)\n \{\n D = 1.0f;\n \}\n\n\n // # Viewing conditions dependent parameters\n float k = 1.0f / (5.0f * L_A + 1.0f);\n float k4 = pow(k,4);\n float F_L = 0.2f * k4 * (5.0f * L_A) + 0.1f * pow((1.0f - k4), 2.0f) * spow(5.0f * L_A, 1.0f / 3.0f) ;\n float n = sdiv(Y_b, Y_w);\n float z = 1.48f + sqrt(n);\n\n float3 D_RGB = D * Y_w / RGB_w + 1.0f - D;\n float3 RGB_wc = D_RGB * RGB_w;\n \n // # Applying forward post-adaptation non-linear response compression.\n float3 F_L_RGB = float3spow(F_L * float3abs(RGB_wc) / 100.0f, 0.42f);\n\n // # Computing achromatic responses for the whitepoint.\n float3 RGB_aw = (400.0f * float3sign(RGB_wc) * F_L_RGB) / (27.13f + F_L_RGB) + 0.1f;\n \n\n // # Computing achromatic responses for the whitepoint.\n float R_aw = RGB_aw.x ;\n float G_aw = RGB_aw.y ;\n float B_aw = RGB_aw.z ;\n float A_w = 2.0f * R_aw + G_aw + 0.05f * B_aw - 0.305f;\n\n // # Step 1\n // # Converting *CIE XYZ* tristimulus values to sharpened *RGB* values.\n\n float3 RGB = vector_dot(MATRIX_16, XYZ);\n // float3 RGB = XYZ;\n\n // # Step 2\n float3 RGB_c = D_RGB * RGB;\n\n // # Step 3\n // # Applying forward post-adaptation non-linear response compression.\n\n if (compressMode)\n \{\n RGB_c = compress(RGB_c);\n \}\n\n float3 RGB_a = post_adaptation_non_linear_response_compression_forward(RGB_c, F_L);\n\n if (compressMode)\n \{\n RGB_a = uncompress(RGB_a);\n \}\n\n\n // # Step 3\n // # Applying forward post-adaptation non-linear response compression.\n if (linear_extension)\n \{\n\n float3 RGB_a_l = d_post_adaptation_non_linear_response_compression_forward(\n L_B, F_L\n ) * (\n RGB_c - L_B\n ) + post_adaptation_non_linear_response_compression_forward(\n L_B, F_L\n );\n \n RGB_a.x = RGB_c.x < L_B.x ? RGB_a_l.x: RGB_a.x;\n RGB_a.y = RGB_c.y < L_B.y ? RGB_a_l.y: RGB_a.y;\n RGB_a.z = RGB_c.z < L_B.z ? RGB_a_l.z: RGB_a.z; \n \}\n\n // # Step 4\n // # Converting to preliminary cartesian coordinates.\n float R_a = RGB_a.x ;\n float G_a = RGB_a.y ;\n float B_a = RGB_a.z ;\n float a = R_a - 12.0f * G_a / 11.0f + B_a / 11.0f;\n float b = (R_a + G_a - 2.0f * B_a) / 9.0f;\n\n // # Computing the *hue* angle :math:`h`.\n // Unclear why this isnt matching the python version.\n float h = mod(degrees(atan2(b, a)), 360.0f);\n\n \n\n // # Step 5\n // # Computing eccentricity factor *e_t*.\n float hr = radians(h);\n\n float _h = hr;\n float _2_h = 2.0f * hr;\n float _3_h = 3.0f * hr;\n float _4_h = 4.0f * hr;\n\n float e_t = (\n -0.0582f * cos(_h)\n - 0.0258f * cos(_2_h)\n - 0.1347f * cos(_3_h)\n + 0.0289f * cos(_4_h)\n - 0.1475f * sin(_h)\n - 0.0308f * sin(_2_h)\n + 0.0385f * sin(_3_h)\n + 0.0096f * sin(_4_h)\n + 1.0f\n );\n\n // # Step 6\n // # Computing achromatic responses for the stimulus.\n float R_a2 = RGB_a.x ;\n float G_a2 = RGB_a.y ;\n float B_a2 = RGB_a.z ;\n float A = 2.0f * R_a2 + G_a2 + 0.05f * B_a2 - 0.305f;\n\n // # Step 7\n // # Computing the correlate of *Lightness* :math:`J`.\n // with sdiv_mode():\n\n float J = 100.0f * spow(sdiv(A, A_w), surround.y * z);\n\n // # Step 8\n // # Computing the correlate of *brightness* :math:`Q`.\n // with sdiv_mode():\n float Q = (2.0f / float(surround.y)) * (J / 100.0f) * A_w;\n\n // # Step 9\n // # Computing the correlate of *colourfulness* :math:`M`.\n float M = 43.0f * surround.z * e_t * sqrt(a * a + b * b);\n\n // # Computing the correlate of *chroma* :math:`C`.\n float C = 35.0f * sdiv(M, A_w);\n\n float s = 100.0f * sdiv(M, Q);\n\n // # *Helmholtz–Kohlrausch* Effect Extension.\n float J_HK = J + hue_angle_dependency_Hellwig2022(hr) * spow(C, 0.587f);\n float Q_HK = (2.0f / surround.y) * (J_HK / 100.0f) * A_w ;\n \n if (HK_mode)\n \{\n return \{J_HK,M,h\};\n \}\n else\n \{\n return \{J,M,h\};\n \}\n \}\n\n float3 Hellwig2022_JMh_to_XYZ( float3 JMh, float3 XYZ_w, float L_A, float Y_b, float3 surround, bool discountIlluminant, bool HK_mode)\n \{\n float J = JMh.x;\n float M = JMh.y;\n float h = JMh.z;\n XYZ_w = XYZ_w * XYZ_w_scaler;\n\n float _X_w = XYZ_w.x;\n float Y_w = XYZ_w.y;\n float _Z_w = XYZ_w.z;\n\n // # Step 0\n // # Converting *CIE XYZ* tristimulus values to sharpened *RGB* values.\n float3x3 MATRIX_16 = CAT_CAT16;\n float3 RGB_w = vector_dot(MATRIX_16, XYZ_w);\n\n\n // # Computing degree of adaptation :math:`D`.\n float D = clip(degree_of_adaptation(surround.x, L_A), 0.0f, 1.0f);\n if(discountIlluminant)\n \{\n D = 1.0f;\n \}\n\n\n\n // # Viewing conditions dependent parameters\n float k = 1.0f / (5.0f * L_A + 1.0f);\n float k4 = pow(k,4);\n float F_L = 0.2f * k4 * (5.0f * L_A) + 0.1f * pow((1.0f - k4), 2.0f) * spow(5.0f * L_A, 1.0f / 3.0f) ;\n float n = sdiv(Y_b, Y_w);\n float z = 1.48f + sqrt(n);\n\n float3 D_RGB = D * Y_w / RGB_w + 1.0f - D;\n float3 RGB_wc = D_RGB * RGB_w;\n \n float3 F_L_RGB = float3spow(F_L * float3abs(RGB_wc) / 100.0f, 0.42f);\n\n float3 RGB_aw = (400.0f * float3sign(RGB_wc) * F_L_RGB) / (27.13f + F_L_RGB) + 0.1f;\n\n float R_aw = RGB_aw.x ;\n float G_aw = RGB_aw.y ;\n float B_aw = RGB_aw.z ;\n float A_w = 2.0f * R_aw + G_aw + 0.05f * B_aw - 0.305f;\n\n float hr = radians(h);\n\n float C = (M * 35.0f) / A_w;\n \n if (HK_mode)\n \{\n J = J - hue_angle_dependency_Hellwig2022(hr) * spow(C, 0.587f);\n \}\n\n float _h = hr;\n float _2_h = 2.0f * hr;\n float _3_h = 3.0f * hr;\n float _4_h = 4.0f * hr;\n \n float e_t = (\n -0.0582f * cos(_h)\n - 0.0258f * cos(_2_h)\n - 0.1347f * cos(_3_h)\n + 0.0289f * cos(_4_h)\n - 0.1475f * sin(_h)\n - 0.0308f * sin(_2_h)\n + 0.0385f * sin(_3_h)\n + 0.0096f * sin(_4_h)\n + 1.0f\n );\n\n float A = A_w * spow(J / 100.0f, 1.0f / (surround.y * z));\n\n\n float P_p_1 = 43.0f * surround.z * e_t;\n float P_p_2 = A;\n\n float gamma = M / P_p_1;\n \n float a = gamma * cos(hr);\n\n float b = gamma * sin(hr);\n\n float3 RGB_a = vector_dot(panlrcm, float3(P_p_2, a, b)) / 1403.0f;\n\n if (compressMode)\n \{\n RGB_a = compress(RGB_a);\n \}\n\n float3 RGB_c = post_adaptation_non_linear_response_compression_inverse(RGB_a + 0.1f, F_L);\n\n if (compressMode)\n \{\n RGB_c = uncompress(RGB_c);\n \}\n\n if (linear_extension)\n \{\n float3 RGB_c_l = ( RGB_a + 0.1f - post_adaptation_non_linear_response_compression_forward( L_B, F_L)) / (d_post_adaptation_non_linear_response_compression_forward( L_B, F_L)) + L_B;\n \n RGB_c.x = RGB_c.x < L_B.x ? RGB_c_l.x : RGB_c.x;\n RGB_c.y = RGB_c.y < L_B.y ? RGB_c_l.y : RGB_c.y;\n RGB_c.z = RGB_c.z < L_B.z ? RGB_c_l.z : RGB_c.z;\n \}\n\n float3 RGB = RGB_c / D_RGB;\n \n float3x3 MATRIX_INVERSE_16 = CAT_CAT16.invert();\n float3 XYZ = vector_dot(MATRIX_INVERSE_16, RGB);\n\n\n // return XYZ;\n return XYZ;\n\n \}\n\n\n // The init() function is run before any calls to process().\n // Local variables can be initialized here.\n void init() \{\n float CAT_CAT16_data\[9]=\n \{\n 0.401288f, 0.650173f, -0.051461f,\n -0.250268f, 1.204414f, 0.045854f,\n -0.002079f, 0.048952f, 0.953127f,\n \};\n \n float Modified_CAT16_data\[]=\n \{\n 0.656619, 0.342071, 0.00131062,\n -0.222571, 1.10658, 0.115987,\n -0.000634146, 0.05855, 0.942084,\n \};\n\n if (catDataSelection == 0)\n \{\n CAT_CAT16.setArray(CAT_CAT16_data);\n \}\n else if (catDataSelection == 1)\n \{\n CAT_CAT16.setArray(Modified_CAT16_data);\n \}\n else if (catDataSelection == 2)\n \{\n CAT_CAT16 = RGBPrimsToXYZMatrix(rxy,gxy,bxy,wxy,1.0f,1);\n \}\n \n float panlrcm_data\[]=\n \{\n 460.0f, 451.0f, 288.0f,\n 460.0f, -891.0f, -261.0f,\n 460.0f, -220.0f, -6300.0f,\n \};\n panlrcm.setArray(panlrcm_data);\n \}\n\n void process() \{\n // Read the input image\n SampleType(src) input = src();\n\n // Isolate the RGB components\n float3 srcPixel(input.x, input.y, input.z);\n\n float3 out = srcPixel;\n float3 surrounds = surround_custom;\n if (surround == 0)\n \{\n surrounds = float3(0.8f, 0.525f, 0.8f);\n \}\n else if (surround == 1)\n \{\n surrounds = float3(0.9f, 0.59f, 0.9f);\n \}\n else if (surround == 2)\n \{\n surrounds = float3(1.0f, 0.69f, 1.0f);\n \}\n \n if (invert)\n \{\n out = Hellwig2022_JMh_to_XYZ(srcPixel, inWhite, L_A, Y_b, surrounds, discountIlluminant, HK_mode);\n \}\n else\n \{\n out = XYZ_to_Hellwig2022_JMh(srcPixel, inWhite, L_A, Y_b, surrounds, discountIlluminant, HK_mode);\n \}\n\n // Write the result to the output image\n dst() = float4(out.x, out.y, out.z, input.w);\n \}\n\};\n" rebuild "" XYZ_JMh_compressMode true XYZ_JMh_XYZ_w_scaler 1 XYZ_JMh_L_A 100 XYZ_JMh_inWhite {5.05 100 108.88} XYZ_JMh_surround -1 XYZ_JMh_surround_custom {0.8 0.57 0.9} XYZ_JMh_Y_b 20 rebuild_finalise "" name BlinkScript1 selected true xpos -62 ypos 44 } BlinkScript { recompileCount 1 ProgramGroup 1 KernelDescription "2 \"XYZ_JMh\" iterate pixelWise 500da4dd91f3554843ad7c1db7ae340971627ae93f07851b537464e383eadfe4 2 \"src\" Read Point \"dst\" Write Point 17 \"invert\" Bool 1 AA== \"compressMode\" Bool 1 AA== \"linear_extension\" Bool 1 AA== \"XYZ_w_scaler\" Float 1 AAAAAA== \"L_A\" Float 1 AAAAAA== \"L_B\" Float 3 AAAAAAAAAAAAAAAAAAAAAA== \"inWhite\" Float 3 AAAAAAAAAAAAAAAAAAAAAA== \"surround\" Int 1 AAAAAA== \"surround_custom\" Float 3 AAAAAAAAAAAAAAAAAAAAAA== \"Y_b\" Float 1 AAAAAA== \"discountIlluminant\" Bool 1 AA== \"HK_mode\" Bool 1 AA== \"catDataSelection\" Int 1 AAAAAA== \"rxy\" Float 2 AAAAAAAAAAA= \"gxy\" Float 2 AAAAAAAAAAA= \"bxy\" Float 2 AAAAAAAAAAA= \"wxy\" Float 2 AAAAAAAAAAA= 17 \"invert\" 1 1 \"compressMode\" 1 1 \"linear_extension\" 1 1 \"XYZ_w_scaler\" 1 1 \"L_A\" 1 1 \"L_B\" 3 1 \"inWhite\" 3 1 \"surround\" 1 1 \"surround_custom\" 3 1 \"Y_b\" 1 1 \"discountIlluminant\" 1 1 \"HK_mode\" 1 1 \"catDataSelection\" 1 1 \"rxy\" 2 1 \"gxy\" 2 1 \"bxy\" 2 1 \"wxy\" 2 1 2 \"CAT_CAT16\" Float 9 1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA \"panlrcm\" Float 9 1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" kernelSource "kernel XYZ_JMh : ImageComputationKernel\n\{\n Image src; // the input image\n Image dst; // the output image\n\n param:\n bool invert;\n bool compressMode;\n bool linear_extension;\n float XYZ_w_scaler;\n float L_A;\n float3 L_B;\n float3 inWhite;\n int surround;\n float3 surround_custom;\n float Y_b;\n bool discountIlluminant;\n bool HK_mode;\n \n // 0 = Stock CAT16\n // 1 = Thomas's custom primaries\n // 2 = live from params below\n int catDataSelection; // original vs modified CAT16 matrix\n // xy coordintes for custom CAT matrix\n float2 rxy;\n float2 gxy;\n float2 bxy;\n float2 wxy;\n\n local:\n float3x3 CAT_CAT16;\n float3x3 panlrcm;\n\n void define() \{\n\n \}\n\n // multiplies a 3D vector with a 3x3 matrix\n float3 vector_dot( float3x3 m, float3 v)\n \{\n float3 r = 1.0f;\n for(int c = 0; c<3; c++)\n \{\n r\[c] = m\[c]\[0]*v.x + m\[c]\[1]*v.y + m\[c]\[2]*v.z;\n \}\n\n return r;\n \}\n\n float degree_of_adaptation(float F, float L_A )\n \{\n float D = F * (1.0f - (1.0f / 3.6f) * exp((-L_A - 42.0f) / 92.0f));\n\n return D;\n \}\n\n float clip(float x, float a, float b)\n \{\n return max(a, min(x, b));\n \}\n\n // \"safe\" power function to avoid NANs or INFs when taking a fractional power of a negative base\n // this one initially returned -pow(abs(b), e) for negative b\n // but this ended up producing undesirable results in some cases\n // so now it just returns 0.0 instead\n float spow( float base, float exponent )\n \{\n float a = base;\n float b = exponent;\n\n if(base < 0.0f && exponent != floor(exponent) )\n \{\n// return 0.0f;\n -pow(-base, exponent); \n \}\n else\n \{\n return pow(base, exponent); \n \}\n \}\n\n // \"safe\" div\n float sdiv( float a, float b )\n \{\n if(b == 0.0f)\n \{\n return 0.0f;\n \}\n else\n \{\n return a / b;\n \}\n \}\n\n float3 float3abs( float3 a )\n \{\n return fabs(a);\n \}\n\n float3 float3spow( float3 base, float exponent )\n \{\n return float3(spow(base.x, exponent), spow(base.y, exponent), spow(base.z, exponent));\n \}\n\n float3 float3sign( float3 v )\n \{\n return float3(sign(v.x), sign(v.y), sign(v.z));\n \}\n\n float3 compress(float3 xyz)\n \{\n \n float x = xyz.x;\n float y = xyz.y;\n float z = xyz.z;\n \n float C = (x+y+z)/3;\n if (C == 0.0f)\n return float3(x,y,z);\n\n float R = sqrt(spow((x-C),2) + spow((y-C),2) + spow((z-C),2)) ;\n // np.sqrt(2/3)\n // 0.816496580927726\n R = R * 0.816496580927726f;\n \n if (R != 0.0f)\n \{\n x = (x-C)/R ;\n y = (y-C)/R ;\n z = (z-C)/R ;\n \}\n \n float r = R/C ;\n float s = -min(x, min(y, z));\n \n float t = 0.0f;\n if (r != 0.0f)\n \{\n t = (0.5f+spow((spow((s-0.5f),2) + spow((sqrt(4.0f/spow(r,2)+1.0f)-1.0f),2)/4.0f),0.5f));\n if (t == 0.0f)\n return float3(xyz.x,xyz.y,xyz.z);\n t = 1.0f/t;\n \}\n \n x = C*x*t + C ;\n y = C*y*t + C ;\n z = C*z*t + C ;\n \n return float3(x,y,z);\n \}\n\nfloat3 uncompress(float3 xyz)\n\{\n\n \n float x = xyz.x;\n float y = xyz.y;\n float z = xyz.z;\n \n float C = (x+y+z)*(1.0f/3.0f) ;\n if (C == 0.0f)\n return float3(x,y,z);\n\n float R = sqrt(spow((x-C),2) + spow((y-C),2) + spow((z-C),2));\n // np.sqrt(2/3)\n // 0.816496580927726\n R = R * 0.816496580927726f;\n \n if (R != 0.0f)\n \{\n x = (x-C)/R ;\n y = (y-C)/R ;\n z = (z-C)/R ;\n \}\n\n float t = R/C ;\n float s = -min(x, min(y, z));\n \n float r = 0.0f;\n if (t != 0.0f)\n \{\n r = sqrt(spow((2*sqrt(spow((1.0f/t-0.5f),2)-spow((s-0.5f),2))+1.0f),2)-1.0f);\n if (r == 0.0f)\n return float3(xyz.x,xyz.y,xyz.z);\n r = 2.0f/r;\n \}\n \n x = C*x*r + C ;\n y = C*y*r + C ;\n z = C*z*r + C ;\n \n return float3(x,y,z);\n\}\n\n float3 post_adaptation_non_linear_response_compression_forward(float3 RGB, float F_L)\n \{\n float3 F_L_RGB = float3spow(F_L * float3abs(RGB) / 100.0f, 0.42f);\n float3 RGB_c = (400.0f * sign(RGB) * F_L_RGB) / (27.13f + F_L_RGB) + 0.1f;\n \n return RGB_c;\n \}\n\n float3 d_post_adaptation_non_linear_response_compression_forward( float3 RGB, float F_L)\n \{\n float3 F_L_RGB = float3spow(F_L * RGB / 100.0f, 0.42f);\n float F_L_100 = spow(F_L / 100.0f, 0.42f);\n \n float3 d_RGB_a = ( 400.0f * ((0.42f * 27.13f) * float3spow(RGB, -0.58f) * F_L_100)/ ( (F_L_RGB + 27.13f) * (F_L_RGB + 27.13f) ));\n\n\n return d_RGB_a;\n \}\n\n float3 post_adaptation_non_linear_response_compression_inverse(float3 RGB,float F_L)\n \{\n\n float3 RGB_p = (float3sign(RGB - 0.1f) * 100.0f / F_L * float3spow((27.13f * float3abs(RGB - 0.1f)) / (400.0f - float3abs(RGB - 0.1f)), 1.0f / 0.42f) );\n return RGB_p;\n \}\n\n // convert radians to degrees\n float degrees( float radians )\n \{\n return radians * 180.0f / PI;\n \}\n\n\n // convert degrees to radians\n float radians( float degrees )\n \{\n return degrees / 180.0f * PI;\n \}\n\n float mod(float a, float N)\n \{\n return a - N*floor(a/N);\n \} \n\n float hue_angle_dependency_Hellwig2022(float h)\n \{\n return float( \\\n -0.160f * cos(h) \\\n + 0.132f * cos(2.0f * h) \\\n - 0.405f * sin(h) \\\n + 0.080f * sin(2.0f * h) \\ \n + 0.792f \\\n );\n\n // return float( -0.160f * cos(h) + 0.132f * cos(2.0f * h) - 0.405f * sin(h) + 0.080f * sin(2.0f * h) + 0.792f );\n \}\n\n float3x3 RGBPrimsToXYZMatrix(float2 rxy, float2 gxy, float2 bxy, float2 wxy,float Y, bool direction)\n \{\n // # given r g b chromaticities and whitepoint, convert RGB colors to XYZ\n // # based on CtlColorSpace.cpp from the CTL source code : 77\n // # param: xy - dict of chromaticity xy coordinates: rxy: float2(x, y) etc\n // # param: Y - luminance of \"white\" - defaults to 1.0\n // # param: inverse - calculate XYZ to RGB instead\n\n float2 r = rxy;\n float2 g = gxy;\n float2 b = bxy;\n float2 w = wxy;\n\n float X = w.x * Y / w.y;\n float Z = (1 - w.x - w.y) * Y / w.y;\n\n // # Scale factors for matrix rows\n float d = r.x * (b.y - g.y) + b.x * (g.y - r.y) + g.x * (r.y - b.y);\n\n float Sr = (X * (b.y - g.y) - \\\n g.x * (Y * (b.y - 1.0f) + \\\n b.y * (X + Z)) + \\\n b.x * (Y * (g.y - 1.0f) + \\\n g.y * (X + Z))) / d ;\n \n float Sg = (X * (r.y - b.y) + \\\n r.x * (Y * (b.y - 1.0f) + \\\n b.y * (X + Z)) - \\\n b.x * (Y * (r.y - 1.0f) + \\\n r.y * (X + Z))) / d ;\n\n float Sb = (X * (g.y - r.y) - \\\n r.x * (Y * (g.y - 1.0f) + \\\n g.y * (X + Z)) + \\\n g.x * (Y * (r.y - 1.0f) + \\\n r.y * (X + Z))) / d ;\n\n // # Assemble the matrix\n float Mdata\[] =\n \{\n Sr * r.x, Sr * r.y, Sr * (1.0f - r.x - r.y),\n Sg * g.x, Sg * g.y, Sg * (1.0f - g.x - g.y),\n Sb * b.x, Sb * b.y, Sb * (1.0f - b.x - b.y),\n \};\n\n float MdataNukeOrder\[] = \{\n Mdata\[0], Mdata\[3], Mdata\[6],\n Mdata\[1], Mdata\[4], Mdata\[7],\n Mdata\[2], Mdata\[5], Mdata\[8],\n \};\n\n float3x3 newMatrix;\n newMatrix.setArray(MdataNukeOrder);\n\n // create inverse matrix\n float3x3 newMatrixInverse = newMatrix.invert();\n\n // return forward or inverse matrix\n if (direction == 0)\n \{\n return newMatrix;\n \}\n else if (direction == 1)\n \{\n return newMatrixInverse;\n \}\n \}\n\n float3 XYZ_to_Hellwig2022_JMh( float3 XYZ, float3 XYZ_w, float L_A, float Y_b, float3 surround, bool discountIlluminant, bool HK_mode)\n \{\n XYZ_w = XYZ_w * XYZ_w_scaler;\n float _X_w = XYZ_w.x ;\n float Y_w = XYZ_w.y ;\n float _Z_w = XYZ_w.z ;\n\n // # Step 0\n // # Converting *CIE XYZ* tristimulus values to sharpened *RGB* values.\n float3x3 MATRIX_16 = CAT_CAT16;\n float3 RGB_w = vector_dot(MATRIX_16, XYZ_w);\n\n // # Computing degree of adaptation :math:`D`.\n float D = clip(degree_of_adaptation(surround.x, L_A), 0.0f, 1.0f);\n if(discountIlluminant)\n \{\n D = 1.0f;\n \}\n\n\n // # Viewing conditions dependent parameters\n float k = 1.0f / (5.0f * L_A + 1.0f);\n float k4 = pow(k,4);\n float F_L = 0.2f * k4 * (5.0f * L_A) + 0.1f * pow((1.0f - k4), 2.0f) * spow(5.0f * L_A, 1.0f / 3.0f) ;\n float n = sdiv(Y_b, Y_w);\n float z = 1.48f + sqrt(n);\n\n float3 D_RGB = D * Y_w / RGB_w + 1.0f - D;\n float3 RGB_wc = D_RGB * RGB_w;\n \n // # Applying forward post-adaptation non-linear response compression.\n float3 F_L_RGB = float3spow(F_L * float3abs(RGB_wc) / 100.0f, 0.42f);\n\n // # Computing achromatic responses for the whitepoint.\n float3 RGB_aw = (400.0f * float3sign(RGB_wc) * F_L_RGB) / (27.13f + F_L_RGB) + 0.1f;\n \n\n // # Computing achromatic responses for the whitepoint.\n float R_aw = RGB_aw.x ;\n float G_aw = RGB_aw.y ;\n float B_aw = RGB_aw.z ;\n float A_w = 2.0f * R_aw + G_aw + 0.05f * B_aw - 0.305f;\n\n // # Step 1\n // # Converting *CIE XYZ* tristimulus values to sharpened *RGB* values.\n\n float3 RGB = vector_dot(MATRIX_16, XYZ);\n // float3 RGB = XYZ;\n\n // # Step 2\n float3 RGB_c = D_RGB * RGB;\n\n // # Step 3\n // # Applying forward post-adaptation non-linear response compression.\n\n if (compressMode)\n \{\n RGB_c = compress(RGB_c);\n \}\n\n float3 RGB_a = post_adaptation_non_linear_response_compression_forward(RGB_c, F_L);\n\n if (compressMode)\n \{\n RGB_a = uncompress(RGB_a);\n \}\n\n\n // # Step 3\n // # Applying forward post-adaptation non-linear response compression.\n if (linear_extension)\n \{\n\n float3 RGB_a_l = d_post_adaptation_non_linear_response_compression_forward(\n L_B, F_L\n ) * (\n RGB_c - L_B\n ) + post_adaptation_non_linear_response_compression_forward(\n L_B, F_L\n );\n \n RGB_a.x = RGB_c.x < L_B.x ? RGB_a_l.x: RGB_a.x;\n RGB_a.y = RGB_c.y < L_B.y ? RGB_a_l.y: RGB_a.y;\n RGB_a.z = RGB_c.z < L_B.z ? RGB_a_l.z: RGB_a.z; \n \}\n\n // # Step 4\n // # Converting to preliminary cartesian coordinates.\n float R_a = RGB_a.x ;\n float G_a = RGB_a.y ;\n float B_a = RGB_a.z ;\n float a = R_a - 12.0f * G_a / 11.0f + B_a / 11.0f;\n float b = (R_a + G_a - 2.0f * B_a) / 9.0f;\n\n // # Computing the *hue* angle :math:`h`.\n // Unclear why this isnt matching the python version.\n float h = mod(degrees(atan2(b, a)), 360.0f);\n\n \n\n // # Step 5\n // # Computing eccentricity factor *e_t*.\n float hr = radians(h);\n\n float _h = hr;\n float _2_h = 2.0f * hr;\n float _3_h = 3.0f * hr;\n float _4_h = 4.0f * hr;\n\n float e_t = (\n -0.0582f * cos(_h)\n - 0.0258f * cos(_2_h)\n - 0.1347f * cos(_3_h)\n + 0.0289f * cos(_4_h)\n - 0.1475f * sin(_h)\n - 0.0308f * sin(_2_h)\n + 0.0385f * sin(_3_h)\n + 0.0096f * sin(_4_h)\n + 1.0f\n );\n\n // # Step 6\n // # Computing achromatic responses for the stimulus.\n float R_a2 = RGB_a.x ;\n float G_a2 = RGB_a.y ;\n float B_a2 = RGB_a.z ;\n float A = 2.0f * R_a2 + G_a2 + 0.05f * B_a2 - 0.305f;\n\n // # Step 7\n // # Computing the correlate of *Lightness* :math:`J`.\n // with sdiv_mode():\n\n float J = 100.0f * spow(sdiv(A, A_w), surround.y * z);\n\n // # Step 8\n // # Computing the correlate of *brightness* :math:`Q`.\n // with sdiv_mode():\n float Q = (2.0f / float(surround.y)) * (J / 100.0f) * A_w;\n\n // # Step 9\n // # Computing the correlate of *colourfulness* :math:`M`.\n float M = 43.0f * surround.z * e_t * sqrt(a * a + b * b);\n\n // # Computing the correlate of *chroma* :math:`C`.\n float C = 35.0f * sdiv(M, A_w);\n\n float s = 100.0f * sdiv(M, Q);\n\n // # *Helmholtz–Kohlrausch* Effect Extension.\n float J_HK = J + hue_angle_dependency_Hellwig2022(hr) * spow(C, 0.587f);\n float Q_HK = (2.0f / surround.y) * (J_HK / 100.0f) * A_w ;\n \n if (HK_mode)\n \{\n return \{J_HK,M,h\};\n \}\n else\n \{\n return \{J,M,h\};\n \}\n \}\n\n float3 Hellwig2022_JMh_to_XYZ( float3 JMh, float3 XYZ_w, float L_A, float Y_b, float3 surround, bool discountIlluminant, bool HK_mode)\n \{\n float J = JMh.x;\n float M = JMh.y;\n float h = JMh.z;\n XYZ_w = XYZ_w * XYZ_w_scaler;\n\n float _X_w = XYZ_w.x;\n float Y_w = XYZ_w.y;\n float _Z_w = XYZ_w.z;\n\n // # Step 0\n // # Converting *CIE XYZ* tristimulus values to sharpened *RGB* values.\n float3x3 MATRIX_16 = CAT_CAT16;\n float3 RGB_w = vector_dot(MATRIX_16, XYZ_w);\n\n\n // # Computing degree of adaptation :math:`D`.\n float D = clip(degree_of_adaptation(surround.x, L_A), 0.0f, 1.0f);\n if(discountIlluminant)\n \{\n D = 1.0f;\n \}\n\n\n\n // # Viewing conditions dependent parameters\n float k = 1.0f / (5.0f * L_A + 1.0f);\n float k4 = pow(k,4);\n float F_L = 0.2f * k4 * (5.0f * L_A) + 0.1f * pow((1.0f - k4), 2.0f) * spow(5.0f * L_A, 1.0f / 3.0f) ;\n float n = sdiv(Y_b, Y_w);\n float z = 1.48f + sqrt(n);\n\n float3 D_RGB = D * Y_w / RGB_w + 1.0f - D;\n float3 RGB_wc = D_RGB * RGB_w;\n \n float3 F_L_RGB = float3spow(F_L * float3abs(RGB_wc) / 100.0f, 0.42f);\n\n float3 RGB_aw = (400.0f * float3sign(RGB_wc) * F_L_RGB) / (27.13f + F_L_RGB) + 0.1f;\n\n float R_aw = RGB_aw.x ;\n float G_aw = RGB_aw.y ;\n float B_aw = RGB_aw.z ;\n float A_w = 2.0f * R_aw + G_aw + 0.05f * B_aw - 0.305f;\n\n float hr = radians(h);\n\n float C = (M * 35.0f) / A_w;\n \n if (HK_mode)\n \{\n J = J - hue_angle_dependency_Hellwig2022(hr) * spow(C, 0.587f);\n \}\n\n float _h = hr;\n float _2_h = 2.0f * hr;\n float _3_h = 3.0f * hr;\n float _4_h = 4.0f * hr;\n \n float e_t = (\n -0.0582f * cos(_h)\n - 0.0258f * cos(_2_h)\n - 0.1347f * cos(_3_h)\n + 0.0289f * cos(_4_h)\n - 0.1475f * sin(_h)\n - 0.0308f * sin(_2_h)\n + 0.0385f * sin(_3_h)\n + 0.0096f * sin(_4_h)\n + 1.0f\n );\n\n float A = A_w * spow(J / 100.0f, 1.0f / (surround.y * z));\n\n\n float P_p_1 = 43.0f * surround.z * e_t;\n float P_p_2 = A;\n\n float gamma = M / P_p_1;\n \n float a = gamma * cos(hr);\n\n float b = gamma * sin(hr);\n\n float3 RGB_a = vector_dot(panlrcm, float3(P_p_2, a, b)) / 1403.0f;\n\n if (compressMode)\n \{\n RGB_a = compress(RGB_a);\n \}\n\n float3 RGB_c = post_adaptation_non_linear_response_compression_inverse(RGB_a + 0.1f, F_L);\n\n if (compressMode)\n \{\n RGB_c = uncompress(RGB_c);\n \}\n\n if (linear_extension)\n \{\n float3 RGB_c_l = ( RGB_a + 0.1f - post_adaptation_non_linear_response_compression_forward( L_B, F_L)) / (d_post_adaptation_non_linear_response_compression_forward( L_B, F_L)) + L_B;\n \n RGB_c.x = RGB_c.x < L_B.x ? RGB_c_l.x : RGB_c.x;\n RGB_c.y = RGB_c.y < L_B.y ? RGB_c_l.y : RGB_c.y;\n RGB_c.z = RGB_c.z < L_B.z ? RGB_c_l.z : RGB_c.z;\n \}\n\n float3 RGB = RGB_c / D_RGB;\n \n float3x3 MATRIX_INVERSE_16 = CAT_CAT16.invert();\n float3 XYZ = vector_dot(MATRIX_INVERSE_16, RGB);\n\n\n // return XYZ;\n return XYZ;\n\n \}\n\n\n // The init() function is run before any calls to process().\n // Local variables can be initialized here.\n void init() \{\n float CAT_CAT16_data\[9]=\n \{\n 0.401288f, 0.650173f, -0.051461f,\n -0.250268f, 1.204414f, 0.045854f,\n -0.002079f, 0.048952f, 0.953127f,\n \};\n \n float Modified_CAT16_data\[]=\n \{\n 0.656619, 0.342071, 0.00131062,\n -0.222571, 1.10658, 0.115987,\n -0.000634146, 0.05855, 0.942084,\n \};\n\n if (catDataSelection == 0)\n \{\n CAT_CAT16.setArray(CAT_CAT16_data);\n \}\n else if (catDataSelection == 1)\n \{\n CAT_CAT16.setArray(Modified_CAT16_data);\n \}\n else if (catDataSelection == 2)\n \{\n CAT_CAT16 = RGBPrimsToXYZMatrix(rxy,gxy,bxy,wxy,1.0f,1);\n \}\n \n float panlrcm_data\[]=\n \{\n 460.0f, 451.0f, 288.0f,\n 460.0f, -891.0f, -261.0f,\n 460.0f, -220.0f, -6300.0f,\n \};\n panlrcm.setArray(panlrcm_data);\n \}\n\n void process() \{\n // Read the input image\n SampleType(src) input = src();\n\n // Isolate the RGB components\n float3 srcPixel(input.x, input.y, input.z);\n\n float3 out = srcPixel;\n float3 surrounds = surround_custom;\n if (surround == 0)\n \{\n surrounds = float3(0.8f, 0.525f, 0.8f);\n \}\n else if (surround == 1)\n \{\n surrounds = float3(0.9f, 0.59f, 0.9f);\n \}\n else if (surround == 2)\n \{\n surrounds = float3(1.0f, 0.69f, 1.0f);\n \}\n \n if (invert)\n \{\n out = Hellwig2022_JMh_to_XYZ(srcPixel, inWhite, L_A, Y_b, surrounds, discountIlluminant, HK_mode);\n \}\n else\n \{\n out = XYZ_to_Hellwig2022_JMh(srcPixel, inWhite, L_A, Y_b, surrounds, discountIlluminant, HK_mode);\n \}\n\n // Write the result to the output image\n dst() = float4(out.x, out.y, out.z, input.w);\n \}\n\};\n" rebuild "" XYZ_JMh_invert true XYZ_JMh_compressMode true XYZ_JMh_XYZ_w_scaler 1 XYZ_JMh_L_A 100 XYZ_JMh_inWhite {5.05 100 108.88} XYZ_JMh_surround -1 XYZ_JMh_surround_custom {0.8 0.57 0.9} XYZ_JMh_Y_b 20 rebuild_finalise "" name BlinkScript2 selected true xpos -62 ypos 110 } Group { name GamutConvert1 tile_color 0xcc804eff label "XYZ D65 to Rec709" selected true xpos -62 ypos 198 addUserKnob {20 Params} addUserKnob {35 presets_src l src M {"gamut/ACES AP0" "knobs this \{src_name \"ACES AP0\" src \"0.7347 0.2653 0.0 1.0 0.0001 -0.077 0.32168 0.33767\"\}" "gamut/ACES AP1" "knobs this \{src_name \"ACES AP1\" src \"0.713 0.293 0.165 0.83 0.128 0.044 0.32168 0.33767\"\}" "gamut/Filmlight E-Gamut" "knobs this \{src_name \"Filmlight E-Gamut\" src \"0.8 0.3177 0.18 0.9 0.065 -0.0805 0.3127 0.329\"\}" "gamut/DaVinci Wide Gamut" "knobs this \{src_name \"DaVinci Wide Gamut\" src \"0.8 0.313 0.1682 0.9877 0.079 -0.1155 0.3127 0.329\"\}" gamut/Rec709 "knobs this \{src_name \"Rec709\" src \"0.64 0.33 0.3 0.6 0.15 0.06 0.3127 0.329\"\}" gamut/Rec2020 "knobs this \{src_name \"Rec2020\" src \"0.708 0.292 0.17 0.797 0.131 0.046 0.3127 0.329\"\}" gamut/P3D60 "knobs this \{src_name \"P3D60\" src \"0.68 0.32 0.265 0.69 0.15 0.06 0.321626 0.337737\"\}" gamut/P3D65 "knobs this \{src_name \"P3D65\" src \"0.68 0.32 0.265 0.69 0.15 0.06 0.3127 0.329\"\}" gamut/P3DCI "knobs this \{src_name \"P3DCI\" src \"0.68 0.32 0.265 0.69 0.15 0.06 0.314 0.351\"\}" "gamut/Arri Wide Gamut" "knobs this \{src_name \"Arri Wide Gamut\" src \"0.684 0.313 0.221 0.848 0.0861 -0.102 0.3127 0.329\"\}" "gamut/Arri Wide Gamut 4" "knobs this \{src_name \"Arri Wide Gamut 4\" src \"0.7347 0.2653 0.1424 0.8576 0.0991 -0.0308 0.3127 0.329\"\}" "gamut/RED Wide Gamut RGB" "knobs this \{src_name \"RED Wide Gamut RGB\" src \"0.780308 0.304253 0.121595 1.493994 0.095612 -0.084589 0.3127 0.329\"\}" "gamut/GoPro Protune Native" "knobs this \{src_name \"GoPro Protune Native\" src \"0.69848046 0.19302645 0.32955538 1.02459662 0.10844263 -0.03467857 0.3127 0.329\"\}" "gamut/Canon Cinema Gamut" "knobs this \{src_name \"Canon Cinema Gamut\" src \"0.74 0.27 0.17 1.14 0.08 -0.1 0.3127 0.329\"\}" "gamut/Sony SGamut3" "knobs this \{src_name \"Sony SGamut3\" src \"0.73 0.28 0.14 0.855 0.1 -0.05 0.3127 0.329\"\}" "gamut/Sony SGamut3.Cine" "knobs this \{src_name \"Sony SGamut3.Cine\" src \"0.766 0.275 0.225 0.8 0.089 -0.087 0.3127 0.329\"\}" "gamut/Panasonic V-Gamut" "knobs this \{src_name \"Panasonic V-Gamut\" src \"0.73 0.28 0.165 0.84 0.1 -0.03 0.3127 0.329\"\}" "gamut/DJI D-Gamut" "knobs this \{src_name \"DJI D-Gamut\" src \"0.71 0.31 0.21 0.88 0.09 -0.08 0.3127 0.329\"\}" "gamut/Fujifilm F-Gamut" "knobs this \{src_name \"Fujifilm F-Gamut\" src \"0.708 0.292 0.17 0.797 0.131 0.046 0.3127 0.329\"\}" "gamut/Nikon N-Gamut" "knobs this \{src_name \"Nikon N-Gamut\" src \"0.708 0.292 0.17 0.797 0.131 0.046 0.3127 0.329\"\}" "gamut/Blackmagic Wide Gamut" "knobs this \{src_name \"Blackmagic Wide Gamut\" src \"0.7177215 0.3171181 0.228041 0.861569 0.1005841 -0.0820452 0.3127 0.329\"\}" "gamut/Adobe RGB" "knobs this \{src_name \"Adobe RGB\" src \"0.64 0.33 0.21 0.71 0.15 0.06 0.3127 0.329\"\}" "gamut/Adobe WideGamutRGB" "knobs this \{src_name \"Adobe WideGamutRGB\" src \"0.7347 0.2653 0.1152 0.8264 0.1566 0.0177 0.3457 0.3585\"\}" gamut/ProPhotoRGB "knobs this \{src_name \"ProPhotoRGB\" src \"0.734699 0.265301 0.159597 0.840403 0.036598 0.000105 0.345704 0.358540\"\}"}} addUserKnob {1 src_name l "" -STARTLINE} src_name Rec709 addUserKnob {1 src l " " t "source gamut chromaticities: rgbw"} src "0.64 0.33 0.3 0.6 0.15 0.06 0.3127 0.329" addUserKnob {35 presets_dst l dst M {gamut/XYZ "knobs this \{dst_name \"XYZ\" dst \"\"\}" "gamut/XYZ D65" "knobs this \{dst_name \"XYZ D65\" dst \"1 0 0 1 0 0 0.3127 0.329\"\}" "gamut/ACES AP0" "knobs this \{dst_name \"ACES AP0\" dst \"0.7347 0.2653 0.0 1.0 0.0001 -0.077 0.32168 0.33767\"\}" "gamut/ACES AP1" "knobs this \{dst_name \"ACES AP1\" dst \"0.713 0.293 0.165 0.83 0.128 0.044 0.32168 0.33767\"\}" "gamut/Filmlight E-Gamut" "knobs this \{dst_name \"Filmlight E-Gamut\" dst \"0.8 0.3177 0.18 0.9 0.065 -0.0805 0.3127 0.329\"\}" "gamut/DaVinci Wide Gamut" "knobs this \{dst_name \"DaVinci Wide Gamut\" dst \"0.8 0.313 0.1682 0.9877 0.079 -0.1155 0.3127 0.329\"\}" gamut/Rec709 "knobs this \{dst_name \"Rec709\" dst \"0.64 0.33 0.3 0.6 0.15 0.06 0.3127 0.329\"\}" gamut/Rec2020 "knobs this \{dst_name \"Rec2020\" dst \"0.708 0.292 0.17 0.797 0.131 0.046 0.3127 0.329\"\}" gamut/P3D60 "knobs this \{dst_name \"P3D60\" dst \"0.68 0.32 0.265 0.69 0.15 0.06 0.321626 0.337737\"\}" gamut/P3D65 "knobs this \{dst_name \"P3D65\" dst \"0.68 0.32 0.265 0.69 0.15 0.06 0.3127 0.329\"\}" gamut/P3DCI "knobs this \{dst_name \"P3DCI\" dst \"0.68 0.32 0.265 0.69 0.15 0.06 0.314 0.351\"\}" "gamut/Arri Wide Gamut" "knobs this \{dst_name \"Arri Wide Gamut\" dst \"0.684 0.313 0.221 0.848 0.0861 -0.102 0.3127 0.329\"\}" "gamut/Arri Wide Gamut 4" "knobs this \{dst_name \"Arri Wide Gamut 4\" dst \"0.7347 0.2653 0.1424 0.8576 0.0991 -0.0308 0.3127 0.329\"\}" "gamut/RED Wide Gamut RGB" "knobs this \{dst_name \"RED Wide Gamut RGB\" dst \"0.780308 0.304253 0.121595 1.493994 0.095612 -0.084589 0.3127 0.329\"\}" "gamut/GoPro Protune Native" "knobs this \{dst_name \"GoPro Protune Native\" dst \"0.69848046 0.19302645 0.32955538 1.02459662 0.10844263 -0.03467857 0.3127 0.329\"\}" "gamut/Canon Cinema Gamut" "knobs this \{dst_name \"Canon Cinema Gamut\" dst \"0.74 0.27 0.17 1.14 0.08 -0.1 0.3127 0.329\"\}" "gamut/Sony SGamut3" "knobs this \{dst_name \"Sony SGamut3\" dst \"0.73 0.28 0.14 0.855 0.1 -0.05 0.3127 0.329\"\}" "gamut/Sony SGamut3.Cine" "knobs this \{dst_name \"Sony SGamut3.Cine\" dst \"0.766 0.275 0.225 0.8 0.089 -0.087 0.3127 0.329\"\}" "gamut/Panasonic V-Gamut" "knobs this \{dst_name \"Panasonic V-Gamut\" dst \"0.73 0.28 0.165 0.84 0.1 -0.03 0.3127 0.329\"\}" "gamut/DJI D-Gamut" "knobs this \{dst_name \"DJI D-Gamut\" dst \"0.71 0.31 0.21 0.88 0.09 -0.08 0.3127 0.329\"\}" "gamut/Fujifilm F-Gamut" "knobs this \{dst_name \"Fujifilm F-Gamut\" dst \"0.708 0.292 0.17 0.797 0.131 0.046 0.3127 0.329\"\}" "gamut/Nikon N-Gamut" "knobs this \{dst_name \"Nikon N-Gamut\" dst \"0.708 0.292 0.17 0.797 0.131 0.046 0.3127 0.329\"\}" "gamut/Blackmagic Wide Gamut" "knobs this \{dst_name \"Blackmagic Wide Gamut\" dst \"0.7177215 0.3171181 0.228041 0.861569 0.1005841 -0.0820452 0.3127 0.329\"\}" "gamut/Adobe RGB" "knobs this \{dst_name \"Adobe RGB\" dst \"0.64 0.33 0.21 0.71 0.15 0.06 0.3127 0.329\"\}" "gamut/Adobe WideGamutRGB" "knobs this \{dst_name \"Adobe WideGamutRGB\" dst \"0.7347 0.2653 0.1152 0.8264 0.1566 0.0177 0.3457 0.3585\"\}" gamut/ProPhotoRGB "knobs this \{dst_name \"ProPhotoRGB\" dst \"0.734699 0.265301 0.159597 0.840403 0.036598 0.000105 0.345704 0.358540\"\}"}} addUserKnob {1 dst_name l "" -STARTLINE} dst_name "XYZ D65" addUserKnob {1 dst l " " t "destination gamut chromaticities: rgbw"} dst "1 0 0 1 0 0 0.3127 0.329" addUserKnob {83 cat M {bradford cat02 none}} cat cat02 addUserKnob {26 ""} addUserKnob {6 invert t "invert matrix direction" +STARTLINE} invert true addUserKnob {22 calc l Calculate t "Calculate a 3x3 matrix to convert from source gamut to destination gamut, with chromatic adaptation transform if whitepoints are different." -STARTLINE T "import nuke\n\ndef matmul(m0, m1):\n # multiply two square matrices\n return \[\[sum(a*b for a,b in zip(r, c)) for c in zip(*m1)] for r in m0]\n\ndef vdot(m, v):\n # multiply AxA matrix by Ax1 vector\n return \[sum(x*y for x,y in zip(r,v)) for r in m]\n\ndef transpose(m):\n # transpose matrix m by swapping rows and cols\n return \[list(r) for r in zip(*m)]\n\ndef zeros(l):\n # create square matrix of size l\n return \[\[0.0]*l for i in range(l)]\n\ndef diag(m, v):\n # set diagonal row of matrix m to vector v or float v\n if isinstance(v, float): \n v = \[v]*len(m)\n for p in range(len(m)):\n m\[p]\[p] = v\[p]\n return m\n\ndef identity(l):\n # return identity matrix of size l\n return diag(zeros(3), 1.0)\n\ndef det(m):\n # calculate determinant of 3x3 matrix m\n return m\[0]\[0]*(m\[1]\[1]*m\[2]\[2]-m\[2]\[1]*m\[1]\[2])-m\[0]\[1]*(m\[1]\[0]*m\[2]\[2]-m\[1]\[2]*m\[2]\[0])+m\[0]\[2]*(m\[1]\[0]*m\[2]\[1]-m\[1]\[1]*m\[2]\[0])\n\ndef inv(m):\n # invert 3x3 matrix m\n d = det(m)\n if d == 0.0:\n return m\n i = zeros(3)\n i\[0]\[0] = (m\[1]\[1]*m\[2]\[2]-m\[2]\[1]*m\[1]\[2])/d\n i\[0]\[1] = (m\[0]\[2]*m\[2]\[1]-m\[0]\[1]*m\[2]\[2])/d\n i\[0]\[2] = (m\[0]\[1]*m\[1]\[2]-m\[0]\[2]*m\[1]\[1])/d\n i\[1]\[0] = (m\[1]\[2]*m\[2]\[0]-m\[1]\[0]*m\[2]\[2])/d\n i\[1]\[1] = (m\[0]\[0]*m\[2]\[2]-m\[0]\[2]*m\[2]\[0])/d\n i\[1]\[2] = (m\[1]\[0]*m\[0]\[2]-m\[0]\[0]*m\[1]\[2])/d\n i\[2]\[0] = (m\[1]\[0]*m\[2]\[1]-m\[2]\[0]*m\[1]\[1])/d\n i\[2]\[1] = (m\[2]\[0]*m\[0]\[1]-m\[0]\[0]*m\[2]\[1])/d\n i\[2]\[2] = (m\[0]\[0]*m\[1]\[1]-m\[1]\[0]*m\[0]\[1])/d\n return i\n\ndef flatten(l):\n # flatten multidimensional list into one dimensional list\n return sum(l, \[])\n\ndef npm(ch):\n # Calculate the Normalized Primaries Matrix for the specified chromaticities\n # Adapted from SMPTE Recommended Practice - Derivation of Basic Television Color Equations\n # http://doi.org/10.5594/S9781614821915\n if len(ch) == 8: # handle flat list\n ch = \[\[ch\[0], ch\[1]], \[ch\[2], ch\[3]], \[ch\[4], ch\[5]], \[ch\[6], ch\[7]]]\n for c in ch:\n c.append(1.0-c\[0]-c\[1]) \n P = transpose(\[ch\[0], ch\[1], ch\[2]])\n W = \[ch\[3]\[0] / ch\[3]\[1], 1.0, ch\[3]\[2] / ch\[3]\[1]]\n C = vdot(inv(P), W)\n C = diag(zeros(3), C)\n return matmul(P, C)\n\ndef cat(ws, wd, method='bradford'):\n # Calculate a von Kries style chromatic adaptation transform matrix given xy chromaticities for src and dst white\n # Source: Mark D. Fairchild - 2013 - Color Appearance Models Third Edition p. 181-186\n # Source: Bruce Lindbloom - Chromatic Adaptation - http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html\n if ws == wd: # src and dst are equal, nothing to do\n return identity(3)\n if method == 'bradford':\n mcat = \[\[0.8951, 0.2664, -0.1614], \[-0.7502, 1.7135, 0.0367], \[0.0389, -0.0685, 1.0296]]\n elif method == 'cat02':\n mcat = \[\[0.7328, 0.4296, -0.1624], \[-0.7036, 1.6975, 0.0061], \[0.003, 0.0136, 0.9834]]\n else:\n mcat = identity(3)\n \n def xy_to_XYZ(xy):\n # convert xy chromaticity to XYZ tristimulus with Y=1.0\n return \[xy\[0]/xy\[1], 1.0, (1.0-xy\[0]-xy\[1])/xy\[1]]\n \n sXYZ = xy_to_XYZ(ws)\n dXYZ = xy_to_XYZ(wd)\n \n s_cone_mtx = vdot(mcat, sXYZ)\n d_cone_mtx = vdot(mcat, dXYZ)\n \n smat = diag(zeros(3), \[a/b for a,b in zip(d_cone_mtx, s_cone_mtx)])\n nmtx = matmul(inv(mcat), smat)\n return matmul(nmtx, mcat)\n\ndef wp(ch):\n # return whitepoint of chromaticities array\n return ch\[-2:]\n\ndef is_xyz(ch):\n # test if ch is XYZ\n prims = ch\[:-2]\n return True if prims == \[1, 0, 0, 1, 0, 0] else False\n\ndef calc_mtx(ch0, ch1, method='bradford'):\n # calculate 3x3 matrix to convert gamut ch0 to ch1, with cat\n if not ch1:\n return npm(ch0)\n rgb0_to_xyz = identity(3) if is_xyz(ch0) else npm(ch0)\n rgb1_to_xyz = identity(3) if is_xyz(ch1) else npm(ch1)\n xyz_to_cat = cat(wp(ch0), wp(ch1), method=method)\n rgb0_to_cat = matmul(xyz_to_cat, rgb0_to_xyz)\n rgb0_to_rgb1 = matmul(inv(rgb1_to_xyz), rgb0_to_cat)\n return rgb0_to_rgb1\n\n\n\nnode = nuke.thisNode()\n\ndef get_ch(k):\n # get chromaticities from knob name k\n ch_str = node\[k].getValue()\n if ' ' in ch_str:\n chs = ch_str.split(' ')\n if not len(chs) == 8:\n print('Error: need 8 space-separated float xy coordinates for RGBW')\n return None\n else:\n ch = list()\n for c in chs:\n try:\n ch.append(float(c))\n except ValueError:\n print('Error: could not convert \{0\} to float'.format(c))\n return None\n return ch\n else:\n return None\n\ndef print_mtx(mtx):\n label = node\['label'].getValue()\n \n # round to 12 digits of precision\n mtx_str = \[format(round(v, 12), '.12g') for v in flatten(mtx)]\n \n # print some useful representations of the matrix\n # an ocio matrixtransform\n mtx_str_pad = mtx_str\[0:3]+\['0']+mtx_str\[3:6]+\['0']+mtx_str\[6:9]+\['0']*4+\['1']\n mtx_string = ', '.join(map(str, mtx_str_pad))\n ocio_matrixtransform_string = '! \{\{matrix: \[\{0\}]\}\}'.format(mtx_string)\n print('\{0\} - OCIO MatrixTransform\\n\{1\}\\n'.format(label, ocio_matrixtransform_string))\n \n # an spimtx file\n spimtx_string = '\{0\} 0\\n\{1\} 0\\n\{2\} 0'.format(\n ' '.join(mtx_str\[0:3]),\n ' '.join(mtx_str\[3:6]),\n ' '.join(mtx_str\[6:9]),\n )\n print('\{0\}.spimtx\\n\{1\}\\n'.format(label, spimtx_string))\n\ndef reset():\n # reset matrix\n node\['matrix'].setValue(flatten(identity(3)))\n \ndef calc():\n # gather node info\n src_ch = get_ch('src')\n if not src_ch:\n reset()\n return\n dst_ch = get_ch('dst')\n cat_method = node\['cat'].value()\n mtx = calc_mtx(src_ch, dst_ch, method=cat_method)\n if node\['invert'].getValue():\n mtx = inv(mtx)\n node\['matrix'].setValue(flatten(mtx))\n label = '\{1\} to \{0\}' if node\['invert'].getValue() else '\{0\} to \{1\}'\n label = label.format(node\['src_name'].getValue(), node\['dst_name'].getValue())\n node\['label'].setValue(label)\n if node\['print_mtx'].getValue():\n print_mtx(mtx)\n \ncalc()"} addUserKnob {6 print_mtx l print t "print a few useful representations of the matrix ni the script editor, for OCIO MatrixTransforms, spimtx files, etc" -STARTLINE} addUserKnob {41 matrix T ColorMatrix.matrix} addUserKnob {22 create_colormatrix l "Create ColorMatrix" t "create ColorMatrix node using calculated 3x3 matrix" -STARTLINE T "from __future__ import with_statement\nnode = nuke.thisNode()\nwith nuke.root():\n nukescripts.clear_selection_recursive()\n m = nuke.createNode('ColorMatrix')\n m.setXYpos(node.xpos()-120, node.ypos())\n m\['matrix'].setValue(node\['matrix'].getValue())\n m\['label'].setValue(node\['label'].getValue())"} } Input { inputs 0 name Input xpos -40 ypos 206 } ColorMatrix { matrix { {3.240969942 -1.537383178 -0.4986107603} {-0.9692436363 1.875967502 0.04155505741} {0.0556300797 -0.2039769589 1.056971514} } name ColorMatrix xpos -40 ypos 255 } Output { name Output xpos -40 ypos 302 } end_group Group { name LogConvert_Davinci_Intermediate selected true xpos -62 ypos 251 addUserKnob {20 Intermediate_tab l Intermediate} addUserKnob {4 operation M {log2lin lin2log}} operation lin2log } Input { inputs 0 name Input xpos -40 ypos -10 } Dot { name Dot1 xpos -6 ypos 114 } set N324fb800 [stack 0] Expression { expr0 r<=DI_LIN_CUT?r*DI_M:(log(r+DI_A)/log(2)+DI_B)*DI_C expr1 g<=DI_LIN_CUT?g*DI_M:(log(g+DI_A)/log(2)+DI_B)*DI_C expr2 b<=DI_LIN_CUT?b*DI_M:(log(b+DI_A)/log(2)+DI_B)*DI_C channel3 none name lin2log xpos 80 ypos 110 addUserKnob {20 Params} addUserKnob {7 DI_A} DI_A 0.0075 addUserKnob {7 DI_B} DI_B 7 addUserKnob {7 DI_C} DI_C 0.07329248 addUserKnob {7 DI_M} DI_M 10.44426855 addUserKnob {7 DI_LIN_CUT} DI_LIN_CUT 0.00262409 addUserKnob {7 DI_LOG_CUT} DI_LOG_CUT 0.02740668 } push $N324fb800 Expression { expr0 r<=DI_LOG_CUT?r/DI_M:pow(2.0,(r/DI_C)-DI_B)-DI_A expr1 g<=DI_LOG_CUT?g/DI_M:pow(2.0,(g/DI_C)-DI_B)-DI_A expr2 b<=DI_LOG_CUT?b/DI_M:pow(2.0,(b/DI_C)-DI_B)-DI_A channel3 none name log2lin selected true xpos -160 ypos 110 addUserKnob {20 Params} addUserKnob {7 DI_A} DI_A 0.0075 addUserKnob {7 DI_B} DI_B 7 addUserKnob {7 DI_C} DI_C 0.07329248 addUserKnob {7 DI_M} DI_M 10.44426855 addUserKnob {7 DI_LIN_CUT} DI_LIN_CUT 0.00262409 addUserKnob {7 DI_LOG_CUT} DI_LOG_CUT 0.02740668 } Switch { inputs 2 which {{parent.operation}} name Operation xpos -40 ypos 190 } Output { name Output1 xpos -40 ypos 310 } end_group PositionToPoints2 { selectable false render_mode off cast_shadow false receive_shadow false P_channel rgb N_channel rgb detail 0.155 pointSize 0.01 name PositionToPoints1 selected true xpos -62 ypos 304 }