Bez popisu
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cryptomatte_utilities.py 46KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297
  1. #
  2. #
  3. # Copyright (c) 2014, 2015, 2016, 2017 Psyop Media Company, LLC
  4. # See license.txt
  5. #
  6. #
  7. __version__ = "1.2.8"
  8. GIZMO_CHANNEL_KNOBS = [
  9. "in00", "in01", "in02", "in03",
  10. "in04", "in05", "in06", "in07",
  11. "in08", "in09", "in10", "in11"
  12. ]
  13. GIZMO_REMOVE_CHANNEL_KNOBS = [
  14. "remove00", "remove01", "remove02", "remove03",
  15. "remove04", "remove05", "remove06", "remove07"
  16. ]
  17. GIZMO_ADD_CHANNEL_KNOBS = [
  18. "add00", "add01", "add02", "add03",
  19. "add04", "add05", "add06", "add07"
  20. ]
  21. CRYPTO_METADATA_LEGAL_PREFIX = ["exr/cryptomatte/", "cryptomatte/"]
  22. CRYPTO_METADATA_DEFAULT_PREFIX = CRYPTO_METADATA_LEGAL_PREFIX[1]
  23. import nuke
  24. import struct
  25. import re
  26. def setup_cryptomatte_ui():
  27. if nuke.GUI:
  28. toolbar = nuke.menu("Nodes")
  29. menu = toolbar.addMenu("Cryptomatte", "cryptomatte_logo.png",index=-1)
  30. menu.addCommand("Cryptomatte", "import cryptomatte_utilities as cu; cu.cryptomatte_create_gizmo();")
  31. menu.addCommand("Decryptomatte All", "import cryptomatte_utilities as cu; cu.decryptomatte_all();")
  32. menu.addCommand("Decryptomatte Selection", "import cryptomatte_utilities as cu; cu.decryptomatte_selected();")
  33. menu.addCommand("Encryptomatte", "import cryptomatte_utilities as cu; cu.encryptomatte_create_gizmo();")
  34. def setup_cryptomatte():
  35. nuke.addOnCreate(lambda: cryptomatte_on_create_event(
  36. nuke.thisNode(), nuke.thisKnob()), nodeClass='Cryptomatte')
  37. nuke.addKnobChanged(lambda: cryptomatte_knob_changed_event(
  38. nuke.thisNode(), nuke.thisKnob()), nodeClass='Cryptomatte')
  39. nuke.addKnobChanged(lambda: encryptomatte_knob_changed_event(
  40. nuke.thisNode(), nuke.thisKnob()), nodeClass='Encryptomatte')
  41. nuke.addOnCreate(lambda: encryptomatte_on_create_event(
  42. nuke.thisNode(), nuke.thisKnob()), nodeClass='Encryptomatte')
  43. #############################################
  44. # Testing
  45. #############################################
  46. class CryptomatteTesting(object):
  47. """ Utility functions for manually running tests.
  48. Returns results if there are failures, otherwise None
  49. Arguments of these functions:
  50. test_filter -- (string) will be matched fnmatch style (* wildcards) to either the name of the TestCase
  51. class or test method.
  52. failfast -- (bool) will stop after a failure, and skip cleanup of the nodes that were created.
  53. """
  54. def get_all_unit_tests(self):
  55. import cryptomatte_utilities_tests as cu_tests
  56. return cu_tests.get_all_unit_tests()
  57. def get_all_nuke_tests(self):
  58. import cryptomatte_utilities_tests as cu_tests
  59. return cu_tests.get_all_nuke_tests()
  60. def run_unit_tests(self, test_filter="", failfast=False):
  61. import cryptomatte_utilities_tests as cu_tests
  62. return cu_tests.run_unit_tests(test_filter, failfast)
  63. def run_nuke_tests(self, test_filter="", failfast=False):
  64. import cryptomatte_utilities_tests as cu_tests
  65. return cu_tests.run_nuke_tests(test_filter, failfast)
  66. tests = CryptomatteTesting()
  67. #############################################
  68. # Hash to float
  69. #############################################
  70. try:
  71. # try with a fast c-implementation, if available ...
  72. import mmh3 as mmh3
  73. except ImportError:
  74. # ... otherwise fallback to the pure python version
  75. import pymmh3 as mmh3
  76. def mm3hash_float(name):
  77. hash_32 = mmh3.hash(name)
  78. exp = hash_32 >> 23 & 255
  79. if (exp == 0) or (exp == 255):
  80. hash_32 ^= 1 << 23
  81. packed = struct.pack('<L', hash_32 & 0xffffffff)
  82. return struct.unpack('<f', packed)[0]
  83. def single_precision(float_in):
  84. import array
  85. return array.array("f", [float_in])[0]
  86. def id_to_rgb(id):
  87. # This takes the hashed id and converts it to a preview color
  88. import ctypes
  89. bits = ctypes.cast(ctypes.pointer(ctypes.c_float(id)), ctypes.POINTER(ctypes.c_uint32)).contents.value
  90. mask = 2 ** 32 - 1
  91. return [0.0, float((bits << 8) & mask) / float(mask), float((bits << 16) & mask) / float(mask)]
  92. def id_to_hex(id):
  93. return "{0:08x}".format(struct.unpack('<I', struct.pack('<f', id))[0])
  94. def layer_hash(layer_name):
  95. return id_to_hex(mm3hash_float(layer_name))[:-1]
  96. #############################################
  97. # Cryptomatte file processing
  98. #############################################
  99. def reset_manifest_cache():
  100. global g_cryptomatte_manf_from_names
  101. global g_cryptomatte_manf_from_IDs
  102. g_cryptomatte_manf_from_names = {}
  103. g_cryptomatte_manf_from_IDs = {}
  104. reset_manifest_cache()
  105. class CryptomatteInfo(object):
  106. def __init__(self, node_in, reload_metadata=False):
  107. """Take a nuke node, such as a read node or a Cryptomatte gizmo,
  108. and Reformat metadata into a dictionary, and collect channel
  109. information."""
  110. self.cryptomattes = {}
  111. self.nuke_node = node_in
  112. self.selection = None
  113. self.filename = None
  114. if not self.nuke_node:
  115. return
  116. exr_metadata_dict = {}
  117. if not reload_metadata:
  118. exr_metadata_dict = self._load_minimal_metadata()
  119. if not exr_metadata_dict:
  120. exr_metadata_dict = self.nuke_node.metadata(view=nuke.thisView()) or {}
  121. default_selection = None
  122. self.cachable_metadata = {}
  123. for key, value in exr_metadata_dict.iteritems():
  124. if key == "input/filename":
  125. self.filename = value
  126. self.cachable_metadata[key] = value
  127. for prefix in CRYPTO_METADATA_LEGAL_PREFIX:
  128. if not key.startswith(prefix):
  129. continue
  130. numbered_key = key[len(prefix):] # ex: "exr/cryptomatte/ae93ba3/name" --> "ae93ba3/name"
  131. metadata_id, partial_key = numbered_key.split("/") # ex: "ae93ba3/name" --> ae93ba3, "name"
  132. if metadata_id not in self.cryptomattes:
  133. self.cryptomattes[metadata_id] = {}
  134. if partial_key == "name":
  135. value = _legal_nuke_layer_name(value)
  136. self.cryptomattes[metadata_id][partial_key] = value
  137. self.cryptomattes[metadata_id]['md_prefix'] = prefix
  138. if partial_key != "manifest":
  139. self.cachable_metadata[key] = value
  140. break
  141. for metadata_id, value in self.cryptomattes.iteritems():
  142. if not "name" in value:
  143. value["name"] = ""
  144. if self.cryptomattes:
  145. default_selection = sorted(
  146. self.cryptomattes.keys(),
  147. key=lambda x: self.cryptomattes[x]["name"])[0]
  148. for metadata_id, value in self.cryptomattes.iteritems():
  149. name = value["name"]
  150. channels = self._identify_channels(name)
  151. self.cryptomattes[metadata_id]["channels"] = channels
  152. self.selection = default_selection
  153. if self.nuke_node.Class() in ["Cryptomatte", "Encryptomatte"]:
  154. selection_name = self.nuke_node.knob("cryptoLayer").getValue()
  155. if selection_name:
  156. valid_selection = self.set_selection(selection_name)
  157. if not valid_selection and not self.nuke_node.knob("cryptoLayerLock").getValue():
  158. self.selection = default_selection
  159. def is_valid(self):
  160. """Checks that the selection is valid."""
  161. if self.selection is None:
  162. return False
  163. if self.selection not in self.cryptomattes:
  164. return False
  165. if "channels" not in self.cryptomattes[self.selection]:
  166. return False
  167. if not self.cryptomattes[self.selection]["channels"]:
  168. return False
  169. return True
  170. def set_selection(self, selection):
  171. """ sets the selection (eg. cryptoObject) based on the name.
  172. Returns true if successful.
  173. """
  174. selection = _legal_nuke_layer_name(selection)
  175. for num in self.cryptomattes:
  176. if self.cryptomattes[num]["name"] == selection:
  177. self.selection = num
  178. return True
  179. self.selection = None
  180. return False
  181. def get_selection_metadata_key(self, key):
  182. return self.cryptomattes[self.selection]["md_prefix"] + self.selection + "/" + key
  183. def get_cryptomatte_names(self):
  184. """ gets the names of the cryptomattes contained the file, which
  185. are the possible selections or cryptomatte channels.
  186. """
  187. return [self.cryptomattes[x]["name"] for x in self.cryptomattes]
  188. def get_channels(self):
  189. return self.cryptomattes[self.selection]["channels"] if self.selection else None
  190. def get_selection_name(self):
  191. return self.cryptomattes[self.selection]["name"] if self.selection else None
  192. def get_metadata_cache(self):
  193. import json
  194. return json.dumps(self.cachable_metadata)
  195. def _load_minimal_metadata(self):
  196. """ Returns {} if no quicklky accessible metadata is found,
  197. otherwise returns only metadata accessible without loading everything,
  198. and no manifest (which may be large).
  199. """
  200. import json
  201. if not "metadataCache" in self.nuke_node.knobs():
  202. return {}
  203. metadata_cache = self.nuke_node.knob("metadataCache").getValue()
  204. if metadata_cache:
  205. return json.loads(metadata_cache)
  206. else:
  207. return {}
  208. def _identify_channels(self, name):
  209. """from a name like "cryptoObject",
  210. gets sorted channels, such as cryptoObject00, cryptoObject01, cryptoObject02
  211. """
  212. channel_list = []
  213. if self.nuke_node.Class() in ["Cryptomatte", "Encryptomatte"]:
  214. # nuke_node is a keyer gizmo or encryptomatte gizmo
  215. channel_list = self.nuke_node.node('Input1').channels()
  216. else:
  217. # nuke_node might a read node
  218. channel_list = self.nuke_node.channels()
  219. # regex for "cryptoObject" + digits + ending with .red or .r
  220. channel_regex = re.compile(r'({name}\d+)\.(?:red|r)$'.format(name=name))
  221. pure_channels = []
  222. for channel in channel_list:
  223. match = channel_regex.match(channel)
  224. if match:
  225. pure_channels.append(match.group(1))
  226. return sorted(pure_channels)[:len(GIZMO_CHANNEL_KNOBS)]
  227. def resolve_manifest_paths(self, exr_path, sidecar_path):
  228. import os
  229. if "\\" in sidecar_path:
  230. print "Cryptomatte: Invalid sidecar path (Back-slashes not allowed): ", sidecar_path
  231. return "" # to enforce the specification.
  232. joined = os.path.join(os.path.dirname(exr_path), sidecar_path)
  233. return os.path.normpath(joined)
  234. def lazy_load_manifest(self):
  235. import json
  236. if 'manifest' not in self.cryptomattes[self.selection]:
  237. manif_key = self.get_selection_metadata_key('manifest')
  238. manif_str = self.nuke_node.metadata(manif_key)
  239. if manif_str is None:
  240. return {}
  241. else:
  242. self.cryptomattes[self.selection]['manifest'] = manif_str
  243. try:
  244. return json.loads(self.cryptomattes[self.selection]['manifest'])
  245. except ValueError, e:
  246. print "Cryptomatte: Unable to parse manifest. (%s)." % e
  247. return {}
  248. def parse_manifest(self):
  249. """ Loads json manifest and unpacks hex strings into floats,
  250. and converts it to two dictionaries, which map IDs to names and vice versa.
  251. Also caches the last manifest in a global variable so that a session of selecting
  252. things does not constantly require reloading the manifest (' ~0.13 seconds for a
  253. 32,000 name manifest.')
  254. """
  255. import json
  256. import struct
  257. import os
  258. num = self.selection
  259. manifest = {}
  260. manif_file = self.cryptomattes[num].get("manif_file", "")
  261. if manif_file:
  262. manif_file = self.resolve_manifest_paths(self.filename, manif_file)
  263. if manif_file:
  264. if os.path.exists(manif_file):
  265. try:
  266. with open(manif_file) as json_data:
  267. manifest = json.load(json_data)
  268. except:
  269. print "Cryptomatte: Unable to parse manifest, ", manif_file
  270. else:
  271. print "Cryptomatte: Unable to find manifest file: ", manif_file
  272. else:
  273. manifest = self.lazy_load_manifest()
  274. from_names = {}
  275. from_ids = {}
  276. unpacker = struct.Struct('=f')
  277. packer = struct.Struct("=I")
  278. for name, value in manifest.iteritems():
  279. packed = packer.pack(int(value,16))
  280. packed = packed = '\0' * (4 - len(packed)) + packed
  281. id_float = unpacker.unpack( packed )[0]
  282. name_str = name.encode("utf8")
  283. from_names[name_str] = id_float
  284. from_ids[id_float] = name_str
  285. self.cryptomattes[num]["names_to_IDs"] = from_names
  286. self.cryptomattes[num]["ids_to_names"] = from_ids
  287. global g_cryptomatte_manf_from_names
  288. global g_cryptomatte_manf_from_IDs
  289. g_cryptomatte_manf_from_names = from_names
  290. g_cryptomatte_manf_from_IDs = from_ids
  291. return from_names
  292. def id_to_name(self, ID_value):
  293. """Checks the manifest for the ID value.
  294. Checks the last used manifest first, before decoding
  295. the existing one.
  296. """
  297. global g_cryptomatte_manf_from_IDs
  298. manf_cache = g_cryptomatte_manf_from_IDs
  299. if (type(manf_cache) is dict and ID_value in manf_cache):
  300. return g_cryptomatte_manf_from_IDs[ID_value]
  301. elif self.selection != None:
  302. self.parse_manifest()
  303. return self.cryptomattes[self.selection]["ids_to_names"].get(ID_value, None)
  304. else:
  305. return None
  306. def name_to_ID(self, name):
  307. return mm3hash_float(name)
  308. def test_manifest(self, quiet=False):
  309. """Testing function to check for implementation errors and hash collisions.
  310. Checks all names and values in the manifest in the manifest by rehashing them,
  311. to ensure that the entire process is sound. Also finds collisions. Returns a tuple
  312. of errors and collisions.
  313. """
  314. self.parse_manifest()
  315. ids = {}
  316. errors = []
  317. collisions = []
  318. manifest = self.cryptomattes[self.selection]["names_to_IDs"]
  319. for name, idvalue in manifest.iteritems():
  320. if mm3hash_float(name) != idvalue:
  321. errors.append("computed ID doesn't match manifest ID: (%s, %s)" % (idvalue, mm3hash_float(name)))
  322. else:
  323. if idvalue in ids:
  324. collisions.append("colliding: %s %s" % (ids[idvalue], name))
  325. ids[idvalue] = name
  326. if not quiet:
  327. print "Tested %s, %s names" % (self.nuke_node.name(), len(manifest))
  328. print " ", len(errors), "non-matching IDs between python and c++."
  329. print " ", len(collisions), "hash collisions in manifest."
  330. return errors, collisions
  331. def print_hash_info(name):
  332. hash_32 = mmh3.hash(name)
  333. print "Name:", name
  334. print "UTF-8 bytes:", " ".join( hex(ord(x))[2:] for x in name)
  335. print "Hash value (signed):", hash_32
  336. if hash_32 < 0:
  337. hash_32 = (-hash_32 - 1) ^ 0xFFFFFFFF
  338. print "Hash value (unsigned):", hash_32
  339. print "Float converted:", mm3hash_float(name)
  340. #############################################
  341. # Public - Create Crypto Gizmos
  342. #############################################
  343. def cryptomatte_create_gizmo():
  344. return nuke.createNode("Cryptomatte")
  345. def encryptomatte_create_gizmo():
  346. return nuke.createNode("Encryptomatte")
  347. #############################################
  348. # Public - cryptomatte Events
  349. #############################################
  350. def cryptomatte_on_create_event(node = None, knob = None):
  351. # make sure choices are available on load
  352. prev_stop_state = node.knob("stopAutoUpdate").getValue()
  353. node.knob("stopAutoUpdate").setValue(True)
  354. _set_crypto_layer_choice_options(node, CryptomatteInfo(node))
  355. node.knob("stopAutoUpdate").setValue(prev_stop_state)
  356. def cryptomatte_knob_changed_event(node = None, knob = None):
  357. if _limbo_state(node):
  358. return
  359. if knob.name() == "inputChange":
  360. if unsafe_to_do_inputChange(node):
  361. return # see comment in #unsafe_to_do_inputChange.
  362. cinfo = CryptomatteInfo(node, reload_metadata=True)
  363. _update_cryptomatte_gizmo(node, cinfo)
  364. elif knob.name() in ["cryptoLayer", "cryptoLayerLock"]:
  365. cinfo = CryptomatteInfo(node)
  366. _update_cryptomatte_gizmo(node, cinfo)
  367. elif knob.name() in ["cryptoLayerChoice"]:
  368. if not node.knob('cryptoLayerLock').value():
  369. knob_value = int(knob.getValue())
  370. choice_options = knob.values()
  371. if knob_value >= len(choice_options):
  372. return
  373. prev_crypto_layer = node.knob('cryptoLayer').value()
  374. new_crypto_layer = knob.values()[knob_value]
  375. if prev_crypto_layer != new_crypto_layer:
  376. node.knob('cryptoLayer').setValue(new_crypto_layer)
  377. cinfo = CryptomatteInfo(node)
  378. _update_cryptomatte_gizmo(node, cinfo)
  379. # Undo user action on menu
  380. knob.setValue(knob.values().index(node.knob('cryptoLayer').value()))
  381. elif knob.name() == "pickerAdd":
  382. if node.knob("singleSelection").getValue():
  383. node.knob("matteList").setValue("")
  384. ID_value = _get_knob_channel_value(node.knob("pickerAdd"), recursive_mode="add")
  385. if ID_value == 0.0:
  386. return
  387. cinfo = CryptomatteInfo(node)
  388. keyed_object = cinfo.id_to_name(ID_value) or "<%s>" % ID_value
  389. node.knob("pickerRemove").setValue([0] * 8)
  390. _matteList_modify(node, keyed_object, False)
  391. _update_cryptomatte_gizmo(node, cinfo)
  392. elif knob.name() == "pickerRemove":
  393. ID_value = _get_knob_channel_value(node.knob("pickerRemove"), recursive_mode="remove")
  394. if ID_value == 0.0:
  395. return
  396. cinfo = CryptomatteInfo(node)
  397. keyed_object = cinfo.id_to_name(ID_value) or "<%s>" % ID_value
  398. node.knob("pickerAdd").setValue([0] * 8)
  399. _matteList_modify(node, keyed_object, True)
  400. _update_cryptomatte_gizmo(node, cinfo)
  401. elif knob.name() == "matteList":
  402. cinfo = CryptomatteInfo(node)
  403. _update_cryptomatte_gizmo(node, cinfo)
  404. node.knob("pickerRemove").setValue([0] * 8)
  405. node.knob("pickerAdd").setValue([0] * 8)
  406. elif knob.name() in ["previewMode", "previewEnabled"]:
  407. cinfo = CryptomatteInfo(node)
  408. _update_cryptomatte_gizmo(node, cinfo)
  409. elif knob.name() == "forceUpdate":
  410. cinfo = CryptomatteInfo(node, reload_metadata=True)
  411. _update_cryptomatte_gizmo(node, cinfo, True)
  412. def encryptomatte_knob_changed_event(node=None, knob=None):
  413. if _limbo_state(node):
  414. return
  415. if knob.name() in ["matteName", "cryptoLayerLock"]:
  416. cinfo = CryptomatteInfo(node, reload_metadata=True)
  417. _update_encryptomatte_gizmo(node, cinfo)
  418. return
  419. if knob.name() in ["setupLayers", "cryptoLayer", "inputChange", "cryptoLayers"]:
  420. if knob.name() == "inputChange":
  421. if unsafe_to_do_inputChange(node):
  422. return # see comment in #unsafe_to_do_inputChange.
  423. _update_encyptomatte_setup_layers(node)
  424. cinfo = CryptomatteInfo(node, reload_metadata=True)
  425. _update_encryptomatte_gizmo(node, cinfo)
  426. def encryptomatte_on_create_event(node = None, knob = None):
  427. node.knob('cryptoLayers').setEnabled(node.knob('setupLayers').value())
  428. #############################################
  429. # Public - cryptomatte functions
  430. #############################################
  431. def update_cryptomatte_gizmo(node, force=False):
  432. """
  433. Not invoked by gizmo button.
  434. The gizmo button relies on knob changed callbacks, to avoid
  435. recursive evaluation of callbacks.
  436. """
  437. cinfo = CryptomatteInfo(node, reload_metadata=True)
  438. _update_cryptomatte_gizmo(node, cinfo, force=force)
  439. def clear_cryptomatte_gizmo(node):
  440. """Relies on knob changed callbacks to update gizmo after values change."""
  441. node.knob("matteList").setValue("")
  442. node.knob("expression").setValue("")
  443. def update_all_cryptomatte_gizmos():
  444. return _force_update_all()
  445. def update_encryptomatte_gizmo(node, force=False):
  446. cinfo = CryptomatteInfo(node, reload_metadata=True)
  447. _update_encryptomatte_gizmo(node, cinfo, force)
  448. def clear_encryptomatte_gizmo(node):
  449. node.knob("matteName").setValue("")
  450. _update_encryptomatte_gizmo(node, cinfo, True)
  451. #############################################
  452. # Utils - Update Gizmi
  453. # (gizmi is the plural of gizmo)
  454. #############################################
  455. def _cancel_update(gizmo, force):
  456. try:
  457. stopAutoUpdate = gizmo.knob("stopAutoUpdate").getValue()
  458. except:
  459. # This happens sometimes on creation. I don't really get it, but this seems to fix it.
  460. return True
  461. if (not force and stopAutoUpdate == 1.0):
  462. return True
  463. else:
  464. return False
  465. def _force_update_all():
  466. with nuke.root():
  467. node_count = 0
  468. for node in nuke.allNodes():
  469. if node.Class() == "Cryptomatte":
  470. node_count = node_count + 1
  471. cinfo = CryptomatteInfo(node, reload_metadata=True)
  472. _update_cryptomatte_gizmo(node, cinfo, force=True)
  473. nuke.message("Updated %s cryptomatte gizmos." % node_count)
  474. def unsafe_to_do_inputChange(node):
  475. """
  476. In Nuke 8, 9, 10, 11, 12 it's been discovered that when copy and pasting certain nodes,
  477. or when opening certain scripts the inputchanged knob change callback breaks the script.
  478. What actually happens is the call to metadata() breaks it.
  479. The only reliable way to notice that it's unsafe we've found is calling node.screenHeight(),
  480. and if zero, stopping.
  481. see: https://github.com/Psyop/Cryptomatte/issues/18
  482. """
  483. return nuke.NUKE_VERSION_MAJOR > 7 and node.screenHeight() == 0
  484. def _limbo_state(gizmo):
  485. """
  486. Checks if the node is in a limbo state. This happens when creating
  487. and connected a node at the same time. It manifests as the error,
  488. ValueError: A PythonObject is not attached to a node
  489. """
  490. try:
  491. gizmo.Class()
  492. except ValueError:
  493. return True
  494. return False
  495. #############################################
  496. # Utils - Update Gizmi
  497. #############################################
  498. def _set_channels(gizmo, channels, layer_name):
  499. gizmo.knob("cryptoLayer").setValue(layer_name)
  500. for i, knob_name in enumerate(GIZMO_CHANNEL_KNOBS):
  501. channel = channels[i] if i < len(channels) else "none"
  502. gizmo.knob(knob_name).setValue(channel)
  503. def _set_metadata_cache(gizmo, cinfo):
  504. gizmo.knob('metadataCache').setValue(cinfo.get_metadata_cache())
  505. def _set_crypto_layer_choice_options(gizmo, cinfo):
  506. layer_locked = gizmo.knob('cryptoLayerLock').value()
  507. values = sorted([v.get('name', '') for v in cinfo.cryptomattes.values()])
  508. gizmo.knob('cryptoLayerChoice').setEnabled(not layer_locked)
  509. gizmo.knob("cryptoLayerChoice").setValues(values)
  510. return values
  511. def _set_crypto_layer_choice(gizmo, cinfo):
  512. values = _set_crypto_layer_choice_options(gizmo, cinfo)
  513. current_selection = cinfo.get_selection_name()
  514. if current_selection:
  515. gizmo.knob("cryptoLayerChoice").setValue(values.index(current_selection))
  516. def _update_cryptomatte_gizmo(gizmo, cinfo, force=False):
  517. if _cancel_update(gizmo, force):
  518. return
  519. _set_metadata_cache(gizmo, cinfo)
  520. if not cinfo.is_valid():
  521. return
  522. cryptomatte_channels = cinfo.get_channels()
  523. if not cryptomatte_channels:
  524. return
  525. _set_channels(gizmo, cryptomatte_channels, cinfo.get_selection_name())
  526. _set_expression(gizmo, cryptomatte_channels)
  527. _set_preview_expression(gizmo, cryptomatte_channels)
  528. _set_crypto_layer_choice(gizmo, cinfo)
  529. def _legal_nuke_layer_name(name):
  530. """ Blender produces channels with certain characters in the name, which Nuke
  531. changes to "_". We have to make sure we handle Cryptomattes
  532. that are built this way. Doing this by only allowing alphanumeric
  533. output plus dash and underscores
  534. """
  535. prefix = ""
  536. if name and name[0] in "0123456789":
  537. prefix = "_"
  538. return prefix + "".join([x if x.lower() in 'abcdefghijklmnopqrstuvwxyz1234567890_-' else '_' for x in name])
  539. def _update_encryptomatte_gizmo(gizmo, cinfo, force=False):
  540. if _cancel_update(gizmo, force):
  541. return
  542. def reset_gizmo(gizmo):
  543. _set_channels(gizmo, [], "")
  544. gizmo.knob("alphaExpression").setValue("")
  545. matte_name = gizmo.knob('matteName').value()
  546. matte_input = gizmo.input(1)
  547. _set_metadata_cache(gizmo, cinfo)
  548. if matte_name == "" and not matte_input is None:
  549. matte_name = matte_input.name()
  550. gizmo.knob('matteName').setValue(matte_name)
  551. if matte_name == "":
  552. gizmo.knob('id').setValue(0.0)
  553. gizmo.knob('idHex').setValue('')
  554. gizmo.knob('previewColor').setValue([0.0, 0.0, 0.0])
  555. else:
  556. id_value = mm3hash_float(matte_name)
  557. gizmo.knob('id').setValue(id_value)
  558. gizmo.knob('idHex').setValue(id_to_hex(id_value))
  559. gizmo.knob('previewColor').setValue(id_to_rgb(id_value))
  560. if gizmo.knob('setupLayers').value():
  561. gizmo.knob('cryptoLayers').setEnabled(True)
  562. if cinfo.is_valid():
  563. cryptomatte_channels = cinfo.get_channels()
  564. if not cryptomatte_channels:
  565. cryptomatte_channels = []
  566. else:
  567. cryptomatte_channels = []
  568. crypto_layer = gizmo.knob('cryptoLayer').value()
  569. crypto_layer = _legal_nuke_layer_name(crypto_layer)
  570. if not crypto_layer:
  571. return reset_gizmo(gizmo)
  572. if crypto_layer in cryptomatte_channels:
  573. gizmo.knob('inputCryptoLayers').setValue(len(cryptomatte_channels) - 1)
  574. manifest_key = cinfo.get_selection_metadata_key("")
  575. gizmo.knob('manifestKey').setValue(manifest_key)
  576. gizmo.knob('newLayer').setValue(False)
  577. else:
  578. gizmo.knob('inputCryptoLayers').setValue(0)
  579. gizmo.knob('manifestKey').setValue(
  580. CRYPTO_METADATA_DEFAULT_PREFIX + layer_hash(crypto_layer) + '/')
  581. gizmo.knob('newLayer').setValue(True)
  582. cryptomatte_channels = [
  583. crypto_layer + "{0:02d}".format(i)
  584. for i in range(int(gizmo.knob('cryptoLayers').value()))
  585. ]
  586. _set_channels(gizmo, cryptomatte_channels, crypto_layer)
  587. else:
  588. gizmo.knob('cryptoLayers').setEnabled(False)
  589. if not cinfo.is_valid():
  590. return
  591. cryptomatte_channels = cinfo.get_channels()
  592. if not cryptomatte_channels:
  593. return
  594. gizmo.knob('newLayer').setValue(False)
  595. _set_channels(gizmo, cryptomatte_channels, cinfo.get_selection_name())
  596. gizmo.knob('inputCryptoLayers').setValue(len(cryptomatte_channels) - 1)
  597. gizmo.knob('cryptoLayers').setValue(len(cryptomatte_channels) - 1)
  598. manifest_key = cinfo.get_selection_metadata_key("")
  599. gizmo.knob('manifestKey').setValue(manifest_key)
  600. gizmo.knob("alphaExpression").setValue(_build_extraction_expression(cryptomatte_channels, [0.0]))
  601. def _update_encyptomatte_setup_layers(gizmo):
  602. setup_layers = gizmo.knob('setupLayers').value()
  603. num_layers = gizmo.knob('cryptoLayers').value()
  604. input_layers = gizmo.knob('inputCryptoLayers').value()
  605. crypto_layer = _legal_nuke_layer_name(gizmo.knob('cryptoLayer').value())
  606. if not setup_layers:
  607. gizmo.knob('manifestKey').setValue("")
  608. for ch_add, ch_remove in zip(GIZMO_ADD_CHANNEL_KNOBS, GIZMO_REMOVE_CHANNEL_KNOBS):
  609. gizmo.knob(ch_add).setValue("none")
  610. gizmo.knob(ch_remove).setValue("none")
  611. return
  612. all_layers = nuke.layers()
  613. num_ch = len(GIZMO_ADD_CHANNEL_KNOBS)
  614. for i, ch_add, ch_remove in zip(
  615. range(num_ch), GIZMO_ADD_CHANNEL_KNOBS, GIZMO_REMOVE_CHANNEL_KNOBS):
  616. this_layer = "{0}{1:02d}".format(crypto_layer, i)
  617. # Add
  618. if i < num_layers:
  619. if not this_layer in all_layers:
  620. channels = ["%s.%s" % (this_layer, c) for c in ['red', 'green', 'blue', 'alpha']]
  621. nuke.Layer(this_layer, channels)
  622. gizmo.knob(ch_add).setValue(this_layer)
  623. gizmo.knob(ch_remove).setValue("none")
  624. else:
  625. gizmo.knob(ch_add).setValue("none")
  626. if i <= input_layers:
  627. gizmo.knob(ch_remove).setValue(this_layer)
  628. else:
  629. gizmo.knob(ch_remove).setValue("none")
  630. def encryptomatte_add_manifest_id():
  631. node = nuke.thisNode()
  632. parent = nuke.thisParent()
  633. name = parent.knob('matteName').value()
  634. id_hex = parent.knob('idHex').value()
  635. manifest_key = parent.knob('manifestKey').value()
  636. metadata = node.metadata()
  637. manifest = metadata.get(manifest_key + 'manifest', "{}")
  638. # Add another item, with closing bracket
  639. last_item = '"%s":"%s"}' % (name, id_hex)
  640. last_bracket_pos = manifest.rfind('}')
  641. existing_items = manifest[:last_bracket_pos].rstrip()
  642. if not existing_items.endswith(',') and not existing_items.endswith('{'):
  643. existing_items += ','
  644. existing_items += last_item
  645. return existing_items
  646. #############################################
  647. # Utils - Troubleshooting
  648. #############################################
  649. def _troubleshoot_gizmo(gizmo):
  650. MSG_WRONGTYPE = 'Troubleshooting: Cannot troubleshoot non-Cryptomatte nodes'
  651. MSG_UNCONNECTED = 'Cryptomatte gizmo is not plugged into anything'
  652. MSG_NONE_FOUND = 'No Cryptomattes found in input. '
  653. MSG_LYR_UNSET = ('Gizmo needs updating, layer is not set. '
  654. 'Press "force update" or reconnect.')
  655. MSG_LYR_UNPOP = ('Gizmo needs updating, layer menu is not populated. '
  656. 'Press "force update" or reconnect.')
  657. MSG_ODD_STATE = ('Gizmo is in an odd state and needs updating. '
  658. 'Press "force update" or reconnect.')
  659. MSG_INVALID_SEL = ('Layer is set to "%s", which is unavailable. '
  660. 'Select another layer.')
  661. if gizmo.Class() != "Cryptomatte":
  662. return [MSG_WRONGTYPE]
  663. issues = []
  664. if not gizmo.input(0):
  665. issues.append(MSG_UNCONNECTED)
  666. else:
  667. cinfo = CryptomatteInfo(gizmo, reload_metadata=True)
  668. available = cinfo.get_cryptomatte_names()
  669. selection = gizmo.knob('cryptoLayer').value()
  670. menu_selection = gizmo.knob('cryptoLayerChoice').value()
  671. if not cinfo.cryptomattes:
  672. issues.append(MSG_NONE_FOUND)
  673. else:
  674. if not selection or not menu_selection:
  675. issues.append(MSG_LYR_UNSET)
  676. if menu_selection not in available:
  677. issues.append(MSG_LYR_UNPOP)
  678. elif selection not in gizmo.knob(GIZMO_CHANNEL_KNOBS[0]).value():
  679. issues.append(MSG_ODD_STATE)
  680. elif selection not in available:
  681. issues.append(MSG_INVALID_SEL % selection)
  682. return issues
  683. def _troubleshoot_setup():
  684. MSG_BAD_INSTALL = ("Installation is wrong, callbacks were not found. "
  685. "setup_cryptomatte() did not run, init.py may "
  686. "not be set up properly. ")
  687. PROXY_MODE = "Proxy mode is on, this can cause artifacts. "
  688. issues = []
  689. expected_knob_changeds = ["Cryptomatte", "Encryptomatte"]
  690. if any(x not in nuke.callbacks.knobChangeds for x in expected_knob_changeds):
  691. issues.append(MSG_BAD_INSTALL)
  692. if nuke.root().knob('proxy').value():
  693. issues.append(PROXY_MODE)
  694. return issues
  695. def troubleshoot_gizmo(node):
  696. issues = _troubleshoot_gizmo(node) + _troubleshoot_setup()
  697. if issues:
  698. nuke.message("Troubleshooting: Found the following issues: %s" %
  699. "\n - ".join([""] + issues))
  700. else:
  701. nuke.message("Troubleshooting: All good!")
  702. #############################################
  703. # Utils - Unload Manifest
  704. #############################################
  705. def unload_manifest(node):
  706. source_node = None
  707. if node.Class() == "Cryptomatte":
  708. source_node = node.input(0)
  709. if not source_node:
  710. nuke.message('Cryptomatte is not plugged into anything')
  711. return
  712. else:
  713. source_node = node
  714. cinfo = CryptomatteInfo(node, reload_metadata=True)
  715. if not cinfo.is_valid():
  716. nuke.message("Gizmo's cryptomatte selection is not valid or no cryptomattes are available. ")
  717. return
  718. names_to_IDs = cinfo.parse_manifest();
  719. if not names_to_IDs:
  720. nuke.message('No Cryptomatte manifest was found in the input. ')
  721. return
  722. new_keyers = []
  723. if nuke.ask('There are %s named mattes in the manifest, are you sure you want to create keyer nodes for all of them?' % len(names_to_IDs)):
  724. with nuke.root():
  725. dot = nuke.nodes.Dot()
  726. dot.setInput(0, source_node)
  727. progress = 0
  728. task = nuke.ProgressTask("Unloading Manifest")
  729. for name, metadata_ID in names_to_IDs.iteritems():
  730. if task.isCancelled():
  731. break
  732. task.setMessage("Creating Cryptomatte Keyer for %s" % name)
  733. task.setProgress( int(float(progress) / float(len(names_to_IDs)) * 100))
  734. keyer = nuke.nodes.Cryptomatte(name="ck_%s" % name, matteList=name, matteOnly=True)
  735. keyer.setInput(0, dot)
  736. _update_cryptomatte_gizmo(keyer, cinfo)
  737. new_keyers.append(keyer)
  738. progress = progress + 1
  739. return new_keyers
  740. #############################################
  741. # Utils - Build Expressions
  742. #############################################
  743. def _is_number(s):
  744. try:
  745. float(s)
  746. return True
  747. except ValueError:
  748. return False
  749. def _set_expression(gizmo, cryptomatte_channels):
  750. matte_list_str = gizmo.knob("matteList").getValue()
  751. ID_list = []
  752. matte_list = get_mattelist_as_set(gizmo)
  753. for item in matte_list:
  754. if item.startswith("<") and item.endswith(">"):
  755. numstr = item[1:-1]
  756. if _is_number(numstr):
  757. ID_list.append(single_precision(float(numstr)))
  758. else:
  759. ID_list.append(mm3hash_float(item))
  760. expression = _build_extraction_expression(cryptomatte_channels, ID_list)
  761. gizmo.knob("expression").setValue(expression)
  762. def _build_condition(condition, IDs):
  763. conditions = []
  764. for ID in IDs:
  765. conditions.append( condition.replace("ID", str(ID)) )
  766. return " || ".join(conditions)
  767. def _build_extraction_expression(channel_list, IDs):
  768. if not IDs:
  769. return ""
  770. iterated_expression = "({red_condition} ? sub_channel.green : 0.0) + ({blue_condition} ? sub_channel.alpha : 0.0) + more_work_needed"
  771. subcondition_red = "sub_channel.red == ID"
  772. subcondition_blue = "sub_channel.blue == ID"
  773. expression = ""
  774. for channel in channel_list:
  775. condition_r = _build_condition(subcondition_red, IDs)
  776. condition_b = _build_condition(subcondition_blue, IDs)
  777. channel_expression = iterated_expression.replace("red_condition", condition_r).replace("blue_condition", condition_b)
  778. channel_expression = channel_expression.replace("sub_channel", channel)
  779. if not expression:
  780. expression = channel_expression
  781. else:
  782. expression = expression.replace("more_work_needed", channel_expression)
  783. expression = expression.replace("more_work_needed", "0")
  784. expression = expression.replace("{", "(")
  785. expression = expression.replace("}", ")")
  786. return expression
  787. def _set_preview_expression(gizmo, cryptomatte_channels):
  788. enabled = gizmo.knob('previewEnabled').getValue()
  789. preview_mode = gizmo.knob('previewMode').value() if enabled else 'None'
  790. channel_pairs = []
  791. for c in cryptomatte_channels:
  792. channel_pairs.append(('%s.red' % c, '%s.green' % c))
  793. channel_pairs.append(('%s.blue' % c, '%s.alpha' % c))
  794. expressions = []
  795. if preview_mode == 'Edges':
  796. expressions = [
  797. "",
  798. "",
  799. "",
  800. "2.0 * %s" % channel_pairs[1][1],
  801. ]
  802. elif preview_mode == 'Colors':
  803. """
  804. Generates an expression like this:
  805. red = (
  806. (mantissa(abs(c00.red)) * 1 % 0.25) * c00.green +
  807. (mantissa(abs(c00.blue)) * 1 % 0.25) * c00.alpha +
  808. (mantissa(abs(c01.red)) * 1 % 0.25) * c01.green +
  809. (mantissa(abs(c01.blue)) * 1 % 0.25) * c01.alpha
  810. )
  811. green = (
  812. (mantissa(abs(c00.red)) * 4 % 0.25) * c00.green +
  813. (mantissa(abs(c00.blue)) * 4 % 0.25) * c00.alpha +
  814. (mantissa(abs(c01.red)) * 4 % 0.25) * c01.green +
  815. (mantissa(abs(c01.blue)) * 4 % 0.25) * c01.alpha
  816. )
  817. blue = (
  818. (mantissa(abs(c00.red)) * 16 % 0.25) * c00.green +
  819. (mantissa(abs(c00.blue)) * 16 % 0.25) * c00.alpha +
  820. (mantissa(abs(c01.red)) * 16 % 0.25) * c01.green +
  821. (mantissa(abs(c01.blue)) * 16 % 0.25) * c01.alpha
  822. )
  823. """
  824. puzzle_layers = 4
  825. factors = [1, 16, 64]
  826. puzzle_bits = "(mantissa(abs({id_chan})) * {factor} % 0.25) * {cov_chan}"
  827. for factor in factors:
  828. puzzle = " + ".join(
  829. puzzle_bits.format(factor=factor, id_chan=id_chan, cov_chan=cov_chan)
  830. for (id_chan, cov_chan) in channel_pairs[:puzzle_layers])
  831. expr = "(%s)" % puzzle
  832. expressions.append(expr)
  833. expressions.append("")
  834. else: # mode is none
  835. expressions = ["", "", "", ""]
  836. for i in range(4):
  837. gizmo.knob('previewExpression' + str(i)).setValue(expressions[i])
  838. #############################################
  839. # Utils - Manifest Processing Helpers
  840. #############################################
  841. def _id_from_matte_name(name):
  842. if name.startswith('<') and name.endswith('>') and _is_number(name[1:-1]):
  843. return single_precision(float(name[1:-1]))
  844. else:
  845. return mm3hash_float(name)
  846. def _get_knob_channel_value(knob, recursive_mode=None):
  847. bbox = knob.getValue()[4:]
  848. node = knob.node()
  849. upstream_node = node.input(0)
  850. if not upstream_node:
  851. return 0.0
  852. if recursive_mode is None:
  853. id_list = []
  854. else:
  855. matte_list = get_mattelist_as_set(node)
  856. id_list = map(_id_from_matte_name, matte_list)
  857. saw_bg = False
  858. add_mode = recursive_mode == "add"
  859. rm_mode = recursive_mode == "remove"
  860. for layer_knob in GIZMO_CHANNEL_KNOBS:
  861. layer = node.knob(layer_knob).value()
  862. if layer == "none":
  863. return 0.0
  864. for id_suffix, cov_suffix in [('.red', '.green'), ('.blue', '.alpha')]:
  865. id_chan = layer + id_suffix
  866. cov_chan = layer + cov_suffix
  867. selected_id = upstream_node.sample(id_chan, bbox[0], bbox[1])
  868. selected_coverage = upstream_node.sample(cov_chan, bbox[0], bbox[1])
  869. if add_mode or rm_mode:
  870. if selected_id == 0.0 or selected_coverage == 0.0:
  871. # Seen bg twice? Select bg.
  872. if saw_bg:
  873. break
  874. saw_bg = True
  875. continue
  876. in_list = selected_id in id_list
  877. if (add_mode and not in_list) or (rm_mode and in_list):
  878. return selected_id
  879. else:
  880. # Non-recursive. Just return the first id found.
  881. return selected_id
  882. #############################################
  883. # Utils - Comma seperated list processing
  884. #############################################
  885. def _encode_csv(iterable_items):
  886. """
  887. Encodes CSVs with special characters escaped, and surrounded in quotes
  888. if it contains any of these or spaces, with a space after each comma.
  889. """
  890. cleaned_items = []
  891. need_escape_chars = '"\\'
  892. need_space_chars = ' ,'
  893. for item in iterable_items:
  894. need_escape = any(x in item for x in need_escape_chars)
  895. need_quotes = need_escape or any(x in item for x in need_space_chars)
  896. cleaned = None
  897. if need_escape:
  898. cleaned = ""
  899. for char in item:
  900. if char in need_escape_chars:
  901. cleaned +=( '\\%s' % char )
  902. else:
  903. cleaned += char
  904. else:
  905. cleaned = item
  906. if need_quotes:
  907. cleaned_items.append('"%s"'%cleaned)
  908. else:
  909. cleaned_items.append(cleaned)
  910. result = ", ".join(cleaned_items)
  911. return result
  912. def _decode_csv(input_str):
  913. """ Decodes CSVs into a list of strings. """
  914. import csv
  915. reader = csv.reader([input_str], quotechar='"', delimiter=',', escapechar="\\",
  916. doublequote=False, quoting=csv.QUOTE_ALL, skipinitialspace=True);
  917. result = []
  918. for row in reader:
  919. result += row
  920. return result
  921. def get_mattelist_as_set(gizmo):
  922. matte_list = gizmo.knob("matteList").getValue()
  923. raw_list = _decode_csv(matte_list)
  924. result = set()
  925. for item in raw_list:
  926. item = item.encode("utf-8") if type(item) is unicode else str(item)
  927. result.add(item)
  928. return result
  929. def set_mattelist_from_set(gizmo, matte_items):
  930. " Creates a CSV matte list. "
  931. matte_names_list = list(matte_items)
  932. matte_names_list.sort(key=lambda x: x.lower())
  933. matte_list_str = _encode_csv(matte_names_list)
  934. matte_list_str = matte_list_str.replace("\\", "\\\\")
  935. gizmo.knob("matteList").setValue(matte_list_str)
  936. def _matteList_modify(gizmo, name, remove):
  937. def _matteList_set_add(name, matte_names):
  938. matte_names.add(name)
  939. def _matteList_set_remove(name, matte_names):
  940. if name in matte_names:
  941. matte_names.remove(name) # the simple case
  942. elif name.startswith('<') and name.endswith('>') and _is_number(name[1:-1]):
  943. # maybe it was selected by name before, but is being removed by number
  944. # (manifest was working, now it doesn't)
  945. num = single_precision(float(name[1:-1]))
  946. for existing_name in matte_names:
  947. if mm3hash_float(existing_name) == num:
  948. matte_names.remove(existing_name)
  949. break
  950. else:
  951. # maybe it was selected by number before, but is being removed by name
  952. # (manifest was broken, now it works)
  953. num_str = "<%s>" % mm3hash_float(name)
  954. if num_str in matte_names:
  955. matte_names.remove(num_str) # the simple case
  956. if not name or gizmo.knob("stopAutoUpdate").getValue() == 1.0:
  957. return
  958. matte_names = get_mattelist_as_set(gizmo)
  959. if remove:
  960. _matteList_set_remove(name, matte_names)
  961. else:
  962. _matteList_set_add(name, matte_names)
  963. set_mattelist_from_set(gizmo, matte_names)
  964. #############################################
  965. # Public - Decryption
  966. #############################################
  967. def decryptomatte_all(ask=True):
  968. decryptomatte_nodes(nuke.allNodes(), ask)
  969. def decryptomatte_selected(ask=False):
  970. decryptomatte_nodes(nuke.selectedNodes(), ask)
  971. def decryptomatte_button(node):
  972. if "." in nuke.thisNode().fullName():
  973. parent_name = ".".join(nuke.thisNode().fullName().split(".")[:-1])
  974. with nuke.toNode(parent_name):
  975. decryptomatte_nodes([node], False)
  976. return
  977. with nuke.root():
  978. decryptomatte_nodes([node], False)
  979. def decryptomatte_nodes(nodes, ask):
  980. """ Replaces Cryptomatte gizmos with equivelant nodes. """
  981. gizmos = [n for n in nodes if n.Class() == "Cryptomatte"]
  982. if not gizmos:
  983. return
  984. if not ask or nuke.ask(('Replace %s Cryptomatte gizmos with expression nodes? '
  985. 'Replaced Gizmos will be disabled and selected.') % len(gizmos)):
  986. for gizmo in gizmos:
  987. _decryptomatte(gizmo)
  988. for node in nuke.selectedNodes():
  989. node.knob("selected").setValue(False)
  990. for gizmo in gizmos:
  991. gizmo.knob("selected").setValue(True)
  992. #############################################
  993. # Decryptomatte helpers
  994. #############################################
  995. def _decryptomatte(gizmo):
  996. """ Returns list of new nodes, in order of connections. """
  997. orig_name = gizmo.name()
  998. disabled = gizmo.knob("disable").getValue()
  999. matte_list = gizmo.knob("matteList").value()
  1000. matte_only = gizmo.knob("matteOnly").value()
  1001. expression = gizmo.knob("expression").value()
  1002. matte_output = gizmo.knob("matteOutput").value()
  1003. unpremultiply = gizmo.knob("unpremultiply").value()
  1004. remove_channels = gizmo.knob("RemoveChannels").value()
  1005. # compile list immediate outputs to connect to
  1006. connect_to = []
  1007. for node in gizmo.dependent():
  1008. for i in xrange(node.inputs()):
  1009. input_node = node.input(i)
  1010. if input_node and input_node.fullName() == gizmo.fullName():
  1011. connect_to.append((i, input_node))
  1012. # Modifiy expression to perform premult.
  1013. if unpremultiply and expression:
  1014. expression = "(%s) / (alpha ? alpha : 1)" % expression
  1015. # Setup expression node.
  1016. expr_node = nuke.nodes.Expression(
  1017. inputs=[gizmo], channel0=matte_output, expr0=expression,
  1018. name="%sExtract" % orig_name, disable=disabled)
  1019. expr_node.addKnob(nuke.nuke.String_Knob(
  1020. 'origMatteList', 'Original Matte List', matte_list))
  1021. for knob_name in GIZMO_CHANNEL_KNOBS:
  1022. expr_node.addKnob(nuke.nuke.Channel_Knob(knob_name, "none") )
  1023. expr_node.knob(knob_name).setValue(gizmo.knob(knob_name).value())
  1024. expr_node.knob(knob_name).setVisible(False)
  1025. new_nodes = [expr_node]
  1026. # Add remove channels node, if needed.
  1027. if remove_channels:
  1028. channels2 = matte_output if matte_output != "alpha" else ""
  1029. remove = nuke.nodes.Remove(
  1030. inputs=[expr_node], operation="keep", channels="rgba",
  1031. channels2=channels2, name="%sRemove" % orig_name,
  1032. disable=disabled)
  1033. new_nodes.append(remove)
  1034. # If "matte only" is used, add shuffle node.
  1035. if matte_only:
  1036. shuffle = nuke.nodes.Shuffle(
  1037. name="%sMatteOnly" % orig_name, inputs=[new_nodes[-1]],
  1038. disable=disabled)
  1039. shuffle.knob("in").setValue(matte_output)
  1040. new_nodes.append(shuffle)
  1041. # Disable original
  1042. gizmo.knob("disable").setValue(True)
  1043. # Reconnect outputs
  1044. for inputID, node in connect_to:
  1045. node.setInput(inputID, new_nodes[-1])
  1046. return new_nodes