hos_SplitEXR_Ultra: Python version

User avatar
bryanfordney
Fusioneer
Posts: 78
Joined: Tue Jan 29, 2019 1:10 pm
Location: Atlanta, GA
Been thanked: 5 times

hos_SplitEXR_Ultra: Python version

#1

Post by bryanfordney » Thu Jul 18, 2019 6:24 pm

Below is a conversion of the hos_SplitEXR_Ultra script from lua to Python, for all who might find that useful. It should run in Python 2.7 or 3.6. I have made no changes to the code other than what was strictly necessary, which was kinda fun. I am working on some customizations for myself to make this script handle Blender's Multi-Layer EXR files better.
Code: [Select all] [Expand/Collapse] [Download] (hos_SplitEXR_Ultra.py)
  1. # -*- coding: utf-8 -*-
  2. from __future__ import print_function, unicode_literals
  3. import re
  4. import os
  5. import time
  6.  
  7. # Split EXR Ultra v2.1
  8. # by S.Neve / House of Secrets
  9.  
  10. # ##############################################################################-
  11. # Description
  12. # ##############################################################################-
  13. # This script will split a multi-channel EXR image out into multiple Loader nodes.
  14.  
  15. # ##############################################################################-
  16. # Fusion Support
  17. # ##############################################################################-
  18. # This script has been tested and works with Fusion v7 to v9 and Resolve 15.1+. It runs on Windows, Mac, and Linux.
  19.  
  20. # ##############################################################################-
  21. # Installation
  22. # ##############################################################################-
  23. # Copy the script to your Fusion user preferences "Scripts:/tool/" folder.
  24.  
  25. # ##############################################################################-
  26. # Usage
  27. # ##############################################################################-
  28. # Add a Loader node to your Fusion composite and select an EXR based image sequence in the file browser dialog.
  29. # Right click on the Loader node in the Fusion flow area. In the contextual popup menu select the "Script > hos_SplitEXR_Ultra" menu item.
  30. # The hos_SplitEXR script will: generate a series of Loader nodes in your composite that are used to process each of the channels in the multi-channel EXR file.
  31.  
  32. # ##############################################################################-
  33. # RELEASE NOTES
  34. # ##############################################################################-
  35.  
  36. # * v2.1 Ultra 2018-09-16 by Andrew Hazelden <andrew@andrewhazelden.com>
  37. #   - Resolve 15.1+ Loader node compatibility verified.
  38. #   - Note: You may have to run the Split EXR Ultra script a 2nd time to activate settings in the script's GUI like switching from a horizontal to a vertical placement in Resolve.
  39.  
  40. # * v2.0.0 Ultra 2018-01-17 by Andrew Hazelden <andrew@andrewhazelden.com>
  41. #   - Prepared for inclusion with Reactor.
  42.  
  43. # * v1.11.0 Ultra 2017-11-24 by Andrew Hazelden <andrew@andrewhazelden.com>
  44. #   - Added a "Split All Selected Nodes" checkbox to the AskUser dialog. The "Split All Selected Nodes" checkbox allows the Split EXR Ultra script to automatically process and split apart multiple Loader nodes at the same time when you have several Loader nodes selected in the flow area.
  45. #   - Added a "Verbose Logging" checkbox to the AskUser dialog. This prints out detailed information to the Console tab about the channel splitting task.
  46. #   - Updated the C4D channel code to only be used if the regular exr channel matching process fails
  47. #   - Fixed an R/G/B channel matching logic issue
  48. #   - Fixed several variable scope issues
  49. #   - Updated the logDebug() def to only print output when a debugging state is active:
  50. #   - Added a logDump() function
  51.  
  52. # * v1.10.0 Ultra 2017-11-23 by Cedric Duriau <duriau.cedric@live.be>
  53. #   - Refactored code
  54. #   - Split code into functions
  55. #   - Improved tool validation
  56. #   - Altered input data format for loader generation
  57. #   - Implemented logging functions by level
  58.  
  59. # * v1.9.0 Ultra 2017-10-31 by Andrew Hazelden <andrew@andrewhazelden.com>
  60. #   - Merged the Tim Little v1.8 revisions with my v1.7.2 revisions to make an "ultra" version that has the best parts of both scripts integrated together. The script works with Fusion (Free) and Fusion Studio v7-9.
  61. #   - Added a new "Skip Importing Alpha Channels" option that can be used when you don't want to have the alpha channels loaded by default. This is useful when loading 3D rerings with image based lighting or physical sky backgrounds that have RGB image data in the sky background zone of the scene but a transparent un-premultiplied alpha channel.
  62.  
  63. # * v1.8.0 2017-08-21 by Tim Little <timhlittl@gmail.com> www.littlevfx.com
  64. #   - Added a method to handle exr's that utilize a phrase at the  of its channel name (rather than a single character)
  65.  
  66. # * v1.7.2 2017-10-27 by Andrew Hazelden <andrew@andrewhazelden.com>
  67. #   - Replaced the preference writing code so it uses the Fusion native setData() and getData() functions.
  68. #   - Updated the AskUser dialog.
  69.  
  70. # * v1.7.1 2017-10-04 by Andrew Hazelden <andrew@andrewhazelden.com>
  71. #   - Updated the script to have cross-platform Fusion 8.2.1 + Fusion 9.0.1 support.
  72. #   - The script can now be installed as either a Fusion Comp script (Scripts:/Comp/) that is accessible using the main Fusion menubar's Script > hos_SplitEXR menu item, or as a Fusion tool script (Scripts:/tool/) and accessed by right clicking on a Loader node in the flow area and: selecting the Script > hos_SplitEXR contextual menu item.
  73. #   - Improved the Lua script comments to make the program flow easier to understand for new pipeline TDs who need to customize this script in the future.
  74. #   - Improved the error handling and added more verbosity to the error messages.
  75. #   - Changed the Fusion preference writing path to use the user preference profile folder since the applications folder is usually write locked for non super-users.
  76. #   - Updated the "loader = Loader({Clip = loader_clip})" line to use the comp.Loader() prefix to avoid a tool script based error.
  77.  
  78. # * v1.7.0 by S.Neve
  79. #   - Added checksum to see which fusion version is running
  80.  
  81. # * v1.6.0 by S.Neve
  82. #   - Fixed preference saving, iterating over the table needs to be done using pairs, rather than ipairs
  83.  
  84. # * v1.5.0 by S.Neve
  85. #   - Fixed the preference saving on Win 7 and 8. This is the same problem that occurs on all tools that use the script preference saving def this tool uses.:
  86. #   - Added Fusion 7 support by explicitly using ipairs when iterating table value key pairs.
  87.  
  88. # * v1.4.0 by S.Neve
  89. #   - Added grid and tile placement options as per request
  90. #   - Added preference writing and loading, so settings are saved between sessions.
  91.  
  92. # * v1.3.0 by S.Neve
  93. #   - Changed the new Loader name creation as per request by Alexey D.
  94. #   - This needs testing to see everything works.
  95.  
  96. # * v1.2.0 by S.Neve
  97. #   - Changed the Loader tool EXR format check, this should have fixed the (strange and random) 'attempt
  98. #   to index global tool (a nil value)' error.
  99.  
  100. # * v1.1.0 by S.Neve
  101. #   - Expanded the prototype a bit, to handle XYZ channel names aswel.
  102. #   - Added menu to handle placement options, later i will add options to handle what to do with which channels.
  103. #   - Spaghetti code galore by the way, will fix this with v1.2
  104.  
  105. # * v1.0.0 by S.Neve
  106. #   - Initial prototype.
  107.  
  108. VERSION = "[[v2.1 \"Ultra\" (2018-09-16)]]"
  109. AUTHOR = "[[S.Neve / House of Secrets]]"
  110. CONTRIBUTORS = ["Tim Little", "Andrew Hazelden", "Cedric Duriau"]
  111. CHANNEL_NO_MATCH = "SomethingThatWontMatchHopefully"
  112. CHANNELS_TO_SKIP = {"r": True, "red": True,
  113.                     "g": True, "green" : True,
  114.                     "b": True, "blue" : True,
  115.                     "a": True, "alpha" : True,
  116.                     "somethingthatwontmatchhopefully" : True}
  117.  
  118. ##############################################################################-
  119. ## Set a fusion specific preference value
  120. ## Example: setPreferenceData("hos_SplitEXR.cdir", 1, true)
  121. def setPreferenceData(pref, value, debugPrint):
  122.     ## Choose if you are saving the preference to the comp or to all of fusion
  123.     ## comp.SetData(pref, value)
  124.     fusion.SetData(pref, value)
  125.  
  126.     ## List the preference value
  127.     if (debugPrint == True) or (debugPrint == 1):
  128.         if value == None:
  129.             print("[Setting " + str(pref) + " Preference Data] " + "nil")
  130.         else:
  131.             print("[Setting " + str(pref) + " Preference Data] " + str(value))
  132.        
  133.    
  134.  
  135.  
  136. ##############################################################################-
  137. ## Read a fusion specific preference value. If nothing exists set and return a default value
  138. ## Example: cdir = getPreferenceData("hos_SplitEXR.cdir", 1, true)
  139. def getPreferenceData(pref, defaultValue, debugPrint):
  140.     ## Choose if you are saving the preference to the comp or to all of fusion
  141.     ## newPreference = comp.GetData(pref)
  142.     newPreference = fusion.GetData(pref)
  143.  
  144.     if newPreference:
  145.         ## List the existing preference value
  146.         if (debugPrint == True) or (debugPrint == 1):
  147.             if newPreference == None:
  148.                 print("[Reading " + str(pref) + " Preference Data] " + "nil")
  149.             else:
  150.                 print("[Reading " + str(pref) + " Preference Data] " + str(newPreference))
  151.            
  152.        
  153.     else:
  154.         ## Force a default value into the preference &: list it
  155.         newPreference = defaultValue
  156.  
  157.         ## Choose if you are saving the preference to the comp or to all of fusion
  158.         ## comp.SetData(pref, defaultValue)
  159.         fusion.SetData(pref, defaultValue)
  160.  
  161.         if  (debugPrint == True) or (debugPrint == 1):
  162.             if newPreference == None:
  163.                 print("[Creating " + str(pref) + " Preference Data] " + "nil")
  164.             else:
  165.                 print("[Creating "+ str(pref) + " Preference Entry] " + str(newPreference))
  166.            
  167.        
  168.    
  169.  
  170.     return newPreference
  171.  
  172.  
  173. def buildDialog():
  174.     ## Read the updated preferences
  175.     verbose = getPreferenceData("hos_SplitEXR.verbose", 0, False)
  176.     splitAllSelectedNodes = getPreferenceData("hos_SplitEXR.splitAllSelectedNodes", 1, verbose)
  177.     cdir = getPreferenceData("hos_SplitEXR.cdir", 0, verbose)
  178.     skipAlpha = getPreferenceData("hos_SplitEXR.skipAlpha", 0, verbose)
  179.     tiles = getPreferenceData("hos_SplitEXR.tiles", getPreferenceData("Comp.FlowView.ForceSource", 0, verbose), verbose)
  180.     grid = getPreferenceData("hos_SplitEXR.grid", 0, verbose)
  181.     ## cxyz = getPreferenceData("hos_SplitEXR.cxyz", 1, verbose)
  182.  
  183.     placementsList = ["Vertical Layout", "Horizontal Layout"]
  184.     dialog = {
  185.         1.0: { 1.0: "", 2.0: "Text", "Lines": 1, "Default": VERSION, "ReadOnly": True, "Width": 1.0},
  186.         2.0: { 1.0: "Description", 2.0: "Text", "Lines": 3, "Wrap": True, "Default": "This script will split a multi-channel EXR image out into multiple Loader nodes.", "ReadOnly": True, "Width": 1.0},
  187.         3.0: { 1.0: "cdir", "Name": "Placement", 2.0: "Dropdown", "Default": cdir if cdir else 0, "Options": placementsList, "Width": 1.0},
  188.         4.0: { 1.0: "grid", "Name": "Grid Placement", 2.0: "Slider", "Integer": True, "Default": grid if grid else 0, "Min": 0, "Max": 25, "Width": 1.0},
  189.         5.0: { 1.0: "splitAllSelectedNodes", "Name": "Split All Selected Nodes", 2.0: "Checkbox", "Default": splitAllSelectedNodes if splitAllSelectedNodes else 1, "Width": 1.0},
  190.         6.0: { 1.0: "skipAlpha", "Name": "Skip Importing Alpha Channels", 2.0: "Checkbox", "Default": skipAlpha if skipAlpha else 0, "Width": 1.0},
  191.         7.0: { 1.0: "tiles", "Name": "Show Source Tiles ", 2.0: "Checkbox", "Default": tiles, "Width": 1.0},
  192.         ## { 1.0: "cxyz", "Name": "Map X,Y,Z channels to RGB channels", 2.0: "Checkbox", Default = cxyz if cxyz else 1, "Width": 1.0},
  193.         8.0: { 1.0: "verbose", "Name": "Verbose Logging", 2.0: "Checkbox", "Default": verbose if verbose else 0, "Width": 1.0},
  194.     }
  195.  
  196.     return dialog
  197.  
  198.  
  199. ########################################################################
  200. ## Logging
  201. ########################################################################
  202. def _log(mode, message):
  203.     return "[{0}] {1}".format(mode, message)
  204.  
  205.  
  206. def logError(message):
  207.     print(_log("ERROR", message))
  208.  
  209.  
  210. def logDebug(message, state):
  211.     if state == 1 or state == True:
  212.         ## Only print the logDebug output when the debugging "state" is enabled
  213.         print(_log("DEBUG", message))
  214.    
  215.  
  216.  
  217. def logDump(variable, state):
  218.     if state == 1 or state == True:
  219.         ## Only print the logDump output when the debugging "state" is enabled
  220.         # dump(variable) FIXME: Can't dump in Python
  221.         pass
  222.    
  223.  
  224.  
  225. def logWarning(message):
  226.     print(_log("WARNING", message))
  227.  
  228.  
  229. ########################################################################
  230. ## Loader
  231. ########################################################################
  232. def getLoaderChannels(loader):
  233.     ## Get all loader channel and filter out the ones to skip
  234.     sourceChannels = loader.Clip1.OpenEXRFormat.RedName.GetAttrs()["INPIDT_ComboControl_ID"]
  235.     channels = []
  236.    
  237.     for i, channelName in sourceChannels.items():
  238.         if not channelName.lower() in CHANNELS_TO_SKIP:
  239.             channels.append(channelName)
  240.        
  241.    
  242.    
  243.     ## Assume a deranged monkey wrote out the channels and decided to scramble 'm all
  244.     channels.sort()
  245.    
  246.     return channels
  247.  
  248.  
  249. def getChannelData(loaderChannels):
  250.     ## Gets the channels by prefix from given loader channels
  251.     _channelPrefixSet = {}
  252.     channelData = {}
  253.    
  254.     for channelName in loaderChannels:
  255.         ## Get prefix and channel from full channel name
  256.         try:
  257.             prefix, channel = re.match("(.+)\.(.+)", channelName).groups()
  258.         except AttributeError:
  259.             continue
  260.        
  261.         channels = []
  262.        
  263.         ## Check if prefix was already stored in tmp set
  264.         if prefix in _channelPrefixSet:
  265.             ## Get previously assigned channels of current prefix
  266.             channels = channelData[prefix]
  267.         else:
  268.             ## Store prefix
  269.             _channelPrefixSet[prefix] = True
  270.        
  271.        
  272.         ## Add full channel name to assigned channels of current prefix
  273.         channels.append(channelName)
  274.  
  275.         ## Store channels for current prefix
  276.         channelData[prefix] = channels
  277.        
  278.    
  279.  
  280.     return channelData
  281.  
  282.  
  283. def getLoader(verbose, dialogResult, tool):
  284.     ## Process an individual loader node
  285.    
  286.     ## Get node attributes
  287.     attrs = tool.GetAttrs()
  288.  
  289.     ## Ensure node is a loader
  290.     node_type = attrs["TOOLS_RegID"]
  291.     if node_type != "Loader":
  292.         logError("Selected tool is not a Loader")
  293.         return
  294.    
  295.  
  296.     ## Ensure loader clip is valid
  297.     loader_clip = tool.Clip[comp.CurrentTime]
  298.     if not loader_clip:
  299.         logError("Loader contains no clips to explore")
  300.         return
  301.    
  302.  
  303.     ## Ensure loader clip is of format EXR
  304.     loader_clip_format = attrs["TOOLST_Clip_FormatName"][1]
  305.     if loader_clip_format != "OpenEXRFormat":
  306.         logError("Loader is not an EXR file")
  307.         return
  308.    
  309.  
  310.     ## Print the Loader attributes
  311.     logDebug("[EXR Check 1] [Loader Node] " + str(loader_clip), verbose)
  312.     logDebug("[EXR Check 2] [Loader Node Atttributes]", verbose)
  313.     logDump(attrs, verbose)
  314.  
  315.  
  316.     ## If the Skip Importing Alpha Channels checkbox was enabled: set the alpha channel to "None" on the orignal Loader node
  317.     skipAlpha = dialogResult["skipAlpha"]
  318.     if skipAlpha == 1:
  319.         tool.Clip1.OpenEXRFormat.AlphaName = CHANNEL_NO_MATCH
  320.    
  321.  
  322.     ## Filter out None,R,G,B and A as these are already used by the original Loader
  323.     ## If the Skip Importing Alpha Channels checkbox was enabled in the AskUser dialog: keep the alpha channel separate as an extra loader item
  324.     loaderChannels = getLoaderChannels(tool)
  325.  
  326.     ## Debug print the channel list
  327.     logDebug("[EXR Check 3] [Sorted Channel List]", verbose)
  328.     logDump(loaderChannels, verbose)
  329.  
  330.     ## Get list of unique channel prefixes to know how many loaders to create
  331.     channelData = getChannelData(loaderChannels)
  332.  
  333.     ## Debug print the loader node list
  334.     logDebug("[EXR Check 4] [Loader Node List]", verbose)
  335.     logDump(channelData, verbose)
  336.  
  337.     flow = comp.CurrentFrame.FlowView
  338.     org_x_pos, org_y_pos = flow.GetPosTable(tool).values()
  339.  
  340.     ## Used to add to the placement offset for the tool in the flow
  341.     count = 1
  342.  
  343.     ## Work out the node spacing offsets in the flow area
  344.     y_pos_add = 0
  345.     if tiles == True:
  346.         y_pos_add = 3
  347.     else:
  348.         y_pos_add = 1
  349.    
  350.  
  351.     ## Update the loader node channel settings
  352.     comp.Lock()
  353.  
  354.     for prefix, channels in channelData.items():
  355.         ## Debug print the loader list
  356.         logDebug("[EXR Check 5] Loader List " + prefix, verbose)
  357.  
  358.         ## Resolve the Loader clip name
  359.         ## loader = Loader({Clip = loader_clip})
  360.         loader = comp.Loader({"Clip" : loader_clip})
  361.  
  362.         ## Force an initial (invalid) EXR channel name value into the OpenEXRFormat setting
  363.         loader.SetAttrs({"TOOLB_NameSet" : True, "TOOLS_Name" : prefix})
  364.         loader.Clip1.OpenEXRFormat.RedName = CHANNEL_NO_MATCH
  365.         loader.Clip1.OpenEXRFormat.GreenName = CHANNEL_NO_MATCH
  366.         loader.Clip1.OpenEXRFormat.BlueName = CHANNEL_NO_MATCH
  367.         loader.Clip1.OpenEXRFormat.AlphaName = CHANNEL_NO_MATCH
  368.         loader.Clip1.OpenEXRFormat.XName = CHANNEL_NO_MATCH
  369.         loader.Clip1.OpenEXRFormat.YName = CHANNEL_NO_MATCH
  370.         loader.Clip1.OpenEXRFormat.ZName = CHANNEL_NO_MATCH
  371.  
  372.         ## Refresh the OpenEXRFormat setting using real channel name data in a 2nd stage
  373.         for channelName in channels:
  374.             channel = re.match(".(.+)$", channelName).group(1) # There was no protection against a non-match here in the original script.
  375.             _channelLower = channel.lower()
  376.             ## Perform a channel match for rerers that use a single letter character at the  of the channel name to define the red/green/blue channels
  377.             ## Example: Vray names its channels like "lighting.R"
  378.             if (_channelLower == "r") or (_channelLower == "red"):
  379.                 ## Red channel found
  380.                 loader.Clip1.OpenEXRFormat.RedName = channelName
  381.                 logDebug("[EXR Check 6] [Red Channel Assignment] " + channelName, verbose)
  382.             elif (_channelLower == "g") or (_channelLower == "green"):
  383.                 ## Green channel found
  384.                 loader.Clip1.OpenEXRFormat.GreenName = channelName
  385.                 logDebug("[EXR Check 6] [Green Channel Assignment] " + channelName, verbose)
  386.             elif (_channelLower == "b") or (_channelLower == "blue"):
  387.                 ## Blue channel found
  388.                 loader.Clip1.OpenEXRFormat.BlueName = channelName
  389.                 logDebug("[EXR Check 6] [Blue Channel Assignment] " + channelName, verbose)
  390.             elif (_channelLower == "a") or (_channelLower == "alpha"):
  391.                 ## Alpha channel found
  392.                 if (skipAlpha == 0):
  393.                     ## Load the regular alpha channel
  394.                     loader.Clip1.OpenEXRFormat.AlphaName = channelName
  395.                     logDebug("[EXR Check 6] [Alpha Channel Assignment] " + channelName, verbose)
  396.                 else:
  397.                     ## The Skip Importing Alpha Channels checkbox was enabled in the AskUser dialog
  398.                     loader.Clip1.OpenEXRFormat.AlphaName = CHANNEL_NO_MATCH
  399.                     logDebug("[EXR Check 6] [Alpha Channel Assignment] " + CHANNEL_NO_MATCH, verbose)
  400.                
  401.             elif (_channelLower == "x"):
  402.                 ## X channel found
  403.                 loader.Clip1.OpenEXRFormat.RedName = channelName
  404.                 logDebug("[EXR Check 6] [X Channel Assignment] " + channelName, verbose)
  405.             elif (_channelLower == "y"):
  406.                 ## Y channel found
  407.                 loader.Clip1.OpenEXRFormat.GreenName = channelName
  408.                 logDebug("[EXR Check 6] [Y Channel Assignment] " + channelName, verbose)
  409.             elif (_channelLower == "z"):
  410.                 ## Z channel found
  411.                 loader.Clip1.OpenEXRFormat.BlueName = channelName
  412.                 logDebug("[EXR Check 6] [Z Channel Assignment] " + channelName, verbose)
  413.             else:
  414.                 ########################################################################
  415.                 ## Check if a Cinema4D (C4D) style EXR channel name is in use
  416.                 ## Perform a channel match for rerers that use a phrase at the  of the channel name to define the red/green/blue channels
  417.                 ## Example: C4D names its channels like "#0005#diffuse_direct.red"
  418.  
  419.                 ## Create a new empty table
  420.                 myTableOfPhrases = {}
  421.                 myIndex = 1
  422.  
  423.                 ## Break a channel name string down into individual phrases using Lua's gmatch def and the . delimiter character:
  424.                 for myPhrase in (match.group(0) for match in re.finditer("[^\.]+", channelName)):
  425.                 #for myPhrase in string.gmatch(channelName, "[^%.]+") do
  426.                     ## Add each phrase to the existing table
  427.                     myTableOfPhrases[myIndex] = myPhrase
  428.  
  429.                     ## Iterate up the index
  430.                     myIndex = myIndex + 1
  431.                
  432.  
  433.                 ## Scan through the "myTableOfPhrases" Lua table to find final phrase
  434.                 ## Example: The phrase "red" would be extracted from a "#0005#diffuse_direct.red" channel name.
  435.                 tableLength = len(myTableOfPhrases)
  436.                 lastItem = myTableOfPhrases[tableLength]
  437.  
  438.                 ## Debug print the C4D channel assignment
  439.                 logDebug("[EXR Check 7] [C4D Channel Phrase] " + str(lastItem), verbose)
  440.  
  441.                 if (lastItem == "red"):
  442.                     ## C4D red channel found
  443.                     loader.Clip1.OpenEXRFormat.RedName = channelName
  444.                     logDebug("[EXR Check 6] [Red Channel Assignment] " + channelName, verbose)
  445.                 elif (lastItem == "green"):
  446.                     ## C4D green channel found
  447.                     loader.Clip1.OpenEXRFormat.GreenName = channelName
  448.                     logDebug("[EXR Check 6] [Green Channel Assignment] " + channelName, verbose)
  449.                 elif (lastItem == "blue"):
  450.                     ## C4D blue channel found
  451.                     loader.Clip1.OpenEXRFormat.BlueName = channelName
  452.                     logDebug("[EXR Check 6] [Blue Channel Assignment] " + channelName, verbose)
  453.                 elif (lastItem == "alpha"):
  454.                     ## C4D alpha channel found
  455.                     if (skipAlpha == 0):
  456.                         ## Load the regular alpha channel
  457.                         loader.Clip1.OpenEXRFormat.AlphaName = channelName
  458.                         logDebug("[EXR Check 6] [Alpha Channel Assignment] " + channelName, verbose)
  459.                     else:
  460.                         ## the Skip Importing Alpha Channels checkbox was enabled in the AskUser dialog
  461.                         loader.Clip1.OpenEXRFormat.AlphaName = CHANNEL_NO_MATCH
  462.                         logDebug("[EXR Check 6] [Alpha Channel Assignment] " + CHANNEL_NO_MATCH, verbose)
  463.                    
  464.                
  465.            
  466.        
  467.  
  468.         ## Update the node position in the flow area
  469.         flow.SetPos(loader, org_x_pos + (count * cdir), org_y_pos + (y_pos_add * count * (1 - cdir)))
  470.  
  471.         count = count + 1
  472.         if grid > 0:
  473.             if count - 1 >= grid:
  474.                 count = 1
  475.                 org_y_pos = org_y_pos + (y_pos_add * cdir)
  476.                 org_x_pos = org_x_pos + (1 * (1-cdir))
  477.            
  478.        
  479.    
  480.  
  481.     ## Unlock the comp to allow Loader node file dialogs to be displayed
  482.     comp.Unlock()
  483.  
  484.  
  485. ##############################################################################-
  486. ## Main
  487. ##############################################################################-
  488. def main():
  489.     ## Check if Fusion is running
  490.     if not fusion:
  491.         logError("[Error] This is a Blackmagic Fusion lua script, it should be run from within Fusion.")
  492.    
  493.  
  494.     print("[Split EXR] {0}".format(VERSION))
  495.     print("[Created By] {0}".format(AUTHOR))
  496.     print("[With Contributions From] {0}".format(",".join(CONTRIBUTORS)))
  497.     print("\n")
  498.  
  499.     ## Show an AskUser dialog to find out your preferred node placement settings
  500.     ## The AskUser default settings will be pulled from the Fusion preferences.
  501.     dialog = buildDialog()
  502.     dialogResult = comp.AskUser("hos_SplitEXR_Ultra", dialog)
  503.  
  504.     ## Exit the script if the cancel button was pressed in the AskUser dialog
  505.     if dialogResult == None:
  506.         logWarning("You pressed cancel in the \"hos_SplitEXR_Ultra\" dialog.")
  507.         return
  508.    
  509.  
  510.     ## Read the Placement, Grid Placement, and Source Tiles settings from the AskUser dialog
  511.     verbose = dialogResult["verbose"]
  512.     splitAllSelectedNodes = dialogResult["splitAllSelectedNodes"]
  513.     global cdir
  514.     cdir = dialogResult["cdir"]
  515.     skipAlpha = dialogResult["skipAlpha"]
  516.     global tiles
  517.     tiles = dialogResult["tiles"]
  518.     global grid
  519.     grid = dialogResult["grid"]
  520.  
  521.     ## Save the updated preferences
  522.     setPreferenceData("hos_SplitEXR.verbose", verbose, verbose)
  523.     setPreferenceData("hos_SplitEXR.splitAllSelectedNodes", splitAllSelectedNodes, verbose)
  524.     setPreferenceData("hos_SplitEXR.cdir", cdir, verbose)
  525.     setPreferenceData("hos_SplitEXR.skipAlpha", skipAlpha, verbose)
  526.     setPreferenceData("hos_SplitEXR.tiles", tiles, verbose)
  527.     setPreferenceData("hos_SplitEXR.grid", grid, verbose)
  528.     ## setPreferenceData("hos_SplitEXR.cxyz", cxyz, verbose)
  529.  
  530.  
  531.     if splitAllSelectedNodes == 0:
  532.         ## Process only the first actively selected node
  533.         tool = comp.ActiveTool
  534.         if tool != None:
  535.             getLoader(verbose, dialogResult, tool)
  536.         else:
  537.             logError("The \"Active Tool\" selection is empty. Please select a node before running this script again.\n\nNote: If you want to process multiple selected nodes at the same time please enable the \"Split All Selected Nodes\" option in the dialog.\n")
  538.             return
  539.        
  540.     else:
  541.         toolList = comp.GetToolList(True, "Loader")
  542.  
  543.         if len(toolList) == 0:
  544.             logError("The \"tool\" selection is empty. Please select a node before running this script again.")
  545.             return
  546.        
  547.         ## Iterate through each of the selected loader nodes
  548.         for i, tool in toolList.items():
  549.             getLoader(verbose, dialogResult, tool)
  550.        
  551.    
  552.  
  553. if __name__ == "__main__":
  554.     ##############################################################################-
  555.     ## Main
  556.     ##############################################################################-
  557.     ## Keep track of exec time
  558.     t_start = time.time()
  559.  
  560.     ## Run main
  561.     main()
  562.  
  563.     ## Print estimated time of execution in seconds
  564.     print("[Processing Time] {:03f} s".format(time.time() - t_start))
  565.     print("[Done]")
  566.  

User avatar
Iddos
Posts: 12
Joined: Sat Oct 06, 2018 4:21 pm
Been thanked: 2 times

Re: hos_SplitEXR_Ultra: Python version

#2

Post by Iddos » Sat Jul 27, 2019 6:53 am

Great work.
Haven’t tested it yet but any lua transliteration to python is much appreciated!
Thanks.

User avatar
SecondMan
Site Admin
Posts: 3416
Joined: Thu Jul 31, 2014 5:31 pm
Answers: 4
Location: Vancouver, Canada
Been thanked: 78 times
Contact:

Re: hos_SplitEXR_Ultra: Python version

#3

Post by SecondMan » Wed Jul 31, 2019 4:13 pm

Very awesome, thanks!

One community request - typically there is a change log inside of Fuses and Scripts. Sometimes it's very detailed, sometimes just a couple of bullet points, but it's really great and handy that it's there. Would be sweet if you could add your contributions in there, just to keep track... :)

User avatar
bryanfordney
Fusioneer
Posts: 78
Joined: Tue Jan 29, 2019 1:10 pm
Location: Atlanta, GA
Been thanked: 5 times

Re: hos_SplitEXR_Ultra: Python version

#4

Post by bryanfordney » Thu Aug 01, 2019 2:49 pm

I ended up scrapping this due to the way Blender's EXR channels are named being quite a lot different from what this script assumes. So, I made a new EXR splitter but I have only been using it for Blender. The thing I pasted above has no changes in functionality from the original. Just made it as an exercise to understand Lua a bit better and the Fusion API.