├── .gitignore ├── CHANGELOG.md ├── README.md ├── docs ├── cryptomatte_logo.svg ├── encryptomatteProperties.png ├── fusion.md ├── fusionInstallDefaults.png ├── fusionInstallUser.png ├── fusionUsageComp.png ├── fusionUsageFuseAdvanced.png ├── fusionUsageFuseControls.png ├── github-zip.png ├── gizmoProperties.png ├── header.png ├── nuke.md └── nukeScreenshot.jpg ├── fusion ├── Config │ └── cryptomatte_shortcuts.fu ├── Fuses │ └── Matte │ │ └── cryptomatte.fuse └── Modules │ └── Lua │ ├── cryptomatte_utilities.lua │ └── test_cryptomatte_utilities.lua ├── license.txt ├── nuke ├── Cryptomatte.gizmo ├── Encryptomatte.gizmo ├── cryptomatte_logo.png ├── cryptomatte_utilities.py ├── cryptomatte_utilities_tests.py ├── init.py ├── menu.py └── pymmh3.py ├── sample_images ├── bunny_CryptoAsset.exr ├── bunny_CryptoMaterial.exr ├── bunny_CryptoObject.exr ├── cornellBox_CryptoWildcard.0001.exr ├── cornellBox_CryptoWildcard.0002.exr ├── debug_images │ ├── about.md │ ├── multichannel.exr │ ├── sidecar_manifest.crypto_asset.json │ ├── sidecar_manifest.exr │ └── special_chars.exr ├── sidecar_manifest │ ├── bunny_CryptoObject.exr │ └── bunny_CryptoObject_manifest.json └── testGrid_CryptoObject.exr └── specification ├── IDmattes_poster.pdf └── cryptomatte_specification.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | .DS_Store 4 | *.pyc 5 | Thumbs.db 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 1.4.0: 2 | 3 | 1.4.0 is a major update for both the Fusion and Nuke side. 4 | 5 | The Fusion plugin has been overhauled by Cédric Duriau. Importantly this fixes a key memory issue causing glitches (#117). It uses less memory in general and is 10-30% faster both interactively and in render times. The user interface has been improved and fine tuned for Resolve. On the development side, it now features a test suite written in Lua. 6 | 7 | The Nuke plugin is also updated with a major new feature, Wildcard support. This is based largely on work done by Veronica Tello during her time at Method Studios. See [Nuke Documentation](/docs/nuke.md#Wildcards) for more information on wildcards. 8 | 9 | Also in Nuke news, Foundry's Nuke 13 now has a native Cryptomatte integration, which leads to obvious questions about the future of this Nuke integration and the purpose of this release. This release is likely the last major release for this Nuke plugin. While this is sad for me to write, it's also worth celebrating. "The goal of releasing Cryptomatte is to turn it into an ecosystem around an open standard", as we say on the front page of the repo, and there is now a robust ecosystem of Cryptomatte readers and writers. This repository still hosts the Cryptomatte standard as well, which is the central part, and not any one implementation. 10 | 11 | This release of the Nuke plug-in gets features released that have sat in beta for a long time, is tested in the newest versions of Nuke, and provides an off-ramp for Nuke users to make the transition. It also remains available for users who rely on the current functionality or need their existing Nuke scripts to continue working. 12 | 13 | Thank you to all our users for taking this journey with us, to Psyop for supporting this project, and everyone who contributed with everything from code contributions to bug fixes to encouragement. 14 | 15 | Nuke: 16 | 17 | * Official release of wildcards (Thanks @vtello, @maxnbk, and others!) See documentation. 18 | * Fixed unload manifest with special characters in names 19 | 20 | Fusion: 21 | 22 | * Fixed memory issue ([#46](https://github.com/Psyop/Cryptomatte/issues/46), [#117](https://github.com/Psyop/Cryptomatte/issues/117)) 23 | * Fixed hex to float decoding bug for Cycles ([#120](https://github.com/Psyop/Cryptomatte/issues/120)) 24 | * Improved performance ([cedricduriau#11](https://github.com/cedricduriau/Cryptomatte/pull/11)) 25 | * Implemented multi-level logging ([cedricduriau#11](https://github.com/cedricduriau/Cryptomatte/pull/11)) 26 | * Improved error management & logging ([#100](https://github.com/Psyop/Cryptomatte/issues/100), [#101](https://github.com/Psyop/Cryptomatte/issues/101), [#116](https://github.com/Psyop/Cryptomatte/issues/116)) 27 | * Simplified GUI ([#136](https://github.com/Psyop/Cryptomatte/issues/136), [cedricduriau#29](https://github.com/cedricduriau/Cryptomatte/issues/29), [cedricduriau#33](https://github.com/cedricduriau/Cryptomatte/issues/33)) 28 | * Added test suite ([cedricduriau#12](https://github.com/cedricduriau/Cryptomatte/issues/12)) 29 | * Allow animatable controls ([cedricduriau#25](https://github.com/cedricduriau/Cryptomatte/issues/25), [cedricduriau#35](https://github.com/cedricduriau/Cryptomatte/issues/35)) 30 | 31 | ### 1.3.0 32 | 33 | Nuke: 34 | 35 | * Updated to support both Python 3 and Python 2.7 with the same code (#134, thanks @PumpingPixels). 36 | * Python 2.6 is no longer supported. This means Nuke 6 and Nuke 7 are no longer supported. 37 | * Fixed #138- load metadata for this view only 38 | 39 | ### 1.2.8 40 | 41 | Nuke: 42 | 43 | Fixed a bug with copy and pasting with "Lock Layer Selection" on (#128) 44 | 45 | ### 1.2.7: 46 | 47 | Nuke: 48 | 49 | * Fixed issue where decryptomatte doesn't work inside groups (#127, contributed by Johannes Hezer) 50 | 51 | ### 1.2.6 52 | 53 | Nuke: 54 | 55 | * Fixed issue where a missing manifest would prevent keying (#124) 56 | * Fixed issue where nuke-unfriendly Cryptomatte names (crypto.material, 123_crypto) would cause issues and noise in tests 57 | * Fixed layer options not being available on Cryptomatte gizmos after scene open 58 | 59 | ### 1.2.5 60 | 61 | Nuke: 62 | 63 | * Added a version number display for users and scripts to test what version is used (#106) 64 | * Better support for Blender layer names (#114, thanks @aliasguru) 65 | * Scalable Cryptomatte logo (#119, thanks JF Panisset) 66 | * Fixed a callback error when tab-creating gizmos with an input selected (#109) 67 | * Fixed troubleshooting button when callbacks are not installed. (#112) 68 | * Tests pass in Nuke 12.0v3 69 | 70 | ### 1.2.4 71 | 72 | Nuke: 73 | 74 | * Performance improvement when keying by caching metadata. (#90) 75 | * Fixed for stereo renders by using nuke.thisView() (#103) 76 | * Fixed errors on open with more than 16 layers of Cryptomatte (#95) 77 | * Fixed gizmo for Blender channel names containing "." which get converted to underscores in Nuke, also fixed for Encryptomatte. (#104) 78 | 79 | ### 1.2.3: 80 | 81 | Nuke: 82 | 83 | * Fixed bug with hierarchical names (contributed by Jens Lindgren) 84 | 85 | ### 1.2.2: 86 | 87 | Fusion (by Cédric Duriau): 88 | 89 | * Support for EXR images with a data window smaller than display window (builtin DoD) 90 | * Fixed crash for Redshift frames with DoD [#80](https://github.com/Psyop/Cryptomatte/issues/80) 91 | * Fixed crash for Mantra frames with DoD [#64](https://github.com/Psyop/Cryptomatte/issues/64) 92 | * Updated README install documentation [#62](https://github.com/Psyop/Cryptomatte/issues/62) 93 | * Minimum Fusion version is now 9.0.2 94 | * Changed repo structure to match Fusion directory structure 95 | * Added Fuse registry information (help, company, version, ...) 96 | * Added dosctrings 97 | * Cleaned up code 98 | 99 | ### 1.2.1: 100 | 101 | A reorganization of this repo, and a minor update to the Nuke plugins. 102 | 103 | * Reorganized Fusion and Nuke documentation into separate files. 104 | * Added some tiny test images showing different cases of Cryptomatte usage. 105 | 106 | Nuke: 107 | 108 | * Added troubleshooting button to Cryptomatte gizmo 109 | * Fixed Encryptomatte issue where it couldn't start a new Cryptomatte with no inputs 110 | * Added test for Encryptomatte with no inputs 111 | * Do not allow keying IDs with zero coverage 112 | * Fixed some test issues in Nuke 11.3 113 | * Cleaner decryptomatte results, with maximum of 3 nodes in sequence, no dots, and better naming 114 | 115 | 116 | ### 1.2.0: 117 | 118 | This is a major update to both the Nuke plugins and Fusion plugins, and a minor update to the Cryptomatte specification. 119 | 120 | Specification: 121 | 122 | * Changed specification regarding sidecar manifests, such that they are always sidecars with relative paths. 123 | * Support for UTF-8 Characters in Nuke plugin and specification 124 | * Deprecated preview channels 125 | 126 | Nuke: 127 | 128 | * Encryptomatte 129 | * Added Encryptomatte - allows modifying or creating Cryptomattes in Nuke 130 | * Added support for sidecar manifests 131 | * Added layer selection pulldown 132 | * New Eyedropper "picker" knobs which use picker position and not sampled values 133 | * No longer use Color knob's built in picker 134 | * Fixed keying problems sometimes caused by GPU-enabled viewers 135 | * Can pick mattes while viewing downstream node (or looking at the RGB/beauty) 136 | * Enables new "Preview" (AKA: "Keyable Surface") options 137 | * "Preview" option provides 3 modes of visual feedback 138 | * "Colors" is an improved version of the old style random colors 139 | * "Edges" allows viewing input RGBA with borders around keyable regions 140 | * "None" allows viewing of input RGBA without borders, but with a visible highlight on selected areas 141 | * Colors now generated dynamically, removing need for preview channels 142 | * Enhancements for multi-channel inline workflow 143 | * "Matte Output" knob enables output to a custom channel 144 | * "Remove Channels" now defaults to false 145 | * "Matte only" now causes mattes to be written to R, G, B, A in addition to specified output 146 | * "Unpremultiply" option to unpremult output matte by input alpha 147 | * Support for special characters and UTF-8 148 | * Support for non-ascii unicode 149 | * Added support names containing spaces, commas, and angle brackets 150 | * Switched matte lists to be YAML-style (names with special characters are enclosed in quotes) 151 | * Added test suite 152 | * Added failfast with cleanup skipping to Nuke tests, to allow inspecting what went wrong 153 | * Bug fixes 154 | * Mixed selections of names and raw IDs now work correctly for all cases 155 | * Gizmo now works when read nodes have "do not attach prefix" enabled 156 | * Fixed rare issue where connections were lost on script load and copy and paste when used with channelmerge 157 | * Fixed (Beta only) bug Encryptomatte retains its layer selection properly 158 | * Fixed (Beta only) bug with PickerAdd and PickerRemove values stored in files 159 | * Fixed (Beta only) bug with errors on load with invalid Cryptomatte 160 | 161 | Fusion (by Cédric Duriau) 162 | 163 | * Minimum Fusion version is now 9.0.1. 164 | * Redesigned around new Fusion 9.0.1 features 165 | * Fuse now loads EXRs directly via the EXRIO module 166 | * For older versions, please use an older release (see GitHub releases) 167 | * Added support for sidecar manifests 168 | * Added layer selection slider with layer name display 169 | * Added "Preview" (AKA "Keyable Surface") options 170 | * Colors now generated dynamically, removing need for preview channels 171 | * Added shortcut configuration file ("cryptomatte_shortcut.fu") 172 | * Added "Toggle" button acting as a Add/Remove switch (Shift+T) 173 | * Added support special characters in selection lists 174 | * Known limitation: Commas in names are not yet supported 175 | * Performance Improvements 176 | * Optimized multi threaded functions 177 | * Added Support for mixed depth EXR images 178 | * Added Support for proxy mode 179 | * Using EXRIO module 180 | * Removed loader channel slots workaround 181 | * Removed "Update Loader" button 182 | * No longer limited to 8 cryptomatte ranks 183 | * Bug fixes 184 | * Improved "matte only" previewing 185 | * Keyable surface feature disabled when in matte only mode 186 | * Code improvements 187 | * Added version to file headers 188 | * Added "cryptomatte_utilities.lua" module 189 | * Added docstrings 190 | * Removed simplejson.lua module, using builtin dkjson module 191 | * Removed "struct.lua" dependency 192 | 193 | ### 1.1.4: 194 | 195 | * Fixes Fusion crash when rendering with FusionRenderConsole 196 | 197 | ### 1.1.3: 198 | 199 | * Adds beta version of Fusion support, also by Cédric Duriau and Kristof Indeherberge at Grid. 200 | * Major tool connection workflow improvement. No longer requires multiple loaders to work, instead populates single loader channel slots when viewed. 201 | 202 | ### 1.1.2: 203 | 204 | * Adds alpha version of Fusion support, created by Cédric Duriau and Kristof Indeherberge at Grid. 205 | 206 | ### 1.1.1: 207 | 208 | * Store channels on hidden knobs on Cryptomatte gizmo and decryptomatte expression nodes 209 | * Fixes expression node errors in batch mode 210 | * No longer prompts users on "decryptomatte selected" 211 | * Allow API users to use decryptomatte without prompt 212 | * Fixed error when loading gizmo in Nuke 7 213 | 214 | ### 1.1.0: 215 | 216 | * Changes to specification regarding storage of metadata 217 | * Enabled Nuke code to read this metadata 218 | * (1.1.0 Nuke plugin is compatible with older Cryptomattes) 219 | * No longer raises errors if no metadata is available (would happen in batch mode) 220 | * No longer raises errors if picker in is in single value mode rather than RGB 221 | 222 | ### 1.0.2: 223 | 224 | * Updated pymmh3 to output signed ints, in compliance with mmh3 225 | 226 | ### 1.0.1: 227 | 228 | * Added layer selection to gizmo and utilities (backwards compatible) 229 | * Added menu.py 230 | * Added `__version__` to cryptomatte_utilities.py 231 | * Bug fix - invalid manifest broke keying 232 | 233 | ### 1.0.0: 234 | 235 | * Initial release of Nuke plugins, specification, and sample images. 236 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Cryptomatte Logo](/docs/header.png) 2 | 3 | Cryptomatte is a tool created at Psyop by Jonah Friedman and Andy Jones. It creates ID mattes automatically with support for motion blur, transparency, and depth of field, using organizational information already available at render time. This organizational information is usually names, object namespaces, and material names. 4 | 5 | * Demo video: [https://vimeo.com/136954966](https://vimeo.com/136954966) 6 | * Poster: [https://github.com/Psyop/Cryptomatte/raw/master/specification/IDmattes_poster.pdf](https://github.com/Psyop/Cryptomatte/raw/master/specification/IDmattes_poster.pdf) 7 | 8 | The goal of releasing Cryptomatte is to turn it into an ecosystem around an open standard. Any developers who wish to make plugins are welcome and encouraged to create tools that inter-operate with the components we are providing. We hope to see a diverse ecosystem of renderers that can create Cryptomatte images and plugins for compositing applications to decode them. 9 | 10 | Cryptomatte is licenced using the BSD 3-clause license. See [license.txt](license.txt). 11 | 12 | Version 1.4.0 See [changelog](CHANGELOG.md) for version history. 13 | 14 | ## Repo Contents 15 | 16 | The contents of this repository are: 17 | 18 | **Nuke:** This contains Python files, an image, and a gizmo. Together these are our implementation for Foundry's Nuke. In Nuke 13, there is also a [native implementation](https://learn.foundry.com/nuke/content/release_notes/nuke_13.0.html) which is similar to this one. This implementation is being kept active to allow for a smooth transition. 19 | 20 | **Fusion:** Fusion integration, including a Fuse file, a Lua module and a Fusion shortcut configuration file. 21 | 22 | **Sample Images:** These example Cryptomatte images can be used for testing your Nuke installation, or for testing other implimentations. 23 | 24 | **Specification:** This is a technical document describing the Cryptomatte standard. It specifies how Cryptomattes are structured, encoded, and decoded. It also contains our SIGGRAPH 2015 poster on the subject. 25 | 26 | ## Documentation 27 | 28 | * [Nuke Documentation](/docs/nuke.md) - Installation, usage instructions, troubleshooting 29 | * [Fusion Documentation](/docs/fusion.md) - Installation, usage instructions 30 | 31 | ## Implementations 32 | 33 | A list of released implementations and links: 34 | 35 | Encoders: 36 | 37 | * [Isotropix Clarisse 3.5 (By Isotropix)](http://www.isotropix.com/products/clarisse-3.5), [Demo](https://www.youtube.com/watch?v=V_ov8B24jq0) 38 | * [Chaos Group V-Ray 3.6 (By Chaos Group)](https://docs.chaosgroup.com/display/VRAY3MAX/Cryptomatte+%7C+VRayCryptomatte), [3DSMax demo](https://www.youtube.com/watch?v=tlahITki4xg), [Maya demo](https://www.youtube.com/watch?v=iVHcuke_aWk), [Nuke demo](https://www.youtube.com/watch?v=Vb4OX7UNIMw) 39 | * [3Delight for Katana and Maya 9.0](https://3delight.atlassian.net/wiki/spaces/3DFK/pages/220135565/Exporting+CryptoMatte+IDs) 40 | * [Houdini 16.5 Mantra (By Sidefx)](http://www.sidefx.com/docs/houdini/render/cryptomatte.html), [Demo](https://vimeo.com/241036613#t=2862s) 41 | * Blender 2.8.0 Cycles (By Tangent Animation and Blender Foundation): [Cryptomatte in Blender 2.8 Alpha 2! demo](https://www.youtube.com/watch?v=lTJJqAGnWFM), [Tutorial by Zacharias Reinhardt](https://zachariasreinhardt.com/blender-2-8-cryptomatte-tutorial), [Cycles for Animated Feature Film Production by Stefan Werner](https://www.youtube.com/watch?v=_2Ia4h8q3xs), [Docs](https://wiki.blender.org/wiki/Reference/Release_Notes/2.80/Cycles#Cryptomatte) 42 | * [Blender 2.92 Eevee (By Jeroen Bakker and Blender Foundation)](https://www.blender.org/download/releases/2-92/) 43 | * [Pixar RenderMan 21.7](https://rmanwiki.pixar.com/display/REN/RenderMan+21.7), [Docs](https://rmanwiki.pixar.com/display/REN/PxrCryptomatte) 44 | * LightWave 3D 2018 ([DB&W EXRTrader plugin](https://www.db-w.com/products/exrtrader)) 45 | * [Redshift 2.6.11](https://www.redshift3d.com) 46 | * [Arnold 4 (AlShaders), by Jonah Friedman, Andy Jones, Anders Langlands.](http://www.anderslanglands.com/alshaders/index.html) 47 | * [Arnold 5 (CryptomatteArnold) by Jonah Friedman, Andy Jones, Anders Langlands.](https://github.com/anderslanglands/alShaders2) 48 | * Nuke 8+ "Encryptomatte", by Andy Jones. In this repo. 49 | * [Appleseed 2.1.0 by Sergo Pogosyan, Jon Dent](https://appleseedhq.net/2019/09/21/appleseed-2-1-0-beta-Released.html) 50 | * [Autodesk VRed](https://knowledge.autodesk.com/support/vred-products/learn-explore/caas/CloudHelp/cloudhelp/2020/ENU/VRED/files/Rendering/VRED-Rendering-How-to-Use-the-Cryptomatte-Options-html-html.html) 51 | * [Otoy OctaneRender for Cinema4D](http://www.aoktar.com/octane/OCTANE%20HELP%20MANUAL.html?Cryptomatte.html) 52 | * [Unreal Engine 4.26](https://docs.unrealengine.com/en-US/AnimatingObjects/Sequencer/Workflow/RenderAndExport/HighQualityMediaExport/RenderPasses/index.html) 53 | * [Unity Cryptomatte - Research Project](https://www.schmuckerdaniel.com/unitycryptomatte) By Daniel Schmuker at Filmakademie Baden-Wuerttemberg 54 | * [Foundry Modo 14.2 mPath Renderer](https://learn.foundry.com/modo/content/help/pages/rendering/render_outputs_cryptomatte.html) 55 | 56 | Decoders: 57 | 58 | * Nuke 8+, by Jonah Friedman, Andy Jones. In this repo. 59 | * [Foundry Nuke 13](https://learn.foundry.com/nuke/content/release_notes/nuke_13.0.html) 60 | * Fusion: by Cédric Duriau and Kristof Indeherberge at Grid. In this repo. 61 | * [Houdini 16.5 Compositor (By Sidefx)](http://www.sidefx.com/docs/houdini/render/cryptomatte.html), [Demo](https://vimeo.com/241036613#t=2862s) 62 | * Blender 2.8.0 Compositor (By Tangent Animation and Blender Foundation): [Cryptomatte in Blender 2.8 Alpha 2! demo](https://www.youtube.com/watch?v=lTJJqAGnWFM), [Tutorial by Zacharias Reinhardt](https://zachariasreinhardt.com/blender-2-8-cryptomatte-tutorial), [Cycles for Animated Feature Film Production by Stefan Werner](https://www.youtube.com/watch?v=_2Ia4h8q3xs), [Docs](https://wiki.blender.org/wiki/Reference/Release_Notes/2.80/Cycles#Cryptomatte) 63 | * [Autodesk Flame (Autodesk)](https://knowledge.autodesk.com/support/flame-products/learn-explore/caas/CloudHelp/cloudhelp/2020/ENU/Flame-EffectsandToolsReference/files/GUID-0402116E-B47C-4E32-9010-DB8C334853E0-htm.html) 64 | * [Adobe After Effects (Fnordware ProEXR plugin 2.0)](https://www.fnordware.com/ProEXR/)[Ships with After Effects 2020](https://theblog.adobe.com/adobe-after-effects-is-faster-than-ever/) 65 | * [Adobe Photoshop (EXR-IO 2)](https://www.exr-io.com/exr-io-2-00/) 66 | * [FilmLight Baselight v5](https://www.filmlight.ltd.uk/pdf/datasheets/FL-BL-DS-0847-Baselightv5.pdf) 67 | * [Natron](https://github.com/NatronGitHub/natron-plugins) by Fahad Hasan Pathik and Fabrice Fernandez 68 | 69 | ## Acknowledgements 70 | 71 | * Anders Langlands 72 | * Alon Gibli 73 | * Jean-Francois Panisset 74 | * Psyop 75 | * Solid Angle 76 | * All the members of the Cryptomatte Committee 77 | * Benoit Leveau 78 | * Cédric Duriau 79 | * Kristof Indeherberge 80 | * Vladimir Koylazov 81 | * Peter Loveday 82 | * Andrew Hazelden 83 | * Jens Lindgren 84 | * Rainer Trummer 85 | * Veronica Tello 86 | * Stephen Mackenzie 87 | -------------------------------------------------------------------------------- /docs/cryptomatte_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 23 | 25 | image/svg+xml 26 | 28 | 29 | 30 | 31 | 32 | 34 | 37 | 44 | 45 | 46 | 66 | 71 | 78 | 82 | 89 | 97 | 103 | 105 | 113 | 121 | 129 | 137 | 145 | 152 | 160 | 168 | 175 | 183 | 184 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /docs/encryptomatteProperties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/docs/encryptomatteProperties.png -------------------------------------------------------------------------------- /docs/fusion.md: -------------------------------------------------------------------------------- 1 | ## Fusion Installation 2 | 3 | 1. Download the entire Cryptomatte GitHub repository using the green `Code` button. Select `Download ZIP` and then extract the contents. 4 | 5 | 2. Open Fusion and open the path mapping preferences. 6 | 7 | - `File` > `Preferences` > `Global and Default Settings` > `Path Map` 8 | 9 | 3. Add to the `User` section by clicking `New` to create a new path mapping entry. Fill in the settings as follows. Replace `{CRYPTOMATTE_DIRECTORY}` with the path to the downloaded and extracted Cryptomatte repository. (See screenshot `User` below.) 10 | 11 | - From: `Cryptomatte:` 12 | - To: `{CRYPTOMATTE_DIRECTORY}/fusion` 13 | 14 | 4. Add the newly created path map by selecting the `Defaults` section `UserPaths:` entry as following. (See screenshot `Defaults` below.) 15 | 16 | - From: `UserPaths:` 17 | - To: `UserData:;AllData:;Fusion:;Cryptomatte:;` 18 | 19 | 20 | User | Defaults 21 | :----: | :--------: 22 | ![Fusion Settings User](fusionInstallUser.png) | ![Fusion Settings Defaults](fusionInstallDefaults.png) 23 | 24 | NOTE: If you copied files from previous releases into their respective Fusion supported directories, remove them to avoid conflicts with path mapping. 25 | 26 | ## Fusion Usage 27 | 28 | ![Cryptomatte for Fusion](fusionUsageComp.png) 29 | 30 | The Cryptomatte Fuse works in Fusion (Free and Studio) v9.0.2+. The Fuse allows you to create matte selections using a Cryptomatte "Matte Locator" control that is positioned using the transform control in the Fusion Viewer window. 31 | 32 | To get started: 33 | 34 | 1. Add a Cryptomatte exr file to your composite, such as the sample images, using a Loader node. 35 | 2. Select the Loader node and use the Select Tool window (Shift + Spacebar) to add a new Cryptomatte node to your composite. 36 | 3. Select the Cryptomatte node in the Flow area and display the output in a Viewer window. 37 | 4. Position the Cryptomatte "Matte Locator" control in the Viewer window over an object in the frame. 38 | 5. Press the "Add" button in the Cryptomatte Tools view to add a new matte entry to the Matte List. Alternatively, you could press the "Shift + T" hotkey in the Fusion Viewer window to toggle the active Cryptomatte "Matte Locator" state between the "Add" and "Remove" selection modes. 39 | 40 | ### Cryptomatte Fuse 41 | 42 | Controls Tab | Advanced Tab 43 | :-----------------: | :-----------------: 44 | ![Fusion Cryptomatte Fuse Controls Tab](fusionUsageFuseControls.png) | ![Fusion Cryptomatte Fuse Advanced Tab](fusionUsageFuseAdvanced.png) 45 | 46 | Controls: 47 | - Matte Locator: Defines the position of the matte to interact with. 48 | - Add: Adds the name of the matte at the current position of the locator to the matte list. 49 | - Removes: Removes the name of the matte at the current position of the locator from matte list. 50 | - Toggle: Adds/Removes the name of the matte at the current position of the locator to/from matte list. 51 | - View Mode: Image to pick mattes on. 52 | - Colors: Unique colors per matte. 53 | - Edges: Input image with a colored border around mattes. 54 | - Beauty: Input image. 55 | - Matte: Monochannel matte image. 56 | - Matte List: A list of names to extract mattes from. This list may be modified in text form or using the controls. 57 | - Clear: Clears the matte list. 58 | - Layer Index: Index of the Cryptomatte EXR layer to use. Automatically set, can be changed manually. 59 | - Layer Name: Name of the Cryptomatte EXR layer for current index. 60 | 61 | Advanced: 62 | - Name Checker Locator: Defines the position of the matte to interact with. 63 | - Show: Shows the name checker locator. 64 | - Hide: Hides the name checker locator. 65 | - Matte Name: Name of the matte at the current name checker locator position. 66 | 67 | ## Logging 68 | 69 | Cryptomatte has a multi-level logging system which notifies the user in case of errors, warnings and process information. There are three log levels supported: `ERROR`, `WARNING` and `INFO`. 70 | 71 | To control the log level, determining what is printed in the console, you can set the `CRYPTOMATTE_LOG_LEVEL` environment variable as following. When no log level is set, `WARNING` is used as default. 72 | 73 | Log Level | Value 74 | --------- | ----- 75 | ERROR | 0 76 | WARNING | 1 77 | INFO | 2 78 | 79 | ```lua 80 | -- example error 81 | [Cryptomatte][Cryptomatte1][ERROR] unknown view mode: 'nil' 82 | ...Cryptomatte/fusion/Modules/Lua/cryptomatte_utilities.lua:614: ERROR 83 | stack traceback: 84 | [C]: in function 'error' 85 | ...Cryptomatte/fusion/Modules/Lua/cryptomatte_utilities.lua:614: in function 'log_error' 86 | ...iau/repo/Cryptomatte/fusion/Fuses/Matte/cryptomatte.fuse:403: in function <...iau/repo/Cryptomatte/fusion/Fuses/Matte/cryptomatte.fuse:249> 87 | Cryptomatte1 failed at time 0 88 | ``` 89 | ```lua 90 | -- example warning 91 | [Cryptomatte][Cryptomatte1][WARNING] matte not present in manifest: foo 92 | ``` 93 | ```lua 94 | -- example info 95 | [Cryptomatte][Cryptomatte1][INFO] -- process started 96 | [Cryptomatte][Cryptomatte1][INFO] reading metadata ... 97 | [Cryptomatte][Cryptomatte1][INFO] setting layer name: 'uCryptoAsset' 98 | [Cryptomatte][Cryptomatte1][INFO] decoding manifest ... 99 | [Cryptomatte][Cryptomatte1][INFO] creating layer images ... 100 | [Cryptomatte][Cryptomatte1][INFO] reading matte names ... 101 | [Cryptomatte][Cryptomatte1][INFO] creating matte image ... 102 | [Cryptomatte][Cryptomatte1][INFO] creating 'Colors' preview image ... 103 | [Cryptomatte][Cryptomatte1][INFO] elapsed time: 2.202 104 | [Cryptomatte][Cryptomatte1][INFO] -- process ended 105 | ``` 106 | 107 | ## Testing 108 | Cryptomatte for Fusion ships with two lua modules. 109 | 110 | 1. `cryptomatte_utilities.lua` 111 | 2. `test_cryptomatte_utilities.lua` 112 | 113 | The first being the utility module for the Fuse, the second is its test suite. This test suite was written to test and ensure functionalities are working and behave in an expected manner. 114 | 115 | The test suite is only used for development and **completely optional** for a functional Cryptomatte Fuse. If you wish to run the test suite yourself, you can do so by running the following snippet in the Fusion Lua script console. 116 | 117 | ```lua 118 | -- replace {PATH_TO_TEST_FILE} with the absolute path to the test file 119 | dofile("{PATH_TO_TEST_FILE}") 120 | ``` 121 | ```lua 122 | -- example run 123 | dofile("/home/cduriau/repo/Cryptomatte/fusion/Modules/Lua/test_cryptomatte_utilities.lua") 124 | collectings test(s) ... 125 | detected 18 test(s) ... 126 | running tests ... 127 | [ 6%] cryptomatte_test__format_log ... [OK] 128 | [ 11%] cryptomatte_test__get_absolute_path ... [OK] 129 | [ 17%] cryptomatte_test__get_absolute_position ... [OK] 130 | [ 22%] cryptomatte_test__get_channel_hierarchy ... [OK] 131 | [ 28%] cryptomatte_test__get_log_level ... [OK] 132 | [ 33%] cryptomatte_test__hex_to_float ... [OK] 133 | [ 39%] cryptomatte_test__is_position_in_rect ... [OK] 134 | [ 44%] cryptomatte_test__solve_channel_name ... [OK] 135 | [ 50%] cryptomatte_test__string_ends_with ... [OK] 136 | [ 56%] cryptomatte_test__string_split ... [OK] 137 | [ 61%] cryptomatte_test__string_starts_with ... [OK] 138 | [ 67%] cryptomatte_test_decode_manifest ... [OK] 139 | [ 72%] cryptomatte_test_get_cryptomatte_metadata ... [OK] 140 | [ 78%] cryptomatte_test_get_matte_names ... [OK] 141 | [ 83%] cryptomatte_test_log_error ... [OK] 142 | [ 89%] cryptomatte_test_log_info ... [OK] 143 | [ 94%] cryptomatte_test_log_warning ... [OK] 144 | [100%] cryptomatte_test_read_manifest_file ... [OK] 145 | ``` 146 | -------------------------------------------------------------------------------- /docs/fusionInstallDefaults.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/docs/fusionInstallDefaults.png -------------------------------------------------------------------------------- /docs/fusionInstallUser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/docs/fusionInstallUser.png -------------------------------------------------------------------------------- /docs/fusionUsageComp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/docs/fusionUsageComp.png -------------------------------------------------------------------------------- /docs/fusionUsageFuseAdvanced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/docs/fusionUsageFuseAdvanced.png -------------------------------------------------------------------------------- /docs/fusionUsageFuseControls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/docs/fusionUsageFuseControls.png -------------------------------------------------------------------------------- /docs/github-zip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/docs/github-zip.png -------------------------------------------------------------------------------- /docs/gizmoProperties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/docs/gizmoProperties.png -------------------------------------------------------------------------------- /docs/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/docs/header.png -------------------------------------------------------------------------------- /docs/nuke.md: -------------------------------------------------------------------------------- 1 | ## Nuke Installation 2 | 3 | 1. Download the entire Cryptomatte GitHub repository using the green "Clone or download" button. Select "Download Zip" and then extract the contents. 4 | 2. Copy the contents of the "nuke" folder from Cryptomatte into a folder in your Nuke plugin path, such as your home directory's ".nuke" folder. 5 | 3. If the destination folder already contains an "init.py" and/or "menu.py" file, open those files in a text editor, and append the contents of the Cryptomatte "init.py" and "menu.py" to those files. 6 | 4. After launching Nuke, if you've installed the plugin correctly you should be able to tab-create a Cryptomatte gizmo. 7 | 8 | For more information on installing Nuke plugins, see: 9 | 10 | [https://www.thefoundry.co.uk/products/nuke/developers/105/pythondevguide/installing_plugins.html](https://www.thefoundry.co.uk/products/nuke/developers/105/pythondevguide/installing_plugins.html) 11 | 12 | To test the functionality, you can try loading one of the sample images supplied. Load the sample images into Nuke, select one of them, and tab-create the gizmo. Viewing the output of the gizmo should show you a preview of the available mattes. Use the color knob, "Picker Add" to eye-dropper colors on the image to create your mattes. 13 | 14 | ## Nuke Usage 15 | 16 | ![Cryptomatte in Nuke](nukeScreenshot.jpg) 17 | 18 | To get started: 19 | 20 | 1. Load a Cryptomatte exr file, such as the sample images, using a Read node. 21 | 2. Select it, and tab create a Cryptomatte gizmo. 22 | 3. View the output of the gizmo. You should see a preview image (pictured). 23 | 4. Use the eyedropper with the 'Picker Add' knob to select objects. They should light up in RGB, and output the matte in Alpha. With the eyedropper, make sure you use control-click and not alt-control click. 24 | 25 | ### Cryptomatte Gizmo 26 | 27 | ![Cryptomatte Gizmo Properties in Nuke](gizmoProperties.png) 28 | 29 | Psyop Cryptomatte Tab: 30 | 31 | * Picker Add: This adds "keyed" objects to the matte selection, meant to be used with Nuke's eyedropper. 32 | * Picker Remove: This removes "keyed" objects from the matte selection, meant to be used with Nuke's eyedropper. 33 | * Preview: Controls whether or not previews of the matte boundaries are drawn. A pulldown controls how they are drawn. 34 | * "Edges" allows viewing input RGBA with borders around keyable regions 35 | * "Colors" is random colors per matte 36 | * "None" allows viewing of input RGBA without borders, but with a visible highlight on selected areas 37 | * Matte Only: Also write the matte to RGBA channels 38 | * Single Selection: Changes the gizmo behavior so that only one object may be selected at a time. 39 | * Expand Wildcards: Expands wildcards in names typed in the matte list. 40 | * Remove Channels: Removes the Cryptomatte channels so that downstream of the gizmo, the additional channels are not present. 41 | * Matte Output: Which channel the extracted matte is written to. 42 | * Unpremultiply: Unpremults the extracted matte against by the alpha. 43 | * Matte List: A list of names to extract mattes from. This list may be modified in text form or using the Picker color knobs. 44 | * Clear: Clears the matte list. 45 | * Force Update: The python scripts keep Cryptomatte gizmos updated when inputs or relevant knobs are changed. If there's a case that it does not update, this button will manually update it. 46 | * Stop Auto Update: Stops the automatic updating described above. 47 | * Layer Selection: If there are multiple Cryptomattes, this is how you select the layer. This is filled in automatically, but may be changed manually. 48 | * Lock Layer Selection: Stops the automatic updating of layer selection, which occurs if the specified selection is not available. 49 | * Expression: Internally the gizmo generates an expression to extract the matte. The expression is saved here. 50 | 51 | Advanced Tab: 52 | 53 | * Decryptomatte: Replaces gizmo with equivalent nodes 54 | * Unload Manifest: Generates a keyer for every name in the manifest. 55 | * Force Update All Gizmos in Script: Same as "Force Update", but runs the force update functionality on all Gizmos in the script. 56 | 57 | #### [Wildcards](#Wildcards) 58 | 59 | When `Use Wildcards` is enabled, typing a wildcard expression such as `flower*` into the Matte list will expand it to all names that it matches, such as `flower1`, `flower2`, etc. 60 | 61 | Simple cases: 62 | 63 | - To use a wildcard expression, enable `Use Wildcards` and add an expression to the matte list. This uses *fnmatch* style matching, which has the following features. 64 | - Wildcards (`*`) 65 | - `flower*` matches `flower1`, `flower3_petal`, `flower_stem`, but not `red_flower`. 66 | - `flower*petal` matches `flower3_petal` and `flower_rose_petal`, but not `flower_stem`. 67 | - Numeric wildcards (`?`) 68 | - `flower?` matches `flower25` but not `flower25_petal2`. 69 | - `flower?_petal?` matches `flower25_petal2` 70 | 71 | Finer points (advanced): 72 | 73 | - If `Use Wildcards` is not enabled, the name `*sterisk` is used as the literal string. This is however somewhat like having an unexploded bomb in your matte list - as soon as you enable wildcards it'll expand. Keying an object with an `*` in the name will add escape characters to the front, to ensure this doesn't happen. 74 | - For example, an object is literally named `*sterisk`, where some smartass put an asterisk into the name itself. 75 | - `\\*sterisk` means the literal name `*sterisk`. The escape characters (`\\`) preceding the `*` mean it is not an fnmatch expression, and will not change when enabling wild cards. 76 | - Keying an object named `*sterisk` will add `\\*sterisk` to your matte list, so that when wildcards are enabled it will not change. 77 | - It's possible to mixing wildcards and literal characters, like so: 78 | - `\\*sterisk*` will match `*sterisk1`, `*sterisk2`, `*sterisk3` and so on at the same time. 79 | - What if my name contains square brackets? (`Brack[et]`) 80 | - This is an even specialer case, as square brackets have meaning in both fnmatch and muke. Nuke will match the square brackets themselves. For a literal square bracket, these require three escape characters. In the matte list this will appear as `Brack\\\[et\\\]`. 81 | - Brackets are also used by fnmatch. In fnmatch, `[*]sterisk` is a way of writing a literal asterisk and will match only `*sterisk`. 82 | - Using this mechanism is too complicated due to multiple levels of escaping and is not recommended. Instead, use `\\*sterisk` in the matte list to signify a literal asterisk. 83 | - `\\*sterisk*` will match `*sterisk1`, `*sterisk2`, but not `asterisk1`. 84 | 85 | 86 | ### Encryptomatte Gizmo 87 | 88 | ![Encryptomatte Gizmo Properties in Nuke](encryptomatteProperties.png) 89 | 90 | Encryptomatte is a gizmo that can modify existing Cryptomattes, or start new ones. One Encryptomatte node adds one matte to a Cryptomatte. 91 | 92 | To get started: 93 | 94 | 1. Load a Cryptomatte with a read node. 95 | 2. Select it, and tab-create an Encryptomatte. 96 | 3. Feed in a matte that you would like to add to that Cryptomatte. You can put it over or under all other mattes. 97 | 4. Write it out as a 32 bit EXR with all metadata, or attach a Cryptomatte node to it to test the mattes. 98 | 99 | Encryptomatte tab: 100 | 101 | * Matte Name: The name your new matte will have in the Cryptomatte 102 | * Merge Operation: Where in the stack of mattes your matte will be added, over or under the rest 103 | * Layer selection: Same as Cryptomatte, see above. 104 | * Force Update: Same as Cryptomatte, see above. 105 | * Layers: If starting a fresh Cryptomatte, sets how many Cryptomatte layers are to be created. If starting from scratch, fill in Layer Selection manually. 106 | * Setup Layers: If on, starts a fresh Cryptomatte. Otherwise, modifies one from the input. 107 | 108 | ### Menu options 109 | 110 | * Cryptomatte: Creates a Cryptomatte gizmo 111 | * Decryptomatte All: Replaces all Cryptomatte gizmos with other nodes which are functionally equivalent. This is useful for sending nuke scripts to other users who do not have the Cryptomatte plugins installed. 112 | * Decryptomatte Selected: Same as "decryptomatte all", except only applies to selected nodes. 113 | * Encryptomatte: Creates an Encryptomatte gizmo 114 | 115 | ### Troubleshooting 116 | 117 | A couple of really simple things to watch out for: 118 | 119 | * Make sure you are viewing the same Cryptomatte gizmo you're keying with. (this is a very easy mistake to make). 120 | * To use the eyedropper, use the control key. Do not use alt-control, which eyedroppers values from upstream of the node. Many users are in the habit of using alt-control eye droppering. 121 | 122 | ### Troubleshooting button 123 | 124 | The advanced tab of the gizmo has a "Troubleshoot" button that will test for common installation and setup problems. 125 | 126 | ### Common issues 127 | 128 | #### Keyed mattes have incorrect pixelated edges. 129 | 130 | Make sure there are no "Reformat" or "LensDistortion" or similar nodes applied to your Cryptomattes before trying to key them. Cryptomatte relies on exact values in channels, and operations that mix values with neighboring values will damage this information, resulting in only being able to extract mattes on pixels containing only one object. These operations should be applied to the extracted mattes instead. 131 | 132 | Likewise, using proxy mode gives bad results for similar reasons. 133 | 134 | #### Things key properly, but not as their names, instead they key as numbers like `<0.1234>` 135 | 136 | The object keyed is not in the manifest provided with this EXR file, or no manifest is provided. This is a problem on the 3D side. However, objects keyed this way will be stable and will work. 137 | 138 | #### I can't tab-create the Cryptomatte gizmo. 139 | 140 | The scripts are not installed correctly. See installation instructions. 141 | 142 | #### I don't get a keyable surface when I connect my gizmo to a read node. 143 | 144 | Try running, "Force Update". If an error dialogue box pops up, the scripts are not installed correctly. See installation instructions. 145 | 146 | Also, test if it works on one of the sample images. 147 | 148 | #### Objects under the Arnold watermark aren't keyable. 149 | 150 | They sure aren't! 151 | 152 | #### I can't key the background (black pixels). 153 | 154 | You can key the background by manually entering the value, `<0.0>` into the matte list of the gizmo. 155 | 156 | ### Testing (developers) 157 | 158 | Nuke Cryptomatte has a suite of unit and integration tests. These cover hashing, CSV resolution, operations of the Cryptomatte and Encryptomatte gizmos, and Decryptomatte. Use of these is strongly encouraged if working with the Cryptomatte code. 159 | 160 | ``` 161 | # To run tests in an ad-hoc style in a Nuke session, in the script editor: 162 | import cryptomatte_utilities as cu 163 | cu.tests.run_nuke_tests() 164 | ``` 165 | 166 | Tests require the provided `sample_images` directory. If it is not located in the default location relative to the Python files, its location may be specified using an env variable, `$CRYPTOMATTE_TESTING_SAMPLES`. This can also be done ad-hoc in Nuke prior to running tests: 167 | 168 | ``` 169 | import os 170 | os.environ["CRYPTOMATTE_TESTING_SAMPLES"] = "" # < specify sample_images dir here 171 | ``` 172 | -------------------------------------------------------------------------------- /docs/nukeScreenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/docs/nukeScreenshot.jpg -------------------------------------------------------------------------------- /fusion/Config/cryptomatte_shortcuts.fu: -------------------------------------------------------------------------------- 1 | --[[ 2 | Version : 1.4.0 3 | Requires : Fusion 9.0.2 - 17.1.1+ 4 | Requires : Resolve 15.1 - 17.1.1+ 5 | Optional : cjson 6 | Created by : Cédric Duriau [duriau.cedric@live.be] 7 | Kristof Indeherberge [xmnr0x23@gmail.com] 8 | Andrew Hazelden [andrew@andrewhazelden.com] 9 | --]] 10 | 11 | { 12 | Action { 13 | ID = "CryptomatteAdd", 14 | Category = "Cryptomatte", 15 | Name = "Cryptomatte Add", 16 | Targets = { 17 | Composition = { 18 | Execute = _Lua [=[ 19 | fuse = obj:Comp().ActiveTool 20 | if fuse then 21 | if fuse:GetID() == 'Fuse.Cryptomatte' then 22 | fuse.Add = 1 23 | end 24 | end 25 | ]=] 26 | } 27 | } 28 | }, 29 | Action { 30 | ID = "CryptomatteRemove", 31 | Category = "Cryptomatte", 32 | Name = "Cryptomatte Remove", 33 | Targets = { 34 | Composition = { 35 | Execute = _Lua [=[ 36 | fuse = obj:Comp().ActiveTool 37 | if fuse then 38 | if fuse:GetID() == 'Fuse.Cryptomatte' then 39 | fuse.Remove = 1 40 | end 41 | end 42 | ]=] 43 | } 44 | } 45 | }, 46 | Action { 47 | ID = "CryptomatteToggle", 48 | Category = "Cryptomatte", 49 | Name = "Cryptomatte Toggle", 50 | Targets = { 51 | Composition = { 52 | Execute = _Lua [=[ 53 | fuse = obj:Comp().ActiveTool 54 | if fuse then 55 | if fuse:GetID() == 'Fuse.Cryptomatte' then 56 | fuse.Toggle = 1 57 | end 58 | end 59 | ]=] 60 | } 61 | } 62 | }, 63 | Action { 64 | ID = "CryptomatteViewMode", 65 | Category = "Cryptomatte", 66 | Name = "Cryptomatte View Mode", 67 | Targets = { 68 | Composition = { 69 | Execute = _Lua [=[ 70 | fuse = obj:Comp().ActiveTool 71 | if fuse then 72 | if fuse:GetID() == 'Fuse.Cryptomatte' then 73 | view_mode = fuse.ViewMode[fu.TIME_UNDEFINED] 74 | if view_mode ~= 3 then 75 | view_mode = view_mode + 1 76 | else 77 | view_mode = 0 78 | end 79 | fuse.ViewMode = view_mode 80 | end 81 | end 82 | ]=] 83 | } 84 | } 85 | }, 86 | Action { 87 | ID = "CryptomatteClear", 88 | Category = "Cryptomatte", 89 | Name = "Cryptomatte Clear", 90 | Targets = { 91 | Composition = { 92 | Execute = _Lua [=[ 93 | fuse = obj:Comp().ActiveTool 94 | if fuse then 95 | if fuse:GetID() == 'Fuse.Cryptomatte' then 96 | fuse.Clear = 1 97 | end 98 | end 99 | ]=] 100 | } 101 | } 102 | }, 103 | Action { 104 | ID = "CryptomatteLayerMinus", 105 | Category = "Cryptomatte", 106 | Name = "Cryptomatte Layer Minus", 107 | Targets = { 108 | Composition = { 109 | Execute = _Lua [=[ 110 | fuse = obj:Comp().ActiveTool 111 | if fuse then 112 | if fuse:GetID() == 'Fuse.Cryptomatte' then 113 | layer_index = fuse.LayerIndex[fu.TIME_UNDEFINED] 114 | if layer_index ~= 1 then 115 | fuse.LayerIndex = layer_index - 1 116 | end 117 | end 118 | end 119 | ]=] 120 | } 121 | } 122 | }, 123 | Action { 124 | ID = "CryptomatteLayerPlus", 125 | Category = "Cryptomatte", 126 | Name = "Cryptomatte Layer Plus", 127 | Targets = { 128 | Composition = { 129 | Execute = _Lua [=[ 130 | fuse = obj:Comp().ActiveTool 131 | if fuse then 132 | if fuse:GetID() == 'Fuse.Cryptomatte' then 133 | layer_index = fuse.LayerIndex[fu.TIME_UNDEFINED] 134 | if layer_index ~= fuse.LayerIndex:GetAttrs("INPN_MaxScale") then 135 | fuse.LayerIndex = layer_index + 1 136 | end 137 | end 138 | end 139 | ]=] 140 | } 141 | } 142 | }, 143 | Hotkeys { 144 | Target = "GLView", 145 | SHIFT_A = "CryptomatteAdd{}", 146 | SHIFT_C = "CryptomatteClear{}", 147 | SHIFT_KEYPAD_MINUS = "CryptomatteLayerMinus{}", 148 | SHIFT_MINUS = "CryptomatteLayerMinus{}", 149 | SHIFT_KEYPAD_PLUS = "CryptomatteLayerPlus{}", 150 | SHIFT_PLUS = "CryptomatteLayerPlus{}", 151 | SHIFT_R = "CryptomatteRemove{}", 152 | SHIFT_T = "CryptomatteToggle{}", 153 | SHIFT_V = "CryptomatteViewMode{}", 154 | SHIFT_CONTROL_K = "CryptomatteViewMode{}", 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /fusion/Fuses/Matte/cryptomatte.fuse: -------------------------------------------------------------------------------- 1 | --[[ 2 | Version : 1.4.0 3 | Requires : Fusion 9.0.2 - 17.1.1+ 4 | Requires : Resolve 15.1 - 17.1.1+ 5 | Optional : cjson 6 | Created by : Cédric Duriau [duriau.cedric@live.be] 7 | Kristof Indeherberge [xmnr0x23@gmail.com] 8 | Andrew Hazelden [andrew@andrewhazelden.com] 9 | --]] 10 | 11 | -- ============================================================================ 12 | -- modules 13 | -- ============================================================================ 14 | local cryptoutils = self and require("cryptomatte_utilities") or nil 15 | 16 | -- ============================================================================ 17 | -- constants 18 | -- ============================================================================ 19 | FUSE_NAME = "Cryptomatte" 20 | FUSE_CATEGORY = "Matte" 21 | FUSE_COMPANY = "Psyop" 22 | FUSE_HELP = "https://www.steakunderwater.com/wesuckless/viewtopic.php?f=6&t=1027" 23 | FUSE_URL = "https://github.com/Psyop/Cryptomatte" 24 | FUSE_VERSION = 140 25 | 26 | SEPARATOR_INDEX = 0 27 | VIEW_MODE_EDGES = "Edges" 28 | VIEW_MODE_COLORS = "Colors" 29 | VIEW_MODE_BEAUTY = "Beauty" 30 | VIEW_MODE_MATTE = "Matte" 31 | VIEW_MODES = {VIEW_MODE_COLORS, VIEW_MODE_EDGES, VIEW_MODE_BEAUTY, VIEW_MODE_MATTE} 32 | SHOW_CALLBACKS = false 33 | 34 | 35 | -- ============================================================================ 36 | -- utils 37 | -- ============================================================================ 38 | function create_separator() 39 | --[[ 40 | Creates a separator input control. 41 | 42 | :rtype: Input 43 | ]] 44 | SEPARATOR_INDEX = SEPARATOR_INDEX + 1 45 | local name = string.format("Separator%s", SEPARATOR_INDEX) 46 | return self:AddInput(name, name, { 47 | INPID_InputControl = "SeparatorControl", 48 | IC_Visible = true, 49 | INP_External = false, 50 | INP_Passive = true 51 | }) 52 | end 53 | 54 | 55 | -- ============================================================================ 56 | -- fuse 57 | -- ============================================================================ 58 | FuRegisterClass(FUSE_NAME, CT_Tool, { 59 | REGS_Name = FUSE_NAME, 60 | REGS_Category = FUSE_CATEGORY, 61 | REGS_Company = FUSE_COMPANY, 62 | REGS_OpIconString = FUSE_NAME, 63 | REGS_OpDescription = FUSE_NAME, 64 | REGS_URL = FUSE_URL, 65 | REGS_HelpTopic = FUSE_HELP, 66 | REG_NoMotionBlurCtrls = true, 67 | REG_NoBlendCtrls = true, 68 | REG_OpNoMask = true, 69 | REG_Version = FUSE_VERSION, 70 | REG_SupportsDoD = true 71 | }) 72 | 73 | function Create() 74 | --[[ Creates the user interface. ]] 75 | -- input 76 | InImage = self:AddInput("Input", "Input", { 77 | LINKID_DataType = "Image", 78 | LINK_Main = 1 79 | }) 80 | 81 | -- output 82 | OutImage = self:AddOutput("Output", "Output", { 83 | LINKID_DataType = "Image", 84 | LINK_Main = 1 85 | }) 86 | 87 | -- locator 88 | LocatorMatte = self:AddInput("Matte Locator", "Locator", { 89 | LINKID_DataType = "Point", 90 | INPID_InputControl = "OffsetControl", 91 | INPID_PreviewControl = "CrosshairControl", 92 | INP_External = false, 93 | INP_Passive = true 94 | }) 95 | 96 | ButtonAdd = self:AddInput("Add", "Add", { 97 | LINKID_DataType = "Number", 98 | INPID_InputControl = "ButtonControl", 99 | INP_External = false, 100 | INP_DoNotifyChanged = true, 101 | ICD_Width = 1 / 3 102 | }) 103 | 104 | ButtonRemove = self:AddInput("Remove", "Remove", { 105 | LINKID_DataType = "Number", 106 | INPID_InputControl = "ButtonControl", 107 | INP_External = false, 108 | INP_DoNotifyChanged = true, 109 | ICD_Width = 1 / 3 110 | }) 111 | 112 | ButtonToggle = self:AddInput("Toggle", "Toggle", { 113 | LINKID_DataType = "Number", 114 | INPID_InputControl = "ButtonControl", 115 | INP_External = false, 116 | INP_DoNotifyChanged = true, 117 | ICD_Width = 1 / 3 118 | }) 119 | 120 | create_separator() 121 | 122 | -- preview 123 | ComboViewMode = self:AddInput("View Mode", "ViewMode", { 124 | LINKID_DataType = "Number", 125 | INPID_InputControl = "ComboControl", 126 | INP_Default = 0.0, 127 | INP_Integer = true, 128 | { CCS_AddString = VIEW_MODES[1] }, 129 | { CCS_AddString = VIEW_MODES[2] }, 130 | { CCS_AddString = VIEW_MODES[3] }, 131 | { CCS_AddString = VIEW_MODES[4] }, 132 | INP_External = true 133 | }) 134 | 135 | create_separator() 136 | 137 | -- matte list 138 | TextMatteList = self:AddInput("Matte List", "MatteList", { 139 | LINKS_Name = "Matte List", 140 | LINKID_DataType = "Text", 141 | INPID_InputControl = "TextEditControl", 142 | TEC_Lines = 1, 143 | TEC_Wrap = false, 144 | TEC_DeferSetInputs = true, 145 | INP_External = true 146 | }) 147 | 148 | ButtonClear = self:AddInput("Clear", "Clear", { 149 | LINKS_Name = "Clear", 150 | LINKID_DataType = "Number", 151 | INPID_InputControl = "ButtonControl", 152 | INP_Integer = true, 153 | INP_DoNotifyChanged = true, 154 | INP_External = false 155 | }) 156 | 157 | create_separator() 158 | 159 | -- layer selection 160 | SliderCryptoLayer = self:AddInput("Layer Index", "LayerIndex", { 161 | LINKID_DataType = "Number", 162 | INPID_InputControl = "SliderControl", 163 | IC_Steps = 1, 164 | INP_Integer = true, 165 | INP_MinAllowed = 1, 166 | INP_MaxAllowed = 100, 167 | INP_External = true 168 | }) 169 | 170 | TextCryptoLayer = self:AddInput("Layer Name", "LayerName", { 171 | LINKID_DataType = "Text", 172 | INPID_InputControl = "TextEditControl", 173 | TEC_Lines = 1, 174 | TEC_ReadOnly = true, 175 | INP_External = false 176 | }) 177 | 178 | -- advanced 179 | SeparatorCallbacks = create_separator() 180 | SeparatorCallbacks:SetAttrs({ICS_ControlPage = "Advanced"}) 181 | 182 | -- name checker 183 | LocatorName = self:AddInput("Locator Name Checker", "LocatorName", { 184 | LINKID_DataType = "Point", 185 | INPID_InputControl = "OffsetControl", 186 | INPID_PreviewControl = "CrosshairControl", 187 | ICS_Name = "Name Checker Locator", 188 | ICS_ControlPage = "Advanced", 189 | PC_Visible = false, 190 | INP_External = false 191 | }) 192 | 193 | ButtonShow = self:AddInput("Show", "Show", { 194 | LINKID_DataType = "Number", 195 | INPID_InputControl = "ButtonControl", 196 | INP_External = false, 197 | INP_DoNotifyChanged = true, 198 | ICD_Width = 0.5 199 | }) 200 | 201 | ButtonHide = self:AddInput("Hide", "Hide", { 202 | LINKID_DataType = "Number", 203 | INPID_InputControl = "ButtonControl", 204 | INP_External = false, 205 | INP_DoNotifyChanged = true, 206 | ICD_Width = 0.5 207 | }) 208 | 209 | TextMatteName = self:AddInput("Matte Name", "MatteName", { 210 | LINKS_Name = "Matte Name", 211 | LINKID_DataType = "Text", 212 | INPID_InputControl = "TextEditControl", 213 | TEC_Lines = 1, 214 | TEC_Wrap = false, 215 | TEC_ReadOnly = true, 216 | INP_External = false 217 | }) 218 | 219 | separator = create_separator() 220 | 221 | -- callbacks 222 | separator:SetAttrs({IC_Visible = SHOW_CALLBACKS}) 223 | CheckboxAdd = self:AddInput("Add Callback", "AddCallback", { 224 | LINKID_DataType = "Number", 225 | INPID_InputControl = "CheckboxControl", 226 | INP_Integer = true, 227 | INP_Default = 0.0, 228 | IC_Visible = SHOW_CALLBACKS, 229 | INP_External = false 230 | }) 231 | 232 | CheckboxRemove = self:AddInput("Remove Callback", "RemoveCallback", { 233 | LINKID_DataType = "Number", 234 | INPID_InputControl = "CheckboxControl", 235 | INP_Integer = true, 236 | INP_Default = 0.0, 237 | IC_Visible = SHOW_CALLBACKS, 238 | INP_External = false 239 | }) 240 | 241 | CheckboxToggle = self:AddInput("Toggle Callback", "ToggleCallback", { 242 | LINKID_DataType = "Number", 243 | INPID_InputControl = "CheckboxControl", 244 | INP_Integer = true, 245 | INP_Default = 0.0, 246 | IC_Visible = SHOW_CALLBACKS, 247 | INP_External = false 248 | }) 249 | end 250 | 251 | function Process(req) 252 | --[[ 253 | Processes all events for one render cycle. 254 | 255 | :param req: render request object 256 | :type req: Request 257 | ]] 258 | local t_start = os.clock() 259 | cryptoutils.log_info("-- process started") 260 | 261 | -- read current settings 262 | local current_layer_count = SliderCryptoLayer:GetAttr("INP_MaxAllowed") 263 | local current_layer_index = SliderCryptoLayer:GetSource(req.Time).Value 264 | local current_layer_name = TextCryptoLayer:GetSource(req.Time).Value 265 | local current_matte_names_str = TextMatteList:GetSource(req.Time).Value 266 | 267 | -- get input image 268 | local input_image = InImage:GetValue(req) 269 | 270 | -- validate input image depth 271 | module.validate_image_depth(input_image) 272 | 273 | -- read metadata 274 | cryptoutils.log_info("reading metadata ...") 275 | local metadata = cryptoutils.get_cryptomatte_metadata(input_image.Metadata) 276 | local layer_count = metadata["layer_count"] 277 | 278 | -- read/check layer data from current index 279 | local layer_index = current_layer_index 280 | local layer_id = metadata["index_to_id"][tostring(current_layer_index)] 281 | if layer_id == nil then 282 | cryptoutils.log_error(string.format("layer '%s' (index %s) not present in metadata", current_layer_name, current_layer_index)) 283 | end 284 | local layer_name = metadata["id_to_name"][layer_id] 285 | 286 | -- apply GUI updates 287 | if layer_count ~= current_layer_count then 288 | SliderCryptoLayer:SetAttrs({INP_MinAllowed = 1, 289 | INP_MaxAllowed = layer_count, 290 | INP_MinScale = 1, 291 | INP_MaxScale = layer_count}) 292 | end 293 | 294 | if layer_name ~= current_layer_name then 295 | cryptoutils.log_info(string.format("setting layer name: '%s'", layer_name)) 296 | TextCryptoLayer:SetSource(Text(layer_name), req.Time, 0) 297 | end 298 | 299 | -- read raw manifest from file 300 | local manifest_file = metadata["layers"][layer_id]["manif_file"] 301 | local raw_manifest = metadata["layers"][layer_id]["manifest"] 302 | if manifest_file ~= nil then 303 | cryptoutils.log_info(string.format("reading manifest file: '%s'", manifest_file)) 304 | raw_manifest = cryptoutils.read_manifest_file(metadata["path"], manifest_file) 305 | end 306 | 307 | -- decode manifest string to table 308 | cryptoutils.log_info("decoding manifest ...") 309 | local manifest = cryptoutils.decode_manifest(raw_manifest) 310 | 311 | -- create layer images 312 | cryptoutils.log_info("creating layer images ...") 313 | local layer_images = cryptoutils.get_layer_images(input_image, metadata["path"], layer_name, 1) 314 | 315 | -- get matte names 316 | cryptoutils.log_info("reading matte names ...") 317 | local matte_names = cryptoutils.get_matte_names(current_matte_names_str) 318 | local matte_name_str = current_matte_names_str 319 | 320 | -- apply callbacks 321 | local callback_add = CheckboxAdd:GetSource(req.Time).Value 322 | local callback_remove = CheckboxRemove:GetSource(req.Time).Value 323 | local callback_toggle = CheckboxToggle:GetSource(req.Time).Value 324 | 325 | if callback_add == 1 or callback_remove == 1 or callback_toggle == 1 then 326 | cryptoutils.log_info("processing callbacks ...") 327 | local screen_pos = LocatorMatte:GetSource(req.Time) 328 | local matte_name = module.get_screen_matte_name(input_image, layer_images, screen_pos, manifest) 329 | local update = false 330 | 331 | if callback_add == 1 then 332 | if matte_name ~= nil then 333 | if matte_names[matte_name] == nil then 334 | cryptoutils.log_info(string.format("adding matte: '%s'", matte_name)) 335 | matte_names[matte_name] = true 336 | update = true 337 | end 338 | end 339 | CheckboxAdd:SetSource(Number(0), req.Time, 0) 340 | end 341 | if callback_remove == 1 then 342 | if matte_name ~= nil then 343 | if matte_names[matte_name] then 344 | cryptoutils.log_info(string.format("removing matte: '%s'", matte_name)) 345 | matte_names[matte_name] = nil 346 | update = true 347 | end 348 | end 349 | CheckboxRemove:SetSource(Number(0), req.Time, 0) 350 | end 351 | if callback_toggle == 1 then 352 | if matte_name ~= nil then 353 | if matte_names[matte_name] then 354 | cryptoutils.log_info(string.format("removing matte: '%s'", matte_name)) 355 | matte_names[matte_name] = nil 356 | update = true 357 | else 358 | cryptoutils.log_info(string.format("adding matte: '%s'", matte_name)) 359 | matte_names[matte_name] = true 360 | update = true 361 | end 362 | end 363 | CheckboxToggle:SetSource(Number(0), req.Time, 0) 364 | end 365 | 366 | if update then 367 | name_array = {} 368 | for name, presence in pairs(matte_names) do 369 | if presence then 370 | table.insert(name_array, "\"" .. name .. "\"") 371 | end 372 | end 373 | matte_name_str = table.concat(name_array, ", ") 374 | TextMatteList:SetSource(Text(matte_name_str), req.Time, 0) 375 | end 376 | end 377 | 378 | -- set name checker 379 | local checker_locator_visible = LocatorName:GetAttr("PC_Visible") 380 | if checker_locator_visible then 381 | cryptoutils.log_info("updating name checker ...") 382 | local checker_screen_pos = LocatorName:GetSource(req.Time) 383 | local checker_matte_name = module.get_screen_matte_name(input_image, layer_images, checker_screen_pos, manifest) 384 | local current_matte_name = TextMatteName:GetSource(req.Time).Value 385 | 386 | if checker_matte_name == nil then 387 | checker_matte_name = "" 388 | else 389 | checker_matte_name = "\"" .. checker_matte_name .. "\"" 390 | end 391 | 392 | if checker_matte_name ~= current_matte_name then 393 | TextMatteName:SetSource(Text(checker_matte_name), req.Time, 0) 394 | end 395 | end 396 | 397 | -- create matte image 398 | cryptoutils.log_info("creating matte image ...") 399 | local matte_image = cryptoutils.create_matte_image(input_image, layer_images, manifest, matte_names) 400 | 401 | -- get view mode 402 | local view_mode_index = ComboViewMode:GetSource(req.Time).Value 403 | local view_mode = VIEW_MODES[view_mode_index + 1] 404 | if view_mode == nil then 405 | cryptoutils.log_error(string.format("unknown view mode: '%s'", view_mode)) 406 | end 407 | 408 | -- create preview image 409 | cryptoutils.log_info(string.format("creating '%s' preview image ...", view_mode)) 410 | local output_image 411 | 412 | if view_mode == VIEW_MODE_MATTE then 413 | output_image = matte_image 414 | else 415 | if view_mode == VIEW_MODE_EDGES then 416 | output_image = cryptoutils.create_preview_image_edges(input_image, layer_images) 417 | elseif view_mode == VIEW_MODE_COLORS then 418 | output_image = cryptoutils.create_preview_image_colors(input_image, layer_images) 419 | elseif view_mode == VIEW_MODE_BEAUTY then 420 | output_image = input_image:CopyOf() 421 | end 422 | 423 | -- apply matte image 424 | output_image = output_image:ChannelOpOf("Add", matte_image, { R = "fg.A", G = "fg.A"}) 425 | output_image = output_image:ChannelOpOf("Copy", matte_image, { A = "fg.A"}) 426 | end 427 | 428 | OutImage:Set(req, output_image) 429 | 430 | local t_end = os.clock() - t_start 431 | cryptoutils.log_info(string.format("elapsed time: %.3f", t_end)) 432 | cryptoutils.log_info("-- process ended") 433 | end 434 | 435 | function NotifyChanged(inp, param, time) 436 | --[[ 437 | Handles all input control events. 438 | 439 | :param inp: Input that triggered a signal. 440 | :type inp: Input 441 | 442 | :param param: Parameter object holding the (new) value. 443 | :type param: Parameter 444 | 445 | :param time: Current frame number. 446 | :type time: number 447 | ]] 448 | -- trigger callbacks 449 | if param and param.Value == 1 then 450 | if inp == ButtonAdd then 451 | CheckboxAdd:SetSource(Number(1), time, 0) 452 | elseif inp == ButtonRemove then 453 | CheckboxRemove:SetSource(Number(1), time, 0) 454 | elseif inp == ButtonToggle then 455 | CheckboxToggle:SetSource(Number(1), time, 0) 456 | elseif inp == ButtonClear then 457 | TextMatteList:SetSource(Text(""), time, 0) 458 | elseif inp == ButtonShow then 459 | LocatorName:SetAttrs({PC_Visible = true}) 460 | elseif inp == ButtonHide then 461 | TextMatteName:SetSource(Text(""), time, 0) 462 | LocatorName:SetAttrs({PC_Visible = false}) 463 | end 464 | end 465 | end 466 | -------------------------------------------------------------------------------- /fusion/Modules/Lua/cryptomatte_utilities.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Version : 1.4.0 3 | Requires : Fusion 9.0.2 - 17.1.1+ 4 | Requires : Resolve 15.1 - 17.1.1+ 5 | Optional : cjson 6 | Created by : Cédric Duriau [duriau.cedric@live.be] 7 | Kristof Indeherberge [xmnr0x23@gmail.com] 8 | Andrew Hazelden [andrew@andrewhazelden.com] 9 | --]] 10 | 11 | -- ============================================================================ 12 | -- third party modules 13 | -- ============================================================================ 14 | function prefered_load(first, second) 15 | --[[ 16 | Loads first module, if that fails, loads second module. 17 | 18 | :param first: Name of preferred module to load first, if loading this module 19 | fails, the second one will be loaded. 20 | :type first: string 21 | 22 | :param second: Name of second module to load if the first module could not 23 | be loaded. If this fails, the default error when loading a 24 | non-existing module will be raised. 25 | :type second: string 26 | 27 | :rtype: table 28 | -- ]] 29 | local status, module = pcall(require, first) 30 | if not status then 31 | module = require(second) 32 | end 33 | return module 34 | end 35 | 36 | -- load cjson module if present, if not, load Fusion stdlib dkjson module 37 | local json = prefered_load("cjson", "dkjson") 38 | local bit = require("bit") 39 | 40 | -- ============================================================================ 41 | -- constants 42 | -- ============================================================================ 43 | ENV_VAR_LOG_LEVEL = "CRYPTOMATTE_LOG_LEVEL" 44 | METADATA_PREFIX = "cryptomatte/" 45 | REGEX_METADATA = "%a+/([a-z0-9]+)/(.+)" 46 | METADATA_KEY_NAME = "name" 47 | METADATA_KEY_FILENAME = "Filename" 48 | REGEX_MATTE_LIST = "([^,]+),?%s*" 49 | REGEX_LAYER_CHANNEL = "(.+)([0-9]+)[.](.+)" 50 | CHANNEL_NAME_MAP = {r="r", red="r", 51 | g="g", green="g", 52 | b="b", blue="b", 53 | a="a", alpha="a"} 54 | BACKGROUND_MATTE_NAME = "Background (value RGBA=0000)" 55 | 56 | -- ============================================================================ 57 | -- ffi C utils 58 | -- ============================================================================ 59 | -- int / float representation of hash 60 | ffi.cdef [[ union int_flt { uint32_t i; float f; }; ]] 61 | local int_flt = ffi.new("union int_flt") 62 | 63 | -- ============================================================================ 64 | -- fusion centric functions (EXRIO/scanline) 65 | -- ============================================================================ 66 | function get_scale_factor(image) 67 | --[[ 68 | Returns the scale factor to apply. 69 | 70 | Two scale factors are possible, Proxy Scale and Auto Proxy Scale. 71 | Auto proxy scale has precedence over proxy scale. 72 | 73 | :param image: Source image holding proxy scale value. (1 = off, >1 = on) 74 | :type image: Image 75 | 76 | :rtype: number 77 | ]] 78 | -- proxy 79 | local proxy_scale = image.ProxyScale 80 | local proxy_enabled 81 | if proxy_scale == 1 then 82 | proxy_enabled = false 83 | else 84 | proxy_enabled = true 85 | end 86 | 87 | -- auto proxy 88 | local prefs = self.Comp:GetPrefs() 89 | local proxy_prefs = prefs["Comp"]["Interactive"]["Proxy"] 90 | local auto_proxy_enabled = proxy_prefs["Auto"] 91 | local auto_proxy_scale = proxy_prefs["AutoScale"] 92 | 93 | -- determine scale 94 | local scale = 1 95 | if auto_proxy_enabled then 96 | scale = auto_proxy_scale 97 | elseif proxy_enabled then 98 | scale = proxy_scale 99 | end 100 | return scale 101 | end 102 | 103 | function get_layer_images(input_image, layer_name, channel_hierarchy, exr, partnum) 104 | --[[ 105 | Returns the images for all indices of layer. 106 | 107 | :param input_image: Source Cryptomatte image. 108 | :type input_image: Image 109 | 110 | :param layer_name: Name of the layer to get all index images for. 111 | :type layer_name: string 112 | 113 | :param channel_hierarchy: Channel datastructure by layer and index. 114 | :type channel_hierarchy: table 115 | 116 | :param exr: EXRIO module instance loaded with the input EXR image. 117 | :type exr: EXRIO 118 | 119 | :param partnum: EXR multipart index. 120 | :type partnum: number 121 | 122 | :rtype: table[string, Image] 123 | ]] 124 | -- calculate datawindow to keep/scan 125 | local dispw = exr:DisplayWindow(partnum) 126 | local ox = dispw.left 127 | local oy = dispw.bottom 128 | local w = dispw.right - dispw.left 129 | local h = dispw.top - dispw.bottom 130 | local dataw = exr:DataWindow(partnum) 131 | local imgw = ImgRectI(dataw) 132 | imgw:Offset(-ox, -oy) 133 | 134 | -- get pixel aspect ratio 135 | local pixel_aspect_ratio = exr:PixelAspectRatio(partnum) 136 | 137 | -- get scale factor to apply 138 | local scale = get_scale_factor(input_image) 139 | 140 | -- select EXR part to load 141 | exr:Part(partnum) 142 | 143 | local images = {} 144 | for index, channels in pairs(channel_hierarchy[layer_name]) do 145 | -- create image from scratch 146 | local image = Image({IMG_Width = w, 147 | IMG_Height = h, 148 | IMG_Depth = IMDP_128bitFloat, 149 | IMG_DataWindow = imgw, 150 | IMG_YScale = 1.0 / pixel_aspect_ratio}) 151 | 152 | -- write out loaded EXR part image channels to layer image 153 | exr:Channel(channels["r"], ANY_TYPE, 1, CHAN_RED) 154 | exr:Channel(channels["g"], ANY_TYPE, 1, CHAN_GREEN) 155 | exr:Channel(channels["b"], ANY_TYPE, 1, CHAN_BLUE) 156 | exr:Channel(channels["a"], ANY_TYPE, 1, CHAN_ALPHA) 157 | exr:ReadPart(partnum, {image}) 158 | 159 | -- handle proxy scaling 160 | local result = image 161 | if scale ~= 1 then 162 | local resized_image = Image({IMG_Like = image, 163 | IMG_Width = input_image.Width, 164 | IMG_Height = input_image.Height}) 165 | image:Resize(resized_image, {RSZ_Filter = "Nearest", 166 | RSZ_Width = input_image.Width, 167 | RSZ_Height = input_image.Height}) 168 | result = resized_image 169 | end 170 | images[tonumber(index)] = result 171 | end 172 | 173 | return images 174 | end 175 | 176 | function create_preview_image_colors_init() 177 | --[[ 178 | Scanline initializer function for the "colors" preview image. 179 | 180 | This function initializes pixel objects and pointers to re-use in scanline 181 | function. This avoids the creation of these objects at every X or Y pass, 182 | improving the overall performance. 183 | 184 | ! FOLLOWING PARAMTERS ARE PASSED INDIRECTLY, SEE `DoMultiProcess` USAGE ! 185 | 186 | :param output_image: Preview image to write pixel information to. 187 | :type output_image: Image 188 | 189 | :param layer_0_image: Image for the layer 0. 190 | :type layer_0_image: Image 191 | 192 | :param layer_1_image: Image for the layer 1. 193 | :type layer_1_image: Image 194 | -- ]] 195 | global_p_00 = Pixel() 196 | global_p_01 = Pixel() 197 | local_p = Pixel() 198 | 199 | pixptr_00 = PixPtr(layer_0_image, global_p_00) 200 | pixptr_01 = PixPtr(layer_1_image, global_p_01) 201 | pixptr_out = PixPtr(output_image, local_p) 202 | end 203 | 204 | function create_preview_image_colors_scanline(n) 205 | --[[ 206 | Scanline function that creates the "colors" preview image. 207 | 208 | This function builds the keyable surface preview image. The algorithm used 209 | to calculate the pixel information was provided by Jonah Friedman in a Nuke 210 | sample which I translated to Lua. 211 | 212 | :param n: Absolute Y coordinate to execute scanline (left to right) pass. 213 | :type n: number 214 | 215 | ! FOLLOWING PARAMTERS ARE PASSED INDIRECTLY, SEE `DoMultiProcess` USAGE ! 216 | 217 | :param output_image: Preview image to write pixel information to. 218 | :type output_image: Image 219 | 220 | :param layer_0_image: Image for the layer 0. 221 | :type layer_0_image: Image 222 | 223 | :param layer_1_image: Image for the layer 1. 224 | :type layer_1_image: Image 225 | -- ]] 226 | -- calculate real scanline y position 227 | local y = n + output_image.DataWindow.bottom 228 | 229 | -- set start X position for scanline pass 230 | pixptr_00:GotoXY(output_image.DataWindow.left, y) 231 | pixptr_01:GotoXY(output_image.DataWindow.left, y) 232 | pixptr_out:GotoXY(output_image.DataWindow.left, y) 233 | 234 | for _ = output_image.DataWindow.left, output_image.DataWindow.right - 1 do 235 | -- go to correct X position 236 | pixptr_00:GetNextPixel(global_p_00) 237 | pixptr_01:GetNextPixel(global_p_01) 238 | 239 | -- get mantissa of R and B channels of both layer 0 and 1 240 | m00_rg, _ = math.frexp(math.abs(global_p_00.R)) 241 | m00_ba, _ = math.frexp(math.abs(global_p_00.B)) 242 | m01_rg, _ = math.frexp(math.abs(global_p_01.R)) 243 | m01_ba, _ = math.frexp(math.abs(global_p_01.B)) 244 | 245 | -- calculate RGB channel values for final id colored image 246 | -- red 247 | r_00_rg = (m00_rg * 1 % 0.25) * global_p_00.G 248 | r_00_ba = (m00_ba * 1 % 0.25) * global_p_00.A 249 | r_01_rg = (m01_rg * 1 % 0.25) * global_p_01.G 250 | r_01_ba = (m01_ba * 1 % 0.25) * global_p_01.A 251 | 252 | -- green 253 | g_00_rg = (m00_rg * 4 % 0.25) * global_p_00.G 254 | g_00_ba = (m00_ba * 4 % 0.25) * global_p_00.A 255 | g_01_rg = (m01_rg * 4 % 0.25) * global_p_01.G 256 | g_01_ba = (m01_ba * 4 % 0.25) * global_p_01.A 257 | 258 | -- blue 259 | b_00_rg = (m00_rg * 16 % 0.25) * global_p_00.G 260 | b_00_ba = (m00_ba * 16 % 0.25) * global_p_00.A 261 | b_01_rg = (m01_rg * 16 % 0.25) * global_p_01.G 262 | b_01_ba = (m01_ba * 16 % 0.25) * global_p_01.A 263 | 264 | -- store calculated R,G and B values 265 | local_p.R = (r_00_rg + r_00_ba + r_01_rg + r_01_ba) 266 | local_p.G = (g_00_rg + g_00_ba + g_01_rg + g_01_ba) 267 | local_p.B = (b_00_rg + b_00_ba + b_01_rg + b_01_ba) 268 | 269 | -- set output pixel pointer 270 | pixptr_out:SetNextPixel(local_p) 271 | end 272 | end 273 | 274 | function create_matte_image_init() 275 | --[[ 276 | Scanline initializer function that creates the matte image. 277 | 278 | ! FOLLOWING PARAMTERS ARE PASSED INDIRECTLY, SEE `DoMultiProcess` USAGE ! 279 | 280 | :param layer_image: Image of an isolated Cryptomatte layer. 281 | :type layer_image: Image 282 | 283 | :param matte_values: Matte ID float values to match. 284 | :type matte_values: table[number, boolean] 285 | 286 | :param dod: Datawindow to process pixels for. 287 | :type dod: FuRect 288 | 289 | :param output_image: Image to store mattes in. 290 | :type output_image: Image 291 | ]] 292 | input_p = Pixel() 293 | output_p = Pixel() 294 | pixptr_in = PixPtr(layer_image, input_p) 295 | pixptr_out = PixPtr(output_image, output_p) 296 | end 297 | 298 | function create_matte_image_scanline(n) 299 | --[[ 300 | Scanline function that creates the matte image. 301 | 302 | :param n: Absolute Y coordinate to execute scanline (left to right) pass. 303 | :type n: number 304 | 305 | ! FOLLOWING PARAMTERS ARE PASSED INDIRECTLY, SEE `DoMultiProcess` USAGE ! 306 | 307 | :param layer_image: Image of an isolated Cryptomatte layer. 308 | :type layer_image: Image 309 | 310 | :param matte_values: Known matte float ID values from manifest. 311 | :type matte_values: table[number, boolean] 312 | 313 | :param dod: Datawindow to process pixels for. 314 | :type dod: FuRect 315 | 316 | Algorithm: 317 | - matte_pixel.A += pixel.G if pixel.R in matte_float_id_values else 0 318 | - matte_pixel.A += pixel.A if pixel.B in matte_float_id_values else 0 319 | 320 | :param output_image: Image to store mattes in. 321 | :type output_image: Image 322 | ]] 323 | local y = n + dod.bottom 324 | 325 | -- set start X position for scanline pass 326 | pixptr_in:GotoXY(dod.left, y) 327 | pixptr_out:GotoXY(dod.left, y) 328 | 329 | local update 330 | for _ = dod.left, dod.right - 1 do 331 | -- go to correct X position 332 | pixptr_in:GetNextPixel(input_p) 333 | pixptr_out:GetPixel(output_p) 334 | 335 | -- reset flag 336 | update = false 337 | 338 | -- detect rank 1 match (red match = add green) 339 | if matte_values[input_p.R] then 340 | output_p.A = output_p.A + input_p.G 341 | update = true 342 | end 343 | 344 | -- detect rank 2 match (blue match = add alpha) 345 | if matte_values[input_p.B] then 346 | output_p.A = output_p.A + input_p.A 347 | update = true 348 | end 349 | 350 | if update then 351 | pixptr_out:SetNextPixel(output_p) 352 | else 353 | pixptr_out:NextPixel() 354 | end 355 | end 356 | end 357 | 358 | function get_screen_pixel(image, x, y) 359 | --[[ 360 | Gets the pixel object from given image at given coordinates. 361 | 362 | :param x: Absolute x position in pixel units. 363 | :type x: number 364 | 365 | :param y: Absolute y position in pixel units. 366 | :type y: number 367 | 368 | :rtype: Pixel 369 | -- ]] 370 | local pixel = Pixel() 371 | image:GetPixel(x, y, pixel) 372 | return pixel 373 | end 374 | 375 | -- ============================================================================ 376 | -- module 377 | -- ============================================================================ 378 | module = {} 379 | 380 | -- private 381 | function module._format_log(level, message) 382 | --[[ 383 | Returns a formatted message to log. 384 | 385 | :param level: Name of the log level. 386 | :type level: string 387 | 388 | :param message: Message to log. 389 | :type message: string 390 | 391 | :rtype: string 392 | ]] 393 | return string.format("[Cryptomatte][%s][%s] %s", self.Name, level, message) 394 | end 395 | 396 | function module._get_log_level() 397 | --[[ 398 | Returns the log level. 399 | 400 | Log levels: 401 | - 0: error (default) 402 | - 1: warning 403 | - 2: info 404 | 405 | Setting the log level to a high number than specified log levels will 406 | result in applying all lower log levels. 407 | 408 | :rtype: number 409 | ]] 410 | local log_level = os.getenv(ENV_VAR_LOG_LEVEL) 411 | if log_level == nil then 412 | return 1 413 | end 414 | return tonumber(log_level) 415 | end 416 | 417 | function module._string_starts_with(str, substr) 418 | --[[ 419 | Returns whether the given string starts with the given substring. 420 | 421 | :param str: Text to match with substring. 422 | :type str: string 423 | 424 | :param substr: Substring to match text with. 425 | :type substr: string 426 | 427 | :rtype: boolean 428 | ]] 429 | return string.sub(str, 1, string.len(substr)) == substr 430 | end 431 | 432 | function module._string_ends_with(str, substr) 433 | --[[ 434 | Returns whether the given string ends with the given substring. 435 | 436 | :param str: Text to match with substring. 437 | :type str: string 438 | 439 | :param substr: Substring to match text with. 440 | :type substr: string 441 | 442 | :rtype: boolean 443 | ]] 444 | return string.sub(str, -string.len(substr), -1) == substr 445 | end 446 | 447 | function module._string_split(str, pattern) 448 | --[[ 449 | Splits the given string to an array of strings using given pattern. 450 | 451 | :param str: String to split/convert to an array. 452 | :type str: string 453 | 454 | :param pattern: Pattern to split string with. 455 | :type pattern: string 456 | 457 | :rtype: table[string] 458 | -- ]] 459 | local parts = {} 460 | for part in string.gmatch(str, pattern) do 461 | table.insert(parts, part) 462 | end 463 | return parts 464 | end 465 | 466 | function module._get_absolute_path(path) 467 | --[[ 468 | Returns the absolute representation of a relative path. 469 | 470 | :param path: Relative path. 471 | :type path: string 472 | 473 | :rtype: string 474 | ]] 475 | local abs_path = self.Comp:MapPath(path) 476 | return abs_path:gsub("([\\])", "/") 477 | end 478 | 479 | function module._solve_channel_name(name) 480 | --[[ 481 | Returns the internal representation of a channel. 482 | 483 | :param name: Channel name to get internal representation of 484 | :type name: string 485 | 486 | :rtype: string 487 | ]] 488 | return CHANNEL_NAME_MAP[string.lower(name)] 489 | end 490 | 491 | function module._get_channel_hierarchy(layer_name, channels) 492 | --[[ 493 | Returns the channels of all indices of a layer. 494 | 495 | :param layer_name: Name of the layer to get channels of. 496 | :type layer_name: string 497 | 498 | :param channels: All channel objects of an EXR file. 499 | :type channels: table[table] 500 | 501 | :rtype: table 502 | ]] 503 | local hierarchy = {} 504 | for _, channel in ipairs(channels) do 505 | full_channel_name = channel["Name"] 506 | if module._string_starts_with(full_channel_name, layer_name) then 507 | -- get layer name & index info from channel name 508 | local _, layer_index, channel_name = string.match(full_channel_name, REGEX_LAYER_CHANNEL) 509 | 510 | -- get internal channel name representation 511 | local internal_channel_name = nil 512 | if layer_index and channel_name then 513 | internal_channel_name = module._solve_channel_name(channel_name) 514 | 515 | -- skip error for matching layer without index (beauty layer) 516 | if layer_index and not internal_channel_name then 517 | module.log_error(string.format("failed to get internal name for channel: '%s'", full_channel_name)) 518 | end 519 | end 520 | 521 | if internal_channel_name ~= nil then 522 | if hierarchy[layer_name] == nil then 523 | hierarchy[layer_name] = {} 524 | end 525 | 526 | if hierarchy[layer_name][layer_index] == nil then 527 | hierarchy[layer_name][layer_index] = {} 528 | end 529 | 530 | if hierarchy[layer_name][layer_index][internal_channel_name] == nil then 531 | hierarchy[layer_name][layer_index][internal_channel_name] = full_channel_name 532 | end 533 | end 534 | end 535 | end 536 | return hierarchy 537 | end 538 | 539 | function module._get_absolute_position(width, height, rel_x, rel_y) 540 | --[[ 541 | Gets the absolute values for given relative coordinates. 542 | 543 | :param width: Reference width. 544 | :type width: number 545 | 546 | :param width: Reference height. 547 | :type width: number 548 | 549 | :param rel_x: Relative x coordinate. 550 | :type rel_x: number 551 | 552 | :param rel_y: Relative y coordinate. 553 | :type rel_y: number 554 | 555 | :rtype: number, number 556 | --]] 557 | return math.floor(width / (1 / rel_x)), math.floor(height / (1 / rel_y)) 558 | end 559 | 560 | function module._is_position_in_rect(rect, x, y) 561 | --[[ 562 | Validates if the given x and y coordinates are in the given rect bounds. 563 | 564 | :param rect: Integer rectangle position to validate x and y position with. 565 | :type rect: FuRectInt 566 | 567 | :param x: Y position to validate. 568 | :type x: number 569 | 570 | :param y: Y position to validate. 571 | :type y: number 572 | 573 | :rtype: bool 574 | --]] 575 | if x < rect.left or x > rect.right then 576 | return false 577 | end 578 | if y > rect.top or y < rect.bottom then 579 | return false 580 | end 581 | return true 582 | end 583 | 584 | function module._hex_to_float(hex) 585 | --[[ 586 | Returns the float representation of a hexademical string. 587 | 588 | :param hex: Hexadecimal string. 589 | :type hex: string 590 | 591 | :rtype: number 592 | ]] 593 | int_flt.i = tonumber(hex, 16) 594 | --fix exponent if required 595 | local exp = bit.rshift(int_flt.i, 23) 596 | exp = bit.band(exp, 255) 597 | if exp == 0 or exp == 255 then 598 | int_flt.i = bit.bxor(int_flt.i, bit.lshift(1, 23)) 599 | end 600 | return int_flt.f 601 | end 602 | 603 | -- public 604 | function module.log_error(message) 605 | --[[ 606 | Logs an error message. 607 | 608 | :param message: Message to log. 609 | :type message: string 610 | ]] 611 | local log_level = module._get_log_level() 612 | if log_level >= 0 then 613 | print(module._format_log("ERROR", message)) 614 | end 615 | error("ERROR") 616 | end 617 | 618 | function module.log_warning(message) 619 | --[[ 620 | Logs an warning message. 621 | 622 | :param message: Message to log. 623 | :type message: string 624 | ]] 625 | local log_level = module._get_log_level() 626 | if log_level >= 1 then 627 | print(module._format_log("WARNING", message)) 628 | end 629 | end 630 | 631 | function module.log_info(message) 632 | --[[ 633 | Logs an information message. 634 | 635 | :param message: Message to log. 636 | :type message: string 637 | ]] 638 | local log_level = module._get_log_level() 639 | if log_level >= 2 then 640 | print(module._format_log("INFO", message)) 641 | end 642 | end 643 | 644 | function module.get_cryptomatte_metadata(metadata) 645 | --[[ 646 | Reads the Cryptomatte metadata of an EXR file. 647 | 648 | :param metadata: Source Cryptomatte EXR metadata. 649 | :type metadata: table 650 | 651 | :rtype: table 652 | ]] 653 | -- exr path metadata 654 | local exr_path = nil 655 | local filename = metadata[METADATA_KEY_FILENAME] 656 | if filename ~= nil then 657 | exr_path = module._get_absolute_path(filename) 658 | else 659 | module.log_error("metadata key 'Filename' empty/not set") 660 | end 661 | 662 | -- cryptomatte metadata 663 | local layer_data_by_id = {} 664 | local layer_names = {} 665 | local id_to_name = {} 666 | local name_to_id = {} 667 | local id_to_index = {} 668 | local index_to_id = {} 669 | 670 | for k, v in pairs(metadata) do 671 | if module._string_starts_with(k, METADATA_PREFIX) then 672 | -- get layer ID and cryptomatte metadata key 673 | local layer_id, partial_key = string.match(k, REGEX_METADATA) 674 | 675 | local layer_data = layer_data_by_id[layer_id] 676 | if layer_data == nil then 677 | layer_data = {} 678 | layer_data_by_id[layer_id] = layer_data 679 | end 680 | 681 | -- store cryptomatte layer metadata 682 | layer_data[partial_key] = v 683 | 684 | -- store layer name/id mapping for fast lookup 685 | if partial_key == METADATA_KEY_NAME then 686 | table.insert(layer_names, v) 687 | name_to_id[v] = layer_id 688 | id_to_name[layer_id] = v 689 | end 690 | end 691 | end 692 | 693 | -- validate layers were found 694 | if #layer_names == 0 then 695 | module.log_error("no cryptomatte metadata found") 696 | end 697 | 698 | -- store layer index/id mapping for fast lookup 699 | table.sort(layer_names) 700 | for i, layer_name in ipairs(layer_names) do 701 | local layer_id = name_to_id[layer_name] 702 | local layer_index = tostring(i) 703 | index_to_id[layer_index] = layer_id 704 | id_to_index[layer_id] = layer_index 705 | end 706 | 707 | local crypto_metadata = {} 708 | crypto_metadata["path"] = exr_path 709 | crypto_metadata["layer_count"] = #layer_names 710 | crypto_metadata["id_to_name"] = id_to_name 711 | crypto_metadata["name_to_id"] = name_to_id 712 | crypto_metadata["index_to_id"] = index_to_id 713 | crypto_metadata["id_to_index"] = id_to_index 714 | crypto_metadata["layers"] = layer_data_by_id 715 | return crypto_metadata 716 | end 717 | 718 | function module.read_manifest_file(exr_path, sidecar_file_path) 719 | --[[ 720 | Reads the manifest of an EXR sidecar file. 721 | 722 | :param exr_path: Absolute path of an EXR file. 723 | :type exr_path: string 724 | 725 | :param sidecar_file_path: Path of a sidecar file relative to the EXR file. 726 | :type sidecar_file_path: string 727 | 728 | :rtype: string 729 | ]] 730 | -- build absolute sidecar file path 731 | local path = exr_path:match("(.*/)") .. sidecar_file_path 732 | local fp = io.open(path, "r") 733 | if fp == nil then 734 | module.log_error(string.format("unable to open manifest file: %s", path)) 735 | return "" 736 | else 737 | local manifest_str = fp:read("*all") 738 | fp:close() 739 | return manifest_str 740 | end 741 | end 742 | 743 | function module.decode_manifest(raw_manifest) 744 | --[[ 745 | Deserializes a manifest from JSON string to table. 746 | 747 | :param raw_manifest: Serialized manifest. 748 | :type raw_manifest: string 749 | 750 | :rtype: table[string, string] 751 | ]] 752 | if raw_manifest == nil or raw_manifest == "" then 753 | module.log_error("no manifest to decode") 754 | return {} 755 | end 756 | return json.decode(raw_manifest) 757 | end 758 | 759 | function module.get_matte_names(matte_str) 760 | --[[ 761 | Returns the matte names from an input string. 762 | 763 | :param matte_str: Matte input string. Example: '"bunny", "flower"' 764 | :type matte_str: Matte input string. 765 | 766 | :rtype: table[string, bool] 767 | ]] 768 | -- get matte entries 769 | local name_array = module._string_split(matte_str, REGEX_MATTE_LIST) 770 | 771 | local name_set = {} 772 | for _, matte in ipairs(name_array) do 773 | -- detect double quote leading & trailing character 774 | if module._string_starts_with(matte, "\"") and module._string_ends_with(matte, "\"") then 775 | name = string.sub(matte, 2, matte:len() - 1) 776 | name_set[name] = true 777 | else 778 | module.log_warning(string.format("invalid syntax for matte: '%s'", matte)) 779 | end 780 | end 781 | return name_set 782 | end 783 | 784 | function module.get_layer_images(input_image, exr_path, layer_name, partnum) 785 | --[[ 786 | Returns the images for all indices of layer. 787 | 788 | :param input_image: Source Cryptomatte image. 789 | :type input_image: Image 790 | 791 | :param exr_path: Absolite path to the EXR file. 792 | :type exr_path: string 793 | 794 | :param layer_name: Name of the layer to get all index images for. 795 | :type layer_name: string 796 | 797 | :param partnum: EXR multipart index. 798 | :type partnum: number 799 | 800 | :rtype: table[string, Image] 801 | ]] 802 | -- load EXR file for current time 803 | local exr = EXRIO() 804 | exr:ReadOpen(exr_path, -1) 805 | 806 | if exr.NumParts > 0 then 807 | exr:Close() 808 | module.log_error("multipart EXR not supported") 809 | end 810 | 811 | local layer_images = {} 812 | if exr:ReadHeader() then 813 | local channels = exr:GetChannels(partnum) 814 | local channel_hierarchy = module._get_channel_hierarchy(layer_name, channels) 815 | if channel_hierarchy == {} or channel_hierarchy[layer_name] == nil then 816 | exr:Close() 817 | module.log_error(string.format("failed to read layer/index/channel information for layer: '%s'", layer_name)) 818 | end 819 | layer_images = get_layer_images(input_image, layer_name, channel_hierarchy, exr, partnum) 820 | end 821 | 822 | -- close EXR file pointer 823 | exr:Close() 824 | 825 | -- log EXRIO internal errors 826 | local exrio_error = exr:GetLastError() 827 | if exrio_error ~= "" then 828 | module.log_error(exrio_error) 829 | end 830 | 831 | return layer_images 832 | end 833 | 834 | function module.create_preview_image_colors(input_image, layer_images) 835 | --[[ 836 | Creates the preview image of view mode "edges". 837 | 838 | :param input_image: Source Cryptomatte image. 839 | :type input_image: Image 840 | 841 | :param layer_images: Cryptomatte layer images. 842 | :type layer_images: table[number, Image] 843 | 844 | Algorithm: 845 | - see `create_preview_image_colors_scanline` function documentation 846 | 847 | :rtype: Image 848 | ]] 849 | local output_image = input_image:CopyOf() 850 | output_image:Clear() 851 | self:DoMultiProcess(create_preview_image_colors_init, 852 | {output_image = output_image, 853 | layer_0_image = layer_images[0], 854 | layer_1_image = layer_images[1]}, 855 | output_image.DataWindow.top - output_image.DataWindow.bottom, 856 | create_preview_image_colors_scanline) 857 | return output_image 858 | end 859 | 860 | function module.create_preview_image_edges(input_image, layer_images) 861 | --[[ 862 | Creates the preview image of view mode "edges". 863 | 864 | :param input_image: Source Cryptomatte image. 865 | :type input_image: Image 866 | 867 | :param layer_images: Cryptomatte layer images. 868 | :type layer_images: table[number, Image] 869 | 870 | Algorithm: 871 | - output.r = input.r 872 | - output.g = input.g + (layer[0].a * 2) 873 | - output.b = input.b + (layer[0].a * 2) 874 | - output.a = input.a 875 | 876 | :rtype: Image 877 | ]] 878 | local coverage = layer_images[0]:CopyOf() 879 | coverage:Gain(1.0, 1.0, 1.0, 2.0) 880 | return input_image:ChannelOpOf("Add", coverage, {G="fg.A", B="fg.A"}) 881 | end 882 | 883 | function module.create_matte_image(input_image, layer_images, manifest, matte_names) 884 | --[[ 885 | Creates the monochannel matte images for selected names. 886 | 887 | :param input_image: Source Cryptomatte image 888 | :type input_image: Image 889 | 890 | :param layer_images: Cryptomatte layer images. 891 | :type layer_images: table[number, Image] 892 | 893 | :param manifest: Manifest storing matte id by name. 894 | :type manifest: table[string, string] 895 | 896 | :param matte_names: Selected mattes to isolate. 897 | :type matte_names: table[string, bool] 898 | 899 | Algorithm: 900 | - see `create_matte_image_scanline` function documentation 901 | 902 | :rtype: Image 903 | ]] 904 | local matte_values = {} 905 | for matte_name, _ in pairs(matte_names) do 906 | -- support background matte picking 907 | if matte_name == BACKGROUND_MATTE_NAME then 908 | matte_values[0.0] = true 909 | else 910 | local matte_id = manifest[matte_name] 911 | if matte_id == nil then 912 | module.log_warning(string.format("matte not present in manifest: %s", matte_name)) 913 | else 914 | local matte_value = module._hex_to_float(matte_id) 915 | matte_values[matte_value] = true 916 | end 917 | end 918 | end 919 | 920 | -- build monochannel matte image 921 | local output_image = Image({IMG_Like = input_image, 922 | IMG_CopyChannels = false, 923 | {IMG_Channel = "Alpha"}}) 924 | output_image:Clear() 925 | local dod = input_image.DataWindow 926 | 927 | if matte_values then 928 | for _, layer_image in pairs(layer_images) do 929 | self:DoMultiProcess(create_matte_image_init, 930 | {layer_image = layer_image, 931 | matte_values = matte_values, 932 | dod = dod, 933 | output_image = output_image}, 934 | dod.top - dod.bottom, 935 | create_matte_image_scanline) 936 | end 937 | end 938 | 939 | return output_image 940 | end 941 | 942 | function module.get_screen_matte_name(input_image, layer_images, screen_pos, manifest) 943 | --[[ 944 | Gets the name of a matte at given screen position. 945 | 946 | :param input_image: Source Cryptomatte image. 947 | :type input_image: Image 948 | 949 | :param layer_images: Cryptomatte layer images. 950 | :type layer_images: table[number, Image] 951 | 952 | :param screen_pos: Relative mouse position on canvas. 953 | :type screen_pos: Point 954 | 955 | :param manifest: Manifest storing matte id by name. 956 | :type manifest: table[string, string] 957 | 958 | :rtype: string or nil 959 | ]] 960 | -- get absolute screen position 961 | local x, y = module._get_absolute_position(input_image.Width, input_image.Height, screen_pos.X, screen_pos.Y) 962 | 963 | local dod = input_image.DataWindow 964 | if not module._is_position_in_rect(dod, x, y) then 965 | module.log_info(string.format("pixel (%s,%s) not present in data window (%s)", x, y, dod)) 966 | return nil 967 | end 968 | 969 | -- get matte values from manifest to detect pixel matches from 970 | local matte_values = {} 971 | local matte_names_by_value = {} 972 | for matte_name, matte_id in pairs(manifest) do 973 | local matte_value = module._hex_to_float(matte_id) 974 | matte_values[matte_value] = true 975 | matte_names_by_value[matte_value] = matte_name 976 | end 977 | 978 | -- add background matte name/value 979 | matte_values[0.0] = true 980 | matte_names_by_value[0.0] = BACKGROUND_MATTE_NAME 981 | 982 | local matte_value = nil 983 | for _, layer_image in pairs(layer_images) do 984 | -- get pixel at screen coordinates for current layer image 985 | local pixel = get_screen_pixel(layer_image, x, y) 986 | 987 | if pixel.R == 0.0 and pixel.G == 0.0 and pixel.B == 0.0 and pixel.A == 0.0 then 988 | -- background is being picked 989 | matte_value = 0.0 990 | else 991 | -- detect if any RGBA channel value matches a known matte float ID 992 | for _, value in ipairs({pixel.R, pixel.G, pixel.B, pixel.A}) do 993 | if value ~= 0.0 and matte_values[value] then 994 | matte_value = value 995 | break 996 | end 997 | end 998 | end 999 | 1000 | if matte_value ~= nil then 1001 | break 1002 | end 1003 | end 1004 | 1005 | if matte_value ~= nil then 1006 | return matte_names_by_value[matte_value] 1007 | end 1008 | 1009 | return nil 1010 | end 1011 | 1012 | function module.validate_image_depth(image) 1013 | --[[ 1014 | Validates the depth of provided image. 1015 | 1016 | Log a warning if the given image is not 32bit RGBA EXR. 1017 | 1018 | :param image: Image to validate. 1019 | :type image: Image 1020 | ]] 1021 | if image.Depth ~= 8 then 1022 | module.log_warning("input image is not 32bit RGBA EXR") 1023 | end 1024 | end 1025 | 1026 | return module 1027 | -------------------------------------------------------------------------------- /fusion/Modules/Lua/test_cryptomatte_utilities.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Version : 1.4.0 3 | Requires : Fusion 9.0.2 - 17.1.1+ 4 | Requires : Resolve 15.1 - 17.1.1+ 5 | Optional : cjson 6 | Created by : Cédric Duriau [duriau.cedric@live.be] 7 | Kristof Indeherberge [xmnr0x23@gmail.com] 8 | Andrew Hazelden [andrew@andrewhazelden.com] 9 | --]] 10 | 11 | local cryptoutils = require("cryptomatte_utilities") 12 | local json = require("dkjson") 13 | 14 | -- utils 15 | function collect_tests() 16 | --[[ 17 | Returns function names detected as test. 18 | 19 | Functions with names starting with "test_" will be picked up. 20 | 21 | :rtype: table[string] 22 | ]] 23 | local tests = {} 24 | local substr = "cryptomatte_test_" 25 | for name, _ in pairs(_G) do 26 | if string.sub(name, 1, string.len(substr)) == substr then 27 | table.insert(tests, name) 28 | end 29 | end 30 | table.sort(tests) 31 | return tests 32 | end 33 | 34 | function run_tests() 35 | --[[ 36 | Detects and runs all test functions of a module. 37 | 38 | :param module: Module to run all tests for. 39 | :type module: table[string, function] 40 | ]] 41 | -- collect all tests from module 42 | print("collectings test(s) ...") 43 | local tests = collect_tests() 44 | local ntests = #tests 45 | print(string.format("detected %s test(s) ...", ntests)) 46 | 47 | print("running tests ...") 48 | local count = 0 49 | for _, name in ipairs(tests) do 50 | count = count + 1 51 | 52 | -- build progess percentage 53 | local percentage = (count / ntests) * 100 54 | local percentage_str = string.format("%.0f%%", percentage) 55 | local padding = string.rep(" ", 4 - string.len(percentage_str)) 56 | percentage_str = string.format("%s%s", padding, percentage_str) 57 | 58 | -- build test report 59 | local report = string.format("[%s] %s ... ", percentage_str, name) 60 | 61 | -- add leading spaces to allign results 62 | while report:len() < 60 do 63 | report = report.." " 64 | end 65 | 66 | -- safe call test function 67 | local status, err = pcall(_G[name]) 68 | 69 | -- error handling & final report update 70 | if status then 71 | report = string.format("%s [%s]", report, "OK") 72 | else 73 | report = string.format("%s [%s]\n%s", report, "FAILED", err) 74 | end 75 | print(report) 76 | end 77 | end 78 | 79 | function assert_equal(x, y) 80 | --[[ 81 | Tests the equality of two variables. 82 | 83 | :rtype: boolean 84 | ]] 85 | local _x, _y = x, y 86 | 87 | -- transform values for equality 88 | if type(x) == "table" and type(y) == "table" then 89 | _x = json.encode(_x) 90 | _y = json.encode(_y) 91 | end 92 | 93 | if _x == _y then 94 | return true 95 | else 96 | error(string.format("%s\nassertion failed: %s != %s", debug.traceback(), _x, _y)) 97 | end 98 | end 99 | 100 | -- mock funtions 101 | storage = {} 102 | 103 | function mock_error(message) 104 | storage["error_return"] = message 105 | end 106 | 107 | function mock_print(message) 108 | storage["print_return"] = message 109 | end 110 | 111 | function mock_log_level_unset() 112 | return nil 113 | end 114 | 115 | function mock_log_level_error() 116 | return "0" 117 | end 118 | 119 | function mock_log_level_warning() 120 | return "1" 121 | end 122 | 123 | function mock_log_level_info() 124 | return "2" 125 | end 126 | 127 | mock_self_node = {Name="NODE1", Comp=fusion} 128 | 129 | -- tests 130 | function cryptomatte_test__format_log() 131 | local old_self = _G["self"] 132 | _G["self"] = mock_self_node 133 | assert_equal(cryptoutils._format_log("LEVEL", "MESSAGE"), "[Cryptomatte][NODE1][LEVEL] MESSAGE") 134 | _G["self"] = old_self 135 | end 136 | 137 | function cryptomatte_test__get_log_level() 138 | -- store original function pre mock 139 | local old_get_env = _G["os"]["getenv"] 140 | 141 | -- mock log level not set in environment 142 | _G["os"]["getenv"] = mock_log_level_unset 143 | local r1 = cryptoutils._get_log_level() 144 | _G["os"]["getenv"] = old_get_env 145 | assert_equal(r1, 1) 146 | 147 | -- mock log level info set in environment (string -> number cast) 148 | _G["os"]["getenv"] = mock_log_level_info 149 | local r2 = cryptoutils._get_log_level() 150 | _G["os"]["getenv"] = old_get_env 151 | assert_equal(r2, 2) 152 | end 153 | 154 | function cryptomatte_test__string_starts_with() 155 | assert_equal(cryptoutils._string_starts_with("foo_bar", "foo_"), true) 156 | assert_equal(cryptoutils._string_starts_with("foo_bar", "bar"), false) 157 | end 158 | 159 | function cryptomatte_test__string_ends_with() 160 | assert_equal(cryptoutils._string_ends_with("foo_bar", "_bar"), true) 161 | assert_equal(cryptoutils._string_ends_with("foo_bar", "foo"), false) 162 | end 163 | 164 | function cryptomatte_test__string_split() 165 | local result = cryptoutils._string_split("foo, bar,bunny", "([^,]+),?%s*") 166 | assert_equal(#result, 3) 167 | local expected = {"foo", "bar", "bunny"} 168 | for i, v in ipairs(result) do 169 | assert_equal(v, expected[i]) 170 | end 171 | end 172 | 173 | function cryptomatte_test__get_absolute_path() 174 | -- store original pre mock 175 | local old_self = _G["self"] 176 | 177 | -- mock 178 | _G["self"] = mock_self_node 179 | 180 | -- linux absolute path with forward path sep 181 | local r1 = cryptoutils._get_absolute_path("/tmp/test.exr") 182 | 183 | -- windows absolute path with double backward path sep 184 | local r2 = cryptoutils._get_absolute_path("C:\\Temp\\test.exr") 185 | 186 | -- path relative to path map system keyword 187 | local r3 = cryptoutils._get_absolute_path("Temp:/test.exr") 188 | 189 | -- reset mock 190 | _G["self"] = old_self 191 | 192 | assert_equal(r1, "/tmp/test.exr") 193 | assert_equal(r2, "C:/Temp/test.exr") 194 | local pathsep = package.config:sub(1,1) 195 | if pathsep == "/" then 196 | assert_equal(r3, "/tmp/test.exr") 197 | else 198 | assert_equal(r3, "C:\\Temp\\test.exr") 199 | end 200 | end 201 | 202 | function cryptomatte_test__solve_channel_name() 203 | -- r 204 | assert_equal(cryptoutils._solve_channel_name("r"), "r") 205 | assert_equal(cryptoutils._solve_channel_name("R"), "r") 206 | assert_equal(cryptoutils._solve_channel_name("red"), "r") 207 | assert_equal(cryptoutils._solve_channel_name("RED"), "r") 208 | 209 | -- g 210 | assert_equal(cryptoutils._solve_channel_name("g"), "g") 211 | assert_equal(cryptoutils._solve_channel_name("G"), "g") 212 | assert_equal(cryptoutils._solve_channel_name("green"), "g") 213 | assert_equal(cryptoutils._solve_channel_name("GREEN"), "g") 214 | 215 | -- b 216 | assert_equal(cryptoutils._solve_channel_name("b"), "b") 217 | assert_equal(cryptoutils._solve_channel_name("B"), "b") 218 | assert_equal(cryptoutils._solve_channel_name("blue"), "b") 219 | assert_equal(cryptoutils._solve_channel_name("BLUE"), "b") 220 | 221 | -- a 222 | assert_equal(cryptoutils._solve_channel_name("a"), "a") 223 | assert_equal(cryptoutils._solve_channel_name("A"), "a") 224 | assert_equal(cryptoutils._solve_channel_name("alpha"), "a") 225 | assert_equal(cryptoutils._solve_channel_name("ALPHA"), "a") 226 | end 227 | 228 | function cryptomatte_test__get_channel_hierarchy() 229 | local channels = { 230 | {Name="Layer.R"}, -- will be skipped 231 | {Name="Layer.G"}, -- will be skipped 232 | {Name="Layer.B"}, -- will be skipped 233 | {Name="Layer.A"}, -- will be skipped 234 | {Name="Layer00.R"}, 235 | {Name="Layer00.G"}, 236 | {Name="Layer00.B"}, 237 | {Name="Layer00.A"}, 238 | {Name="Layer01.R"}, 239 | {Name="Layer01.G"}, 240 | {Name="Layer01.B"}, 241 | {Name="Layer01.A"} 242 | } 243 | local result = cryptoutils._get_channel_hierarchy("Layer", channels) 244 | local expected = {} 245 | expected["Layer"] = {} 246 | expected["Layer"]["0"] = {r="Layer00.R",g="Layer00.G",b="Layer00.B",a="Layer00.A"} 247 | expected["Layer"]["1"] = {r="Layer01.R",g="Layer01.G",b="Layer01.B",a="Layer01.A"} 248 | assert_equal(result, expected) 249 | end 250 | 251 | function cryptomatte_test__get_absolute_position() 252 | local x, y = cryptoutils._get_absolute_position(10, 10, 0.5, 0.5) 253 | assert_equal(x, 5) 254 | assert_equal(y, 5) 255 | end 256 | 257 | function cryptomatte_test__is_position_in_rect() 258 | -- NOTE: fusion rectangles follow mathematical convention, (origin=left,bottom) 259 | local rect = {left=0, top=10, right=10, bottom=0} 260 | assert_equal(cryptoutils._is_position_in_rect(rect, 5, 5), true) 261 | assert_equal(cryptoutils._is_position_in_rect(rect, 12, 5), false) 262 | assert_equal(cryptoutils._is_position_in_rect(rect, 5, 12), false) 263 | end 264 | 265 | function cryptomatte_test__hex_to_float() 266 | assert_equal(cryptoutils._hex_to_float("3f800000"), 1.0) 267 | assert_equal(cryptoutils._hex_to_float("bf800000"), -1.0) 268 | end 269 | 270 | function cryptomatte_test_log_error() 271 | -- store original pre mock 272 | local old_get_env = _G["os"]["getenv"] 273 | local old_self = _G["self"] 274 | local old_print = _G["print"] 275 | local old_error = _G["error"] 276 | 277 | -- mock 278 | _G["os"]['getenv'] = mock_log_level_error 279 | _G["self"] = mock_self_node 280 | _G["print"] = mock_print 281 | _G["error"] = mock_error 282 | 283 | cryptoutils.log_error("HELP") 284 | 285 | -- reset mock 286 | _G["os"]["getenv"] = old_get_env 287 | _G["self"] = old_self 288 | _G["print"] = old_print 289 | _G["error"] = old_error 290 | 291 | assert_equal(storage["print_return"], "[Cryptomatte][NODE1][ERROR] HELP") 292 | assert_equal(storage["error_return"], "ERROR") 293 | end 294 | 295 | function cryptomatte_test_log_warning() 296 | -- store original pre mock 297 | local old_get_env = _G["os"]["getenv"] 298 | local old_self = _G["self"] 299 | local old_print = _G["print"] 300 | 301 | -- mock with matching log level 302 | _G["os"]['getenv'] = mock_log_level_warning 303 | _G["self"] = mock_self_node 304 | _G["print"] = mock_print 305 | 306 | cryptoutils.log_warning("HELP") 307 | local pr1 = storage["print_return"] 308 | storage["print_return"] = nil -- clear print result for next run 309 | 310 | -- mock with non matching log level 311 | _G["os"]['getenv'] = mock_log_level_error 312 | 313 | cryptoutils.log_warning("HELP") 314 | local pr2 = storage["print_return"] 315 | 316 | -- reset mock 317 | _G["os"]["getenv"] = old_get_env 318 | _G["self"] = old_self 319 | _G["print"] = old_print 320 | 321 | assert_equal(pr1, "[Cryptomatte][NODE1][WARNING] HELP") 322 | assert_equal(pr2, nil) -- never got called 323 | end 324 | 325 | function cryptomatte_test_log_info() 326 | -- store original pre mock 327 | local old_get_env = _G["os"]["getenv"] 328 | local old_self = _G["self"] 329 | local old_print = _G["print"] 330 | 331 | -- mock 332 | _G["os"]['getenv'] = mock_log_level_info 333 | _G["self"] = mock_self_node 334 | _G["print"] = mock_print 335 | 336 | cryptoutils.log_info("HELP") 337 | local pr1 = storage["print_return"] 338 | storage["print_return"] = nil -- clear print result for next run 339 | 340 | -- mock with non matching log level 341 | _G["os"]['getenv'] = mock_log_level_unset 342 | 343 | cryptoutils.log_info("HELP") 344 | local pr2 = storage["print_return"] 345 | 346 | -- reset mock 347 | _G["os"]["getenv"] = old_get_env 348 | _G["self"] = old_self 349 | _G["print"] = old_print 350 | 351 | assert_equal(pr1, "[Cryptomatte][NODE1][INFO] HELP") 352 | assert_equal(pr2, nil) -- never got called 353 | end 354 | 355 | function cryptomatte_test_get_cryptomatte_metadata() 356 | local metadata = {} 357 | metadata["cryptomatte/123456/conversion"] = "uint32_to_float32" 358 | metadata["cryptomatte/123456/hash"] = "MurmurHash3_32" 359 | metadata["cryptomatte/123456/manifest"] = "{\"bunny\": \"3f800000\"}" 360 | metadata["cryptomatte/123456/name"] = "Layer" 361 | metadata["Filename"] = "/tmp/foo" 362 | 363 | -- store original pre mock 364 | local old_self = _G["self"] 365 | 366 | -- mock 367 | _G["self"] = mock_self_node 368 | 369 | local result = cryptoutils.get_cryptomatte_metadata(metadata) 370 | 371 | -- reset mock 372 | _G["self"] = old_self 373 | 374 | local expected = {} 375 | expected["path"] = "/tmp/foo" 376 | expected["layer_count"] = 1 377 | expected["id_to_name"] = {} 378 | expected["id_to_name"]["123456"]="Layer" 379 | expected["name_to_id"] = {} 380 | expected["name_to_id"]["Layer"] = "123456" 381 | expected["index_to_id"] = {} 382 | expected["index_to_id"]["1"] = "123456" 383 | expected["id_to_index"] = {} 384 | expected["id_to_index"]["123456"] = "1" 385 | expected["layers"] = {} 386 | expected["layers"]["123456"] = {} 387 | expected["layers"]["123456"]["conversion"] = "uint32_to_float32" 388 | expected["layers"]["123456"]["hash"] = "MurmurHash3_32" 389 | expected["layers"]["123456"]["manifest"] = "{\"bunny\": \"3f800000\"}" 390 | expected["layers"]["123456"]["name"] = "Layer" 391 | assert_equal(result, expected) 392 | end 393 | 394 | function cryptomatte_test_read_manifest_file() 395 | -- valid manifest file 396 | local tmp_file = os.tmpname() 397 | local fp = io.open(tmp_file, "w") 398 | fp:write("{\"bunny\": \"3f800000\"}") 399 | fp:close() 400 | 401 | local dir, file = string.match(tmp_file, "(.-)([^\\/]-%.?[^%.\\/]*)$") 402 | local result = cryptoutils.read_manifest_file(dir, file) 403 | os.remove(tmp_file) 404 | assert_equal(result, "{\"bunny\": \"3f800000\"}") 405 | 406 | -- invalid manifest file, file does not exist 407 | -- store original pre mock 408 | local old_print = _G["print"] 409 | local old_self = _G["self"] 410 | local old_error = _G["error"] 411 | 412 | -- mock 413 | _G["print"] = mock_print 414 | _G["self"] = mock_self_node 415 | _G["error"] = mock_error 416 | 417 | local result = cryptoutils.read_manifest_file(dir, file) 418 | 419 | -- reset mock 420 | _G["print"] = old_print 421 | _G["self"] = old_self 422 | _G["error"] = old_error 423 | 424 | assert_equal(result, "") 425 | end 426 | 427 | function cryptomatte_test_decode_manifest() 428 | local result = cryptoutils.decode_manifest("{\"bunny\": \"3f800000\"}") 429 | assert_equal(result, {bunny="3f800000"}) 430 | end 431 | 432 | function cryptomatte_test_get_matte_names() 433 | -- valid single name string 434 | assert_equal(cryptoutils.get_matte_names("\"bunny\""), {bunny=true}) 435 | 436 | -- valid multiple names string 437 | assert_equal(cryptoutils.get_matte_names("\"bunny\", \"flower\""), {bunny=true, flower=true}) 438 | 439 | -- valid name string with numbers 440 | assert_equal(cryptoutils.get_matte_names("\"bunny123\""), {bunny123=true}) 441 | 442 | -- valid name string with escaped quotes (single & double) 443 | local result = cryptoutils.get_matte_names("\"bunny'\"\"") 444 | local expected = {} 445 | expected["bunny'\""] = true 446 | assert_equal(result, expected) 447 | 448 | -- valid name string with spaces 449 | result = cryptoutils.get_matte_names("\"b u n n y\"") 450 | expected = {} 451 | expected["b u n n y"] = true 452 | assert_equal(result, expected) 453 | 454 | -- valid name string with special characters 455 | result = cryptoutils.get_matte_names("\"bunny?!\"") 456 | expected = {} 457 | expected["bunny?!"] = true 458 | assert_equal(result, expected) 459 | 460 | -- valid name string with latin encoding characters 461 | result = cryptoutils.get_matte_names("\"itsabunnyé\"") 462 | expected = {} 463 | expected["itsabunnyé"] = true 464 | assert_equal(result, expected) 465 | 466 | -- valid name string in Russian cyrillic 467 | result = cryptoutils.get_matte_names("\"кролик\"") 468 | expected = {} 469 | expected["кролик"] = true 470 | assert_equal(result, expected) 471 | 472 | -- invalid name strings 473 | -- store original pre mock 474 | local old_self = _G["self"] 475 | local old_print = _G["print"] 476 | 477 | -- mock 478 | _G["self"] = mock_self_node 479 | _G["print"] = mock_print 480 | 481 | local no_quotes = cryptoutils.get_matte_names("bunny") 482 | local comma = cryptoutils.get_matte_names("bu,nny") 483 | 484 | -- reset mock 485 | _G["self"] = old_self 486 | _G["print"] = old_print 487 | 488 | assert_equal(no_quotes, {}) 489 | assert_equal(comma, {}) 490 | end 491 | 492 | run_tests() 493 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, 2015, 2016 Psyop Media Company, LLC 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | -------------------------------------------------------------------------------- /nuke/Cryptomatte.gizmo: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2014, 2015, 2016, 2017 Psyop Media Company, LLC 3 | # See license.txt 4 | # 5 | 6 | version 7.0 v1 7 | #! C:/Temp/psyop_cache/apps/nuke/win64/10.0v6/nuke-10.0.6.dll -nx 8 | Gizmo { 9 | addUserKnob {20 cryptomatte l "Psyop Cryptomatte"} 10 | 11 | addUserKnob {36 pickerAdd l "Picker Add" t "Key objects to add to the Matte List here. " +STARTLINE +DO_NOT_WRITE} 12 | pickerAdd {0 0 0 0 0 0 0 0} 13 | addUserKnob {26 pickerAddLabel l "Picker Add" -STARTLINE T " "} 14 | 15 | addUserKnob {36 pickerRemove l "Picker Remove" t "Key objects to remove from the Matte List here. " +STARTLINE +DO_NOT_WRITE} 16 | pickerRemove {0 0 0 0 0 0 0 0} 17 | addUserKnob {26 pickerRemoveLabel l "Picker Remove" -STARTLINE T " "} 18 | 19 | addUserKnob {26 ""} 20 | 21 | addUserKnob {6 previewEnabled l "Preview" +STARTLINE} 22 | previewEnabled true 23 | addUserKnob {4 previewMode l "" t "Choose how Cryptomatte will visualize keyable regions." -STARTLINE M {Colors Edges None ""}} 24 | 25 | addUserKnob {6 matteOnly l "Matte Only" t "Extracted matte is copied to RGB channels as well. This disables keying. " +STARTLINE} 26 | addUserKnob {6 singleSelection l "Single Selection" t "Picker only selects matte at a time, rather than selecting a list. " -STARTLINE} 27 | addUserKnob {6 RemoveChannels l "Remove Channels" t "Removes all non-RGBA channels for the output. This will leave the downstream cleaner. " -STARTLINE} 28 | 29 | addUserKnob {26 ""} 30 | 31 | addUserKnob {41 matteOutput l "Matte Output" t "Set the channel(s) the matte will write to in \"Matte Only\" mode. For example, you can use this to store the matte in a custom channel called \"Matte\" and use it for color-correction downstream." T ShuffleCopy_embedMask.out} 32 | addUserKnob {6 unpremultiply l Unpremultiply -STARTLINE} 33 | 34 | addUserKnob {1 matteList l "Matte List" t "The list of names the mattes are built from. Color picking values with the color fields above works by populating this field. "} 35 | addUserKnob {22 clear l Clear t "Clears the selection in this Gizmo" T "try: \n import cryptomatte_utilities as cu\n cu.clear_cryptomatte_gizmo(nuke.thisNode())\nexcept Exception as err:\n import traceback\n nuke.message('''Unable to run Cryptomatte Gizmo update script. This script is necessary for the Cryptomatte system to work properly. Please check with Pipeline that the Cryptomatte python plugin is available on this project. \n\nError Traceback (send this to Pipeline): \n\n%s''' % traceback.format_exc())" +STARTLINE} 36 | addUserKnob {22 forceUpdate l "Force Update" t "Updates the Gizmo based on which channels are available in the input. \n\nThis happens automatically when input changes, when loading the nuke script, or when a new gizmo is created. This is how it deals with differently named channels in the different Cryptomatte layers, and different depths that it's possible to render at. " -STARTLINE T "try: \n import cryptomatte_utilities as cu\nexcept Exception as err:\n import traceback\n nuke.message('''Unable to run Cryptomatte Gizmo update script. This script is necessary for the Cryptomatte system to work properly. \n\nError Traceback: \n\n%s''' % traceback.format_exc())"} 37 | addUserKnob {6 stopAutoUpdate l "Stop Auto Update" t "Stops the automatic update of this copy of the Gizmo." -STARTLINE} 38 | addUserKnob {6 useWildcards l "Use Wildcards" t "Expands wildcard entries in the Matte List knob." -STARTLINE} 39 | useWildcards false 40 | 41 | addUserKnob {26 ""} 42 | addUserKnob {4 cryptoLayerChoice l "Layer Selection" t "Choose which Cryptomatte layer to key." M {" " ""} +DO_NOT_WRITE} 43 | addUserKnob {1 cryptoLayer l INVISIBLE t "Internal storage of selection between multiple cryptomattes." -STARTLINE +INVISIBLE} 44 | addUserKnob {1 metadataCache l INVISIBLE t "Internal storage of selection between multiple cryptomatte metadata keys." -STARTLINE +INVISIBLE +DO_NOT_WRITE} 45 | addUserKnob {6 cryptoLayerLock l "Lock Layer Selection" t "Stops the automatic update of the layer selection." -STARTLINE} 46 | addUserKnob {41 expression l "Keyer Expression" t "The built expression. This knob is set automatically and is only left exposed as information for the user. " T Expression_key.expr0} 47 | 48 | addUserKnob {20 Advanced} 49 | 50 | addUserKnob {36 ColorKey l "" t "Key an object here to check its name. It will not effect your mattes. " -STARTLINE +HIDDEN} 51 | ColorKey {0 0 0 0 0 0 0 0} 52 | addUserKnob {6 ColorKey_panelDropped l "panel dropped state" -STARTLINE +HIDDEN} 53 | addUserKnob {26 ColorKeyLabel l "Name Checker" -STARTLINE T " " +HIDDEN} 54 | addUserKnob {1 keyedName l "Keyed Name" t "This field is for information only." +HIDDEN} 55 | addUserKnob {26 "" +HIDDEN} 56 | 57 | addUserKnob {26 cryptomatteVersion l "Cryptomatte Version" T 1.4.0 +DO_NOT_WRITE} 58 | addUserKnob {22 troubleshoot l "Troubleshoot" +STARTLINE 59 | t "'Force Update All' will run 'Force Update' on all Cryptomatte gizmos in the Nuke script. 'Force Update' orders the gizmos to re-configure themselves based on available metadata, Cryptomatte depth, and the matte list. This updates all aspects of thier operation, from the preview modes to the extraction expression. Usually these updates occur automatically on certain actions, such as 'keying' the image, changing layer selection, or reconnecting images. However if changes occur upstream of the gizmos that require manually invoked updates, 'Force Update' and 'Force Update All' may be used. " 60 | T " 61 | try: 62 | import cryptomatte_utilities as cu 63 | cu.troubleshoot_gizmo(nuke.thisNode()) 64 | except ImportError as err: 65 | nuke.message('''Could not import cryptomatte_utilities.py, the python files are not available. \n\nError Traceback: \n\n%s''' % traceback.format_exc()) 66 | except Exception as err: 67 | import traceback 68 | nuke.message('''Something went wrong. If you would like to report this, please the text in this window: \n\nError Traceback: \n\n%s''' % traceback.format_exc()) 69 | "} 70 | addUserKnob {26 ""} 71 | 72 | addUserKnob {22 decryptomatte l "Decryptomatte (Replace with Expression)" +STARTLINE 73 | t "Replaces this gizmo with an expression node that does the extraction. This will replicate the matte extraction, but not the preview modes. " 74 | T " 75 | try: 76 | import cryptomatte_utilities as cu 77 | cu.decryptomatte_button(nuke.thisNode()) 78 | except Exception as err: 79 | import traceback 80 | nuke.message('''Unable to run a Cryptomatte script. This script is necessary for the Cryptomatte system to work properly. \n\nError Traceback: \n\n%s''' % traceback.format_exc()) 81 | "} 82 | 83 | addUserKnob {22 unloadManifest l "Unload Manifest (Extract all Mattes)" +STARTLINE 84 | t "Unload Manifest will create a separate gizmo for every named matte in the Cryptomatte manifest. This can potentially be thousands of nodes, though if the number is high a warning will be displayed before creating them. " 85 | T " 86 | try: 87 | import cryptomatte_utilities as cu 88 | cu.unload_manifest(nuke.thisNode()) 89 | except Exception as err: 90 | import traceback 91 | nuke.message('''Unable to run a Cryptomatte script. This script is necessary for the Cryptomatte system to work properly. \n\nError Traceback: \n\n%s''' % traceback.format_exc()) 92 | "} 93 | 94 | addUserKnob {22 forceUpdateAll l "Force Update All (Update all Gizmos)" +STARTLINE 95 | t "'Force Update All' will run 'Force Update' on all Cryptomatte gizmos in the Nuke script. 'Force Update' orders the gizmos to re-configure themselves based on available metadata, Cryptomatte depth, and the matte list. This updates all aspects of thier operation, from the preview modes to the extraction expression. Usually these updates occur automatically on certain actions, such as 'keying' the image, changing layer selection, or reconnecting images. However if changes occur upstream of the gizmos that require manually invoked updates, 'Force Update' and 'Force Update All' may be used. " 96 | T " 97 | try: 98 | import cryptomatte_utilities as cu 99 | cu.update_all_cryptomatte_gizmos() 100 | except Exception as err: 101 | import traceback 102 | nuke.message('''Unable to run Cryptomatte Gizmo update script. This script is necessary for the Cryptomatte system to work properly. \n\nError Traceback: \n\n%s''' % traceback.format_exc()) 103 | "} 104 | addUserKnob {3 frame l INVISIBLE +INVISIBLE} 105 | addUserKnob {1 manifestKey l INVISIBLE +INVISIBLE} 106 | 107 | addUserKnob {41 previewExpression0 l INVISIBLE +INVISIBLE T Expression_preview.expr0} 108 | addUserKnob {41 previewExpression1 l INVISIBLE +INVISIBLE T Expression_preview.expr1} 109 | addUserKnob {41 previewExpression2 l INVISIBLE +INVISIBLE T Expression_preview.expr2} 110 | addUserKnob {41 previewExpression3 l INVISIBLE +INVISIBLE T Expression_preview.expr3} 111 | 112 | addUserKnob {11 previewChannel +HIDDEN} 113 | previewChannel none 114 | addUserKnob {11 in00 +HIDDEN} 115 | addUserKnob {11 in01 +HIDDEN} 116 | addUserKnob {11 in02 +HIDDEN} 117 | addUserKnob {11 in03 +HIDDEN} 118 | addUserKnob {11 in04 +HIDDEN} 119 | addUserKnob {11 in05 +HIDDEN} 120 | addUserKnob {11 in06 +HIDDEN} 121 | addUserKnob {11 in07 +HIDDEN} 122 | addUserKnob {11 in08 +HIDDEN} 123 | addUserKnob {11 in09 +HIDDEN} 124 | addUserKnob {11 in10 +HIDDEN} 125 | addUserKnob {11 in11 +HIDDEN} 126 | addUserKnob {26 "" +HIDDEN} 127 | 128 | } 129 | Input { 130 | inputs 0 131 | name Input1 132 | xpos -1361 133 | ypos -499 134 | } 135 | Dot { 136 | name Dot4 137 | xpos -1327 138 | ypos -439 139 | } 140 | set N13c4bc00 [stack 0] 141 | Dot { 142 | name Dot15 143 | xpos -1093 144 | ypos -439 145 | } 146 | set N13c4b800 [stack 0] 147 | Dot { 148 | name Dot1 149 | xpos -960 150 | ypos -439 151 | } 152 | Shuffle { 153 | red black 154 | green black 155 | blue black 156 | name Shuffle_blackRGB 157 | xpos -994 158 | ypos -398 159 | } 160 | Expression { 161 | channel1 none 162 | channel2 none 163 | channel3 none 164 | name Expression_key 165 | xpos -994 166 | ypos -372 167 | } 168 | Unpremult { 169 | channels {rgba.red -rgba.green -rgba.blue none} 170 | name Unpremult_matte 171 | xpos -994 172 | ypos -340 173 | disable {{!parent.unpremultiply}} 174 | } 175 | set N13c4a400 [stack 0] 176 | Dot { 177 | name Dot11 178 | xpos -960 179 | ypos -142 180 | } 181 | set N13c4a000 [stack 0] 182 | Dot { 183 | name Dot2 184 | xpos -960 185 | ypos -65 186 | } 187 | push $N13c4a000 188 | push $N13c4bc00 189 | Dot { 190 | name Dot8 191 | xpos -1327 192 | ypos -395 193 | } 194 | set N12b01800 [stack 0] 195 | Dot { 196 | name Dot9 197 | xpos -1227 198 | ypos -395 199 | } 200 | Remove { 201 | operation keep 202 | channels rgba 203 | name Remove_channels 204 | xpos -1261 205 | ypos -360 206 | } 207 | push $N12b01800 208 | Switch { 209 | inputs 2 210 | which {{parent.RemoveChannels}} 211 | name Switch_removeChannels 212 | xpos -1361 213 | ypos -312 214 | } 215 | Dot { 216 | name Dot17 217 | xpos -1327 218 | ypos -235 219 | } 220 | set N12b00c00 [stack 0] 221 | Dot { 222 | name Dot10 223 | xpos -1327 224 | ypos -193 225 | } 226 | set N12b00800 [stack 0] 227 | ShuffleCopy { 228 | inputs 2 229 | red red 230 | green red 231 | blue red 232 | alpha red 233 | name ShuffleCopy_matteOnly 234 | xpos -1361 235 | ypos -146 236 | } 237 | Dot { 238 | name Dot16 239 | xpos -1327 240 | ypos -91 241 | } 242 | push $N13c4a400 243 | push $N13c4b800 244 | Shuffle { 245 | alpha black 246 | name Shuffle_blackAlpha 247 | xpos -1127 248 | ypos -394 249 | } 250 | Expression { 251 | name Expression_preview 252 | xpos -1127 253 | ypos -368 254 | } 255 | Grade { 256 | inputs 1+1 257 | channels {rgba.red rgba.green -rgba.blue none} 258 | multiply 0 259 | add 1 260 | black_clamp false 261 | maskChannelMask rgba.red 262 | name Grade_selection 263 | xpos -1127 264 | ypos -340 265 | } 266 | Grade { 267 | channels {-rgba.red rgba.green rgba.blue none} 268 | multiply 0 269 | add 1 270 | black_clamp false 271 | maskChannelInput rgba.alpha 272 | name Grade_highlight 273 | xpos -1127 274 | ypos -302 275 | } 276 | push $N12b00c00 277 | ShuffleCopy { 278 | inputs 2 279 | red red 280 | green green 281 | blue blue 282 | alpha alpha2 283 | name ShuffleCopy_restoreAlpha 284 | xpos -1127 285 | ypos -239 286 | } 287 | push $N12b00800 288 | Switch { 289 | inputs 2 290 | which {{parent.previewEnabled}} 291 | name Switch_preview 292 | xpos -1127 293 | ypos -197 294 | } 295 | Switch { 296 | inputs 2 297 | which {{parent.matteOnly}} 298 | name Switch_matteOnly 299 | xpos -1127 300 | ypos -95 301 | } 302 | ShuffleCopy { 303 | inputs 2 304 | red red 305 | green red 306 | blue red 307 | alpha red 308 | out alpha 309 | name ShuffleCopy_embedMask 310 | xpos -1127 311 | ypos -69 312 | } 313 | Output { 314 | name Output 315 | xpos -1127 316 | ypos -21 317 | } 318 | end_group 319 | -------------------------------------------------------------------------------- /nuke/Encryptomatte.gizmo: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2014, 2015, 2016, 2017 Psyop Media Company, LLC 3 | # See license.txt 4 | # 5 | version 7.0 v1 6 | Gizmo { 7 | inputs 2 8 | help "Composites new mattes to be added to a set of Cryptomatte-encoded mattes." 9 | addUserKnob {20 Encryptomatte} 10 | addUserKnob {1 matteName l "Matte Name" t "Enter the descriptive name of your matte. If the name is empty, the node does nothing."} 11 | addUserKnob {4 mergeOperation l "Merge Operation" t "Choose a compositing mode to control how your new matte will be merged with the existing set. \n\nThe new matte will be treated like the A input, while the existing mattes will be treated like the B input. The combined result of A and B will be placed over the background matte." M {over under "" ""}} 12 | addUserKnob {7 id l INVISIBLE +INVISIBLE} 13 | addUserKnob {1 idHex l INVISIBLE +INVISIBLE} 14 | addUserKnob {18 previewColor l INVISIBLE +INVISIBLE} 15 | previewColor {0 0 0} 16 | addUserKnob {6 previewColor_panelDropped l "panel dropped state" -STARTLINE +HIDDEN} 17 | addUserKnob {1 cryptoLayer l "Layer Selection" t "If there are multiple cryptomattes, this is how you select the layer."} 18 | addUserKnob {6 cryptoLayerLock l "Lock Layer Selection" t "Stops the automatic update of the layer selection." -STARTLINE} 19 | addUserKnob {1 metadataCache l INVISIBLE t "Internal storage of selection between multiple cryptomatte metadata keys." -STARTLINE +INVISIBLE +DO_NOT_WRITE} 20 | addUserKnob {22 forceUpdate l "Force Update" t "Updates the Gizmo based on which channels are available in the input. \n\nThis happens automatically when input changes, when loading the nuke script, or when a new gizmo is created. This is how it deals with differently named channels in the different Cryptomatte layers, and different depths that it's possible to render at. " 21 | T " 22 | try: 23 | import cryptomatte_utilities as cu 24 | cu.update_encryptomatte_gizmo(nuke.thisNode(), True) 25 | except Exception as err: 26 | import traceback 27 | nuke.message('''Unable to run Encryptomatte Gizmo update script. This script is necessary for the Cryptomatte system to work properly. Please check that the Cryptomatte python plugin is loaded. \n\nError Traceback: \n\n%s''' % traceback.format_exc()) 28 | " +STARTLINE} 29 | addUserKnob {6 stopAutoUpdate l "Stop Auto Update" t "Stops the automatic update of this copy of the Gizmo." -STARTLINE} 30 | addUserKnob {41 alphaExpression l INVISIBLE +INVISIBLE T Expression_alpha.expr3} 31 | addUserKnob {1 manifestKey l INVISIBLE +INVISIBLE} 32 | addUserKnob {3 cryptoLayers l Layers t "Number of cryptomatte layers to use."} 33 | cryptoLayers 3 34 | 35 | addUserKnob {3 inputCryptoLayers l INVISIBLE +INVISIBLE} 36 | addUserKnob {6 setupLayers l "Setup Layers" t "Set up the Cryptomatte layers if they do not already exist, and remove any extras." -STARTLINE} 37 | addUserKnob {6 newLayer l INVISIBLE +INVISIBLE +STARTLINE} 38 | addUserKnob {41 previewChannel l INVISIBLE +INVISIBLE T Shuffle_previewChannel.in} 39 | addUserKnob {41 in00 l INVISIBLE +INVISIBLE T Shuffle_00.in} 40 | addUserKnob {41 in01 l INVISIBLE +INVISIBLE T Shuffle_01.in} 41 | addUserKnob {41 in02 l INVISIBLE +INVISIBLE T Shuffle_02.in} 42 | addUserKnob {41 in03 l INVISIBLE +INVISIBLE T Shuffle_03.in} 43 | addUserKnob {41 in04 l INVISIBLE +INVISIBLE T Shuffle_04.in} 44 | addUserKnob {41 in05 l INVISIBLE +INVISIBLE T Shuffle_05.in} 45 | addUserKnob {41 in06 l INVISIBLE +INVISIBLE T Shuffle_06.in} 46 | addUserKnob {41 in07 l INVISIBLE +INVISIBLE T Shuffle_07.in} 47 | addUserKnob {41 in08 l INVISIBLE +INVISIBLE T Shuffle_08.in} 48 | addUserKnob {41 in09 l INVISIBLE +INVISIBLE T Shuffle_09.in} 49 | addUserKnob {41 in10 l INVISIBLE +INVISIBLE T Shuffle_10.in} 50 | addUserKnob {41 in11 l INVISIBLE +INVISIBLE T Shuffle_11.in} 51 | addUserKnob {41 removePreviewChannel l INVISIBLE +INVISIBLE T Remove_previewChannel.channels} 52 | addUserKnob {41 remove00 l INVISIBLE +INVISIBLE T Remove_00_03.channels} 53 | addUserKnob {41 remove01 l INVISIBLE +INVISIBLE T Remove_00_03.channels2} 54 | addUserKnob {41 remove02 l INVISIBLE +INVISIBLE T Remove_00_03.channels3} 55 | addUserKnob {41 remove03 l INVISIBLE +INVISIBLE T Remove_00_03.channels4} 56 | addUserKnob {41 remove04 l INVISIBLE +INVISIBLE T Remove_04_07.channels} 57 | addUserKnob {41 remove05 l INVISIBLE +INVISIBLE T Remove_04_07.channels2} 58 | addUserKnob {41 remove06 l INVISIBLE +INVISIBLE T Remove_04_07.channels3} 59 | addUserKnob {41 remove07 l INVISIBLE +INVISIBLE T Remove_04_07.channels4} 60 | addUserKnob {41 addPreviewChannel l INVISIBLE +INVISIBLE T AddChannels_previewChannel.channels} 61 | addUserKnob {41 add00 l INVISIBLE +INVISIBLE T AddChannels_00_03.channels} 62 | addUserKnob {41 add01 l INVISIBLE +INVISIBLE T AddChannels_00_03.channels2} 63 | addUserKnob {41 add02 l INVISIBLE +INVISIBLE T AddChannels_00_03.channels3} 64 | addUserKnob {41 add03 l INVISIBLE +INVISIBLE T AddChannels_00_03.channels4} 65 | addUserKnob {41 add04 l INVISIBLE +INVISIBLE T AddChannels_04_07.channels} 66 | addUserKnob {41 add05 l INVISIBLE +INVISIBLE T AddChannels_04_07.channels2} 67 | addUserKnob {41 add06 l INVISIBLE +INVISIBLE T AddChannels_04_07.channels3} 68 | addUserKnob {41 add07 l INVISIBLE +INVISIBLE T AddChannels_04_07.channels4} 69 | 70 | addUserKnob {20 Advanced} 71 | 72 | addUserKnob {26 cryptomatteVersion l "Cryptomatte Version" T 1.4.0} 73 | } 74 | Input { 75 | inputs 0 76 | name Input1 77 | xpos -1305 78 | ypos -1346 79 | } 80 | Dot { 81 | name Dot47 82 | xpos -1271 83 | ypos -1287 84 | } 85 | set N6fbc7400 [stack 0] 86 | Dot { 87 | name Dot48 88 | xpos -1491 89 | ypos -1287 90 | } 91 | Remove { 92 | channels none 93 | name Remove_previewChannel 94 | xpos -1525 95 | ypos -1257 96 | } 97 | Remove { 98 | channels none 99 | name Remove_00_03 100 | xpos -1525 101 | ypos -1219 102 | } 103 | Remove { 104 | channels none 105 | name Remove_04_07 106 | xpos -1525 107 | ypos -1181 108 | } 109 | AddChannels { 110 | name AddChannels_previewChannel 111 | xpos -1525 112 | ypos -1120 113 | } 114 | AddChannels { 115 | name AddChannels_00_03 116 | xpos -1525 117 | ypos -1082 118 | } 119 | AddChannels { 120 | name AddChannels_04_07 121 | xpos -1525 122 | ypos -1044 123 | } 124 | set N9419b800 [stack 0] 125 | Dot { 126 | name Dot52 127 | xpos -1379 128 | ypos -1034 129 | } 130 | Shuffle { 131 | in {{{parent.Shuffle_00.in}}} 132 | red black 133 | green white 134 | blue black 135 | alpha black 136 | out {{{parent.Shuffle_00.in}}} 137 | name Shuffle_SetBG 138 | xpos -1413 139 | ypos -989 140 | } 141 | ModifyMetaData { 142 | metadata { 143 | {set "\[value parent.manifestKey]manifest" "\{\}"} 144 | {set "\[value parent.manifestKey]conversion" uint32_to_float32} 145 | {set "\[value parent.manifestKey]hash" MurmurHash3_32} 146 | {set "\[value parent.manifestKey]name" "\[value parent.cryptoLayer]"} 147 | } 148 | name ModifyMetaData1 149 | xpos -1413 150 | ypos -940 151 | } 152 | push $N9419b800 153 | Dot { 154 | name Dot50 155 | xpos -1491 156 | ypos -899 157 | } 158 | Switch { 159 | inputs 2 160 | which {{parent.newLayer}} 161 | name Switch_ResetMetadata 162 | xpos -1413 163 | ypos -903 164 | } 165 | Dot { 166 | name Dot49 167 | xpos -1379 168 | ypos -847 169 | } 170 | push $N6fbc7400 171 | Switch { 172 | inputs 2 173 | which {{parent.setupLayers}} 174 | name Switch_SetupLayers 175 | xpos -1305 176 | ypos -851 177 | } 178 | Dot { 179 | name Dot15 180 | xpos -1271 181 | ypos -693 182 | } 183 | set N9419a000 [stack 0] 184 | Dot { 185 | name Dot14 186 | xpos -1271 187 | ypos 1856 188 | } 189 | set N9949c00 [stack 0] 190 | Dot { 191 | name Dot46 192 | xpos -1271 193 | ypos 1954 194 | } 195 | Input { 196 | inputs 0 197 | name Matte 198 | xpos 455 199 | ypos -820 200 | number 1 201 | } 202 | Dot { 203 | name Dot53 204 | xpos 489 205 | ypos -674 206 | } 207 | set N9949000 [stack 0] 208 | Dot { 209 | name Dot51 210 | xpos 599 211 | ypos -674 212 | } 213 | Shuffle { 214 | red black 215 | green black 216 | blue black 217 | name Shuffle6 218 | xpos 565 219 | ypos 1349 220 | } 221 | Add { 222 | channels rgba 223 | value {{parent.previewColor.r} {parent.previewColor.g} {parent.previewColor.b} 0} 224 | name Add1 225 | xpos 565 226 | ypos 1435 227 | } 228 | Premult { 229 | name Premult1 230 | xpos 565 231 | ypos 1509 232 | } 233 | push $N9419a000 234 | Dot { 235 | name Dot1 236 | xpos -1074 237 | ypos -693 238 | } 239 | set N9971c00 [stack 0] 240 | Dot { 241 | name Dot11 242 | xpos -897 243 | ypos -693 244 | } 245 | set N9971800 [stack 0] 246 | Dot { 247 | name Dot16 248 | xpos 119 249 | ypos -700 250 | } 251 | set N9971400 [stack 0] 252 | Dot { 253 | name Dot41 254 | xpos 261 255 | ypos -700 256 | } 257 | Expression { 258 | channel0 none 259 | channel1 none 260 | channel2 none 261 | channel3 alpha 262 | name Expression_alpha 263 | xpos 227 264 | ypos -682 265 | } 266 | Invert { 267 | channels alpha 268 | name Invert1 269 | xpos 227 270 | ypos -656 271 | } 272 | Dot { 273 | name Dot4 274 | xpos 261 275 | ypos -597 276 | } 277 | set N9970000 [stack 0] 278 | Dot { 279 | name Dot6 280 | xpos 261 281 | ypos 1236 282 | } 283 | set N9991c00 [stack 0] 284 | Dot { 285 | name Dot44 286 | xpos 360 287 | ypos 1236 288 | } 289 | Shuffle { 290 | in none 291 | in2 rgba 292 | alpha alpha2 293 | name Shuffle_previewChannel 294 | xpos 326 295 | ypos 1347 296 | } 297 | Merge2 { 298 | inputs 2 299 | operation {{"\[python nuke.thisKnob().values().index(nuke.thisParent().knob('mergeOperation').value())]"}} 300 | name Merge_AB 301 | xpos 326 302 | ypos 1509 303 | addUserKnob {20 User} 304 | addUserKnob {22 createExpression l "Create Expression" T "nuke.thisNode().knob('operation').setExpression(\"\[python nuke.thisKnob().values().index(nuke.thisParent().knob('mergeOperation').value())]\")" +STARTLINE} 305 | } 306 | set C9991000 [stack 0] 307 | push $N9991c00 308 | Shuffle { 309 | red black 310 | green black 311 | blue black 312 | alpha white 313 | name Shuffle1 314 | xpos 227 315 | ypos 1345 316 | } 317 | Merge2 { 318 | inputs 2 319 | name Merge_Background 320 | xpos 227 321 | ypos 1570 322 | } 323 | set C9990800 [stack 0] 324 | push $N9949000 325 | Shuffle { 326 | red black 327 | green black 328 | blue white 329 | name Shuffle_A 330 | xpos 455 331 | ypos -601 332 | } 333 | Premult { 334 | name Premult3 335 | xpos 455 336 | ypos -548 337 | } 338 | push $N9970000 339 | Shuffle { 340 | red white 341 | green black 342 | blue black 343 | name Shuffle_B 344 | xpos 339 345 | ypos -601 346 | } 347 | Premult { 348 | name Premult2 349 | xpos 339 350 | ypos -549 351 | } 352 | clone $C9991000 { 353 | inputs 2 354 | xpos 455 355 | ypos -478 356 | selected false 357 | } 358 | push $N9971400 359 | Dot { 360 | name Dot42 361 | xpos 119 362 | ypos -507 363 | } 364 | Dot { 365 | name Dot43 366 | xpos 376 367 | ypos -507 368 | } 369 | Shuffle { 370 | red black 371 | green white 372 | blue black 373 | alpha white 374 | name Shuffle_Background 375 | xpos 342 376 | ypos -460 377 | } 378 | clone $C9990800 { 379 | inputs 2 380 | xpos 455 381 | ypos -392 382 | selected true 383 | } 384 | set N99c6400 [stack 0] 385 | Shuffle { 386 | red black 387 | name Shuffle_Under 388 | xpos 511 389 | ypos -283 390 | } 391 | push $N99c6400 392 | Shuffle { 393 | red blue 394 | name Shuffle_Over 395 | xpos 408 396 | ypos -284 397 | } 398 | Switch { 399 | inputs 2 400 | which {{parent.mergeOperation}} 401 | name Switch_MatteExisting 402 | xpos 456 403 | ypos -189 404 | } 405 | Dot { 406 | name Dot19 407 | xpos -3 408 | ypos -185 409 | } 410 | set N99fb800 [stack 0] 411 | Dot { 412 | name Dot29 413 | xpos -73 414 | ypos -185 415 | } 416 | set N99fb400 [stack 0] 417 | Dot { 418 | name Dot28 419 | xpos -183 420 | ypos -185 421 | } 422 | set N99fb000 [stack 0] 423 | push $N9971800 424 | Dot { 425 | name Dot10 426 | xpos -897 427 | ypos -293 428 | } 429 | set N99fac00 [stack 0] 430 | Dot { 431 | name Dot12 432 | xpos -787 433 | ypos -293 434 | } 435 | set N99fa800 [stack 0] 436 | Dot { 437 | name Dot13 438 | xpos -677 439 | ypos -293 440 | } 441 | set N99fa400 [stack 0] 442 | Dot { 443 | name Dot17 444 | xpos -567 445 | ypos -293 446 | } 447 | set N99fa000 [stack 0] 448 | Dot { 449 | name Dot18 450 | xpos -457 451 | ypos -293 452 | } 453 | set N9a1fc00 [stack 0] 454 | Dot { 455 | name Dot22 456 | xpos -347 457 | ypos -293 458 | } 459 | set N9a1f800 [stack 0] 460 | Dot { 461 | name Dot23 462 | xpos -237 463 | ypos -293 464 | } 465 | set N9a1f400 [stack 0] 466 | Shuffle { 467 | in none 468 | name Shuffle_06 469 | xpos -271 470 | ypos -263 471 | } 472 | Multiply { 473 | inputs 1+1 474 | channels {-rgba.red rgba.green -rgba.blue rgba.alpha} 475 | value {1 0 1 0} 476 | maskChannelMask rgba.red 477 | name Multiply1 478 | xpos -271 479 | ypos -127 480 | } 481 | set C9a1ec00 [stack 0] 482 | Dot { 483 | name Dot34 484 | xpos -237 485 | ypos 6 486 | } 487 | set N9a1e800 [stack 0] 488 | push $N99fb400 489 | push $N9a1f400 490 | Dot { 491 | name Dot24 492 | xpos -127 493 | ypos -293 494 | } 495 | Shuffle { 496 | in none 497 | name Shuffle_07 498 | xpos -161 499 | ypos -264 500 | } 501 | clone $C9a1ec00 { 502 | inputs 1+1 503 | xpos -161 504 | ypos -126 505 | selected false 506 | } 507 | push $N99fb800 508 | Shuffle { 509 | red black 510 | blue black 511 | alpha blue 512 | name Shuffle_ForegroundBackground 513 | xpos -37 514 | ypos -126 515 | } 516 | Add { 517 | channels {-rgba.red -rgba.green rgba.blue none} 518 | value {0 0 {parent.id} 0} 519 | name Add2 520 | xpos -37 521 | ypos -78 522 | } 523 | set Na1fb400 [stack 0] 524 | MergeExpression { 525 | inputs 2 526 | temp_name0 orderAr 527 | temp_expr0 "(A.green * (A.red != 0.0) >= A.alpha * (A.blue != 0.0)) + (A.green * (A.red != 0.0) >= B.green) + (A.green * (A.red != 0.0) >= B.alpha)" 528 | temp_name1 orderAb 529 | temp_expr1 "(A.alpha * (A.blue != 0.0) > A.green * (A.red != 0.0)) + (A.alpha >= B.green) + (A.alpha * (A.blue != 0.0) >= B.alpha)" 530 | temp_name2 orderBr 531 | temp_expr2 "(B.green > A.green * (A.red != 0.0)) + (B.green > A.alpha * (A.blue != 0.0)) + (B.green >= B.alpha)" 532 | temp_name3 orderBb 533 | temp_expr3 "(B.alpha > A.green * (A.red != 0.0)) + (B.alpha > B.green) + (B.alpha > A.alpha * (A.blue != 0.0))" 534 | expr0 "(orderAr == 3.0) * A.red + (orderBr == 3.0) * B.red + (orderAb == 3.0) * A.blue + (orderBb == 3.0) * B.blue" 535 | expr1 "(orderAr == 3.0) * A.green * (A.red != 0.0) + (orderBr == 3.0) * B.green + (orderAb == 3.0) * A.alpha * (A.blue != 0.0) + (orderBb == 3.0) * B.alpha" 536 | expr2 "(orderAr == 2.0) * A.red + (orderBr == 2.0) * B.red + (orderAb == 2.0) * A.blue + (orderBb == 2.0) * B.blue" 537 | channel3 alpha 538 | expr3 "(orderAr == 2.0) * A.green * (A.red != 0.0) + (orderBr == 2.0) * B.green + (orderAb == 2.0) * A.alpha * (A.blue != 0.0) + (orderBb == 2.0) * B.alpha" 539 | name MergeExpression7 540 | xpos -161 541 | ypos 4 542 | } 543 | set Ca1fb000 [stack 0] 544 | push $Na1fb400 545 | Dot { 546 | name Dot35 547 | xpos -3 548 | ypos 77 549 | } 550 | Switch { 551 | inputs 2 552 | which {{"parent.cryptoLayers > 7"}} 553 | name Switch_07 554 | xpos -161 555 | ypos 73 556 | disable true 557 | } 558 | set C6f60d200 [stack 0] 559 | set N6f60d200 [stack 0] 560 | MergeExpression { 561 | inputs 2 562 | temp_name0 orderAr 563 | temp_expr0 "(A.green * (A.red != 0.0) >= A.alpha * (A.blue != 0.0)) + (A.green * (A.red != 0.0) >= B.green) + (A.green * (A.red != 0.0) >= B.alpha)" 564 | temp_name1 orderAb 565 | temp_expr1 "(A.alpha * (A.blue != 0.0) > A.green * (A.red != 0.0)) + (A.alpha >= B.green) + (A.alpha * (A.blue != 0.0) >= B.alpha)" 566 | temp_name2 orderBr 567 | temp_expr2 "(B.green > A.green * (A.red != 0.0)) + (B.green > A.alpha * (A.blue != 0.0)) + (B.green >= B.alpha)" 568 | temp_name3 orderBb 569 | temp_expr3 "(B.alpha > A.green * (A.red != 0.0)) + (B.alpha > B.green) + (B.alpha > A.alpha * (A.blue != 0.0))" 570 | expr0 "(orderAr == 1.0) * A.red + (orderBr == 1.0) * B.red + (orderAb == 1.0) * A.blue + (orderBb == 1.0) * B.blue" 571 | expr1 "(orderAr == 1.0) * A.green * (A.red != 0.0) + (orderBr == 1.0) * B.green + (orderAb == 1.0) * A.alpha * (A.blue != 0.0) + (orderBb == 1.0) * B.alpha" 572 | expr2 "(orderAr == 0.0) * A.red + (orderBr == 0.0) * B.red + (orderAb == 0.0) * A.blue + (orderBb == 0.0) * B.blue" 573 | channel3 alpha 574 | expr3 "(orderAr == 0.0) * A.green * (A.red != 0.0) + (orderBr == 0.0) * B.green + (orderAb == 0.0) * A.alpha * (A.blue != 0.0) + (orderBb == 0.0) * B.alpha" 575 | name MergeExpression2 576 | xpos -161 577 | ypos 154 578 | } 579 | set Ca1fa400 [stack 0] 580 | Dot { 581 | name Dot40 582 | xpos -130 583 | ypos 1257 584 | } 585 | push $N99fb000 586 | Dot { 587 | name Dot27 588 | xpos -293 589 | ypos -185 590 | } 591 | set Na22d800 [stack 0] 592 | push $N9a1f800 593 | Shuffle { 594 | in none 595 | name Shuffle_05 596 | xpos -381 597 | ypos -264 598 | } 599 | clone $C9a1ec00 { 600 | inputs 1+1 601 | xpos -381 602 | ypos -127 603 | selected false 604 | } 605 | Dot { 606 | name Dot33 607 | xpos -347 608 | ypos 75 609 | } 610 | set Na22cc00 [stack 0] 611 | push $N9a1e800 612 | push $N6f60d200 613 | clone $Ca1fb000 { 614 | inputs 2 615 | xpos -271 616 | ypos 71 617 | selected false 618 | } 619 | push $N6f60d200 620 | Switch { 621 | inputs 2 622 | which {{"parent.cryptoLayers > 6"}} 623 | name Switch_06 624 | xpos -271 625 | ypos 137 626 | disable true 627 | } 628 | set C6f60cd80 [stack 0] 629 | set N6f60cd80 [stack 0] 630 | clone $Ca1fa400 { 631 | inputs 2 632 | xpos -271 633 | ypos 221 634 | selected false 635 | } 636 | Dot { 637 | name Dot39 638 | xpos -240 639 | ypos 1184 640 | } 641 | push $Na22d800 642 | Dot { 643 | name Dot26 644 | xpos -398 645 | ypos -185 646 | } 647 | set Na255400 [stack 0] 648 | push $N9a1fc00 649 | Shuffle { 650 | in none 651 | name Shuffle_04 652 | xpos -491 653 | ypos -264 654 | } 655 | clone $C9a1ec00 { 656 | inputs 1+1 657 | xpos -491 658 | ypos -128 659 | selected false 660 | } 661 | Dot { 662 | name Dot32 663 | xpos -457 664 | ypos 141 665 | } 666 | set Na254800 [stack 0] 667 | push $Na22cc00 668 | push $N6f60cd80 669 | clone $Ca1fb000 { 670 | inputs 2 671 | xpos -381 672 | ypos 137 673 | selected false 674 | } 675 | push $N6f60cd80 676 | Switch { 677 | inputs 2 678 | which {{"parent.cryptoLayers > 5"}} 679 | name Switch_05 680 | xpos -381 681 | ypos 203 682 | disable true 683 | } 684 | set C6f60c900 [stack 0] 685 | set N6f60c900 [stack 0] 686 | clone $Ca1fa400 { 687 | inputs 2 688 | xpos -381 689 | ypos 288 690 | selected false 691 | } 692 | Dot { 693 | name Dot38 694 | xpos -347 695 | ypos 1120 696 | } 697 | push $Na255400 698 | Dot { 699 | name Dot25 700 | xpos -508 701 | ypos -185 702 | } 703 | set Na289000 [stack 0] 704 | push $N99fa000 705 | Shuffle { 706 | in none 707 | name Shuffle_03 708 | xpos -601 709 | ypos -264 710 | } 711 | clone $C9a1ec00 { 712 | inputs 1+1 713 | xpos -601 714 | ypos -128 715 | selected false 716 | } 717 | Dot { 718 | name Dot31 719 | xpos -567 720 | ypos 207 721 | } 722 | set Na288400 [stack 0] 723 | push $Na254800 724 | push $N6f60c900 725 | clone $Ca1fb000 { 726 | inputs 2 727 | xpos -491 728 | ypos 203 729 | selected false 730 | } 731 | push $N6f60c900 732 | Switch { 733 | inputs 2 734 | which {{"parent.cryptoLayers > 4"}} 735 | name Switch_04 736 | xpos -491 737 | ypos 269 738 | disable true 739 | } 740 | set C6f60c480 [stack 0] 741 | set N6f60c480 [stack 0] 742 | clone $Ca1fa400 { 743 | inputs 2 744 | xpos -491 745 | ypos 353 746 | selected false 747 | } 748 | Dot { 749 | name Dot37 750 | xpos -457 751 | ypos 1054 752 | } 753 | push $Na289000 754 | Dot { 755 | name Dot3 756 | xpos -618 757 | ypos -185 758 | } 759 | set Na2aac00 [stack 0] 760 | push $N99fa400 761 | Shuffle { 762 | in none 763 | name Shuffle_02 764 | xpos -711 765 | ypos -264 766 | } 767 | clone $C9a1ec00 { 768 | inputs 1+1 769 | xpos -711 770 | ypos -128 771 | selected false 772 | } 773 | Dot { 774 | name Dot30 775 | xpos -677 776 | ypos 273 777 | } 778 | set Na2aa000 [stack 0] 779 | push $Na288400 780 | push $N6f60c480 781 | clone $Ca1fb000 { 782 | inputs 2 783 | xpos -601 784 | ypos 269 785 | selected false 786 | } 787 | push $N6f60c480 788 | Switch { 789 | inputs 2 790 | which {{"parent.cryptoLayers > 3"}} 791 | name Switch_03 792 | xpos -601 793 | ypos 337 794 | disable true 795 | } 796 | set C6f60c000 [stack 0] 797 | set N6f60c000 [stack 0] 798 | clone $Ca1fa400 { 799 | inputs 2 800 | xpos -601 801 | ypos 420 802 | selected false 803 | } 804 | Dot { 805 | name Dot36 806 | xpos -567 807 | ypos 978 808 | } 809 | push $Na2aac00 810 | Dot { 811 | name Dot5 812 | xpos -728 813 | ypos -185 814 | } 815 | set Na2dc800 [stack 0] 816 | push $N99fa800 817 | Shuffle { 818 | in none 819 | name Shuffle_01 820 | xpos -821 821 | ypos -263 822 | } 823 | clone $C9a1ec00 { 824 | inputs 1+1 825 | xpos -821 826 | ypos -129 827 | selected false 828 | } 829 | Dot { 830 | name Dot9 831 | xpos -787 832 | ypos 339 833 | } 834 | set N4b26bc00 [stack 0] 835 | push $Na2aa000 836 | push $N6f60c000 837 | clone $Ca1fb000 { 838 | inputs 2 839 | xpos -711 840 | ypos 337 841 | selected false 842 | } 843 | push $N6f60c000 844 | Switch { 845 | inputs 2 846 | which {{"parent.cryptoLayers > 2"}} 847 | name Switch_02 848 | xpos -711 849 | ypos 411 850 | } 851 | set C4b27da80 [stack 0] 852 | set N4b27da80 [stack 0] 853 | clone $Ca1fa400 { 854 | inputs 2 855 | xpos -711 856 | ypos 489 857 | selected false 858 | } 859 | Dot { 860 | name Dot21 861 | xpos -677 862 | ypos 913 863 | } 864 | push $Na2dc800 865 | Dot { 866 | name Dot7 867 | xpos -838 868 | ypos -185 869 | } 870 | push $N99fac00 871 | Shuffle { 872 | in none 873 | black red 874 | white green 875 | red2 blue 876 | green2 alpha 877 | name Shuffle_00 878 | xpos -931 879 | ypos -262 880 | } 881 | clone $C9a1ec00 { 882 | inputs 1+1 883 | xpos -931 884 | ypos -129 885 | selected false 886 | } 887 | Dot { 888 | name Dot8 889 | xpos -897 890 | ypos 409 891 | } 892 | set N4b299800 [stack 0] 893 | push $N4b26bc00 894 | push $N4b27da80 895 | clone $Ca1fb000 { 896 | inputs 2 897 | xpos -821 898 | ypos 406 899 | selected false 900 | } 901 | push $N4b27da80 902 | Switch { 903 | inputs 2 904 | which {{"parent.cryptoLayers > 1"}} 905 | name Switch_01 906 | xpos -821 907 | ypos 475 908 | } 909 | set C4b27d600 [stack 0] 910 | set N4b27d600 [stack 0] 911 | clone $Ca1fa400 { 912 | inputs 2 913 | xpos -821 914 | ypos 561 915 | selected false 916 | } 917 | Dot { 918 | name Dot20 919 | xpos -787 920 | ypos 828 921 | } 922 | push $N4b299800 923 | push $N4b27d600 924 | clone $Ca1fb000 { 925 | inputs 2 926 | xpos -931 927 | ypos 475 928 | selected false 929 | } 930 | push $N4b27d600 931 | Switch { 932 | inputs 2 933 | which {{"parent.cryptoLayers > 0"}} 934 | name Switch_00 935 | xpos -931 936 | ypos 536 937 | } 938 | set C4b27d180 [stack 0] 939 | push $N9971c00 940 | Dot { 941 | name Dot2 942 | xpos -1074 943 | ypos 802 944 | } 945 | set N4b2c5800 [stack 0] 946 | ShuffleCopy { 947 | inputs 2 948 | alpha alpha2 949 | black red 950 | white green 951 | red2 blue 952 | green2 alpha 953 | out2 {{{parent.Shuffle_00.in}}} 954 | name ShuffleCopy_00 955 | xpos -931 956 | ypos 798 957 | addUserKnob {20 User} 958 | addUserKnob {22 setExpression l "Set Expression" T "nuke.thisNode().knob('out2').setExpression('\[value parent.in00]')" +STARTLINE} 959 | } 960 | push $N4b2c5800 961 | clone $C4b27d180 { 962 | inputs 2 963 | xpos -1108 964 | ypos 857 965 | selected false 966 | } 967 | set N4b27cd00 [stack 0] 968 | ShuffleCopy { 969 | inputs 2 970 | alpha alpha2 971 | black red 972 | white green 973 | red2 blue 974 | green2 alpha 975 | out2 {{{parent.Shuffle_01.in}}} 976 | name ShuffleCopy_01 977 | xpos -931 978 | ypos 857 979 | } 980 | push $N4b27cd00 981 | clone $C4b27d600 { 982 | inputs 2 983 | xpos -1108 984 | ypos 910 985 | selected false 986 | } 987 | set N4b27c880 [stack 0] 988 | ShuffleCopy { 989 | inputs 2 990 | alpha alpha2 991 | black red 992 | white green 993 | red2 blue 994 | green2 alpha 995 | out2 {{{parent.Shuffle_02.in}}} 996 | name ShuffleCopy_02 997 | xpos -931 998 | ypos 909 999 | } 1000 | push $N4b27c880 1001 | clone $C4b27da80 { 1002 | inputs 2 1003 | xpos -1108 1004 | ypos 974 1005 | selected false 1006 | } 1007 | set N4b27c400 [stack 0] 1008 | ShuffleCopy { 1009 | inputs 2 1010 | alpha alpha2 1011 | black red 1012 | white green 1013 | red2 blue 1014 | green2 alpha 1015 | out2 {{{parent.Shuffle_03.in}}} 1016 | name ShuffleCopy_03 1017 | xpos -931 1018 | ypos 974 1019 | } 1020 | push $N4b27c400 1021 | clone $C6f60c000 { 1022 | inputs 2 1023 | xpos -1108 1024 | ypos 1053 1025 | selected false 1026 | } 1027 | set N4b27bf80 [stack 0] 1028 | ShuffleCopy { 1029 | inputs 2 1030 | alpha alpha2 1031 | black red 1032 | white green 1033 | red2 blue 1034 | green2 alpha 1035 | out2 {{{parent.Shuffle_04.in}}} 1036 | name ShuffleCopy_04 1037 | xpos -931 1038 | ypos 1050 1039 | } 1040 | push $N4b27bf80 1041 | clone $C6f60c480 { 1042 | inputs 2 1043 | xpos -1108 1044 | ypos 1120 1045 | selected false 1046 | } 1047 | set N4b27bb00 [stack 0] 1048 | ShuffleCopy { 1049 | inputs 2 1050 | alpha alpha2 1051 | black red 1052 | white green 1053 | red2 blue 1054 | green2 alpha 1055 | out2 {{{parent.Shuffle_05.in}}} 1056 | name ShuffleCopy_05 1057 | xpos -931 1058 | ypos 1116 1059 | } 1060 | push $N4b27bb00 1061 | clone $C6f60c900 { 1062 | inputs 2 1063 | xpos -1108 1064 | ypos 1185 1065 | selected false 1066 | } 1067 | set N4b27b680 [stack 0] 1068 | ShuffleCopy { 1069 | inputs 2 1070 | alpha alpha2 1071 | black red 1072 | white green 1073 | red2 blue 1074 | green2 alpha 1075 | out2 {{{parent.Shuffle_06.in}}} 1076 | name ShuffleCopy_06 1077 | xpos -931 1078 | ypos 1180 1079 | } 1080 | push $N4b27b680 1081 | clone $C6f60cd80 { 1082 | inputs 2 1083 | xpos -1108 1084 | ypos 1256 1085 | selected false 1086 | } 1087 | set N4b27b200 [stack 0] 1088 | ShuffleCopy { 1089 | inputs 2 1090 | alpha alpha2 1091 | black red 1092 | white green 1093 | red2 blue 1094 | green2 alpha 1095 | out2 {{{parent.Shuffle_07.in}}} 1096 | name ShuffleCopy_07 1097 | xpos -931 1098 | ypos 1253 1099 | } 1100 | push $N4b27b200 1101 | clone $C6f60d200 { 1102 | inputs 2 1103 | xpos -1108 1104 | ypos 1328 1105 | selected false 1106 | } 1107 | Dot { 1108 | name Dot45 1109 | xpos -1074 1110 | ypos 1646 1111 | } 1112 | ShuffleCopy { 1113 | inputs 2 1114 | alpha alpha2 1115 | black red 1116 | white green 1117 | red2 blue 1118 | green2 alpha 1119 | out2 {{{parent.Shuffle_previewChannel.in}}} 1120 | name ShuffleCopy_previewChannel 1121 | xpos 227 1122 | ypos 1642 1123 | } 1124 | ModifyMetaData { 1125 | metadata { 1126 | {set "\[value parent.manifestKey]manifest" "\[python -execlocal import cryptomatte_utilities\\nret=cryptomatte_utilities.encryptomatte_add_manifest_id()]"} 1127 | } 1128 | name ModifyMetaData2 1129 | xpos 227 1130 | ypos 1703 1131 | } 1132 | push $N9949c00 1133 | Switch { 1134 | inputs 2 1135 | which {{parent.id!=0.0}} 1136 | name Switch1 1137 | xpos 227 1138 | ypos 1852 1139 | } 1140 | Switch { 1141 | inputs 2 1142 | which {{"\[python (nuke.thisParent().input(0)\\ is\\ None)\\ and\\ (nuke.thisParent().input(1)\\ is\\ None)\\ and\\ not\\ nuke.thisParent()\\\['setupLayers'\\].value()]"}} 1143 | name Switch2 1144 | xpos 227 1145 | ypos 1950 1146 | } 1147 | Output { 1148 | name Output1 1149 | xpos 227 1150 | ypos 2051 1151 | } 1152 | end_group 1153 | -------------------------------------------------------------------------------- /nuke/cryptomatte_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/nuke/cryptomatte_logo.png -------------------------------------------------------------------------------- /nuke/init.py: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright (c) 2014, 2015, 2016, 2017 Psyop Media Company, LLC 4 | # See license.txt 5 | # 6 | # 7 | 8 | import cryptomatte_utilities 9 | cryptomatte_utilities.setup_cryptomatte() 10 | 11 | -------------------------------------------------------------------------------- /nuke/menu.py: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Copyright (c) 2014, 2015, 2016, 2017 Psyop Media Company, LLC 4 | # See license.txt 5 | # 6 | # 7 | 8 | import cryptomatte_utilities 9 | cryptomatte_utilities.setup_cryptomatte_ui() 10 | 11 | -------------------------------------------------------------------------------- /nuke/pymmh3.py: -------------------------------------------------------------------------------- 1 | ''' 2 | pymmh3 was written by Fredrik Kihlander and enhanced by Swapnil Gusani, and is placed in the public 3 | domain. The authors hereby disclaim copyright to this source code. 4 | 5 | pure python implementation of the murmur3 hash algorithm 6 | 7 | https://code.google.com/p/smhasher/wiki/MurmurHash3 8 | 9 | This was written for the times when you do not want to compile c-code and install modules, 10 | and you only want a drop-in murmur3 implementation. 11 | 12 | As this is purely python it is FAR from performant and if performance is anything that is needed 13 | a proper c-module is suggested! 14 | 15 | This module is written to have the same format as mmh3 python package found here for simple conversions: 16 | 17 | https://pypi.python.org/pypi/mmh3/2.3.1 18 | ''' 19 | 20 | import sys as _sys 21 | if (_sys.version_info > (3, 0)): 22 | def xrange( a, b, c ): 23 | return list(range( a, b, c)) 24 | def xencode(x): 25 | if isinstance(x, bytes) or isinstance(x, bytearray): 26 | return x 27 | else: 28 | return x.encode() 29 | else: 30 | def xencode(x): 31 | return x 32 | del _sys 33 | 34 | def hash( key, seed = 0x0 ): 35 | ''' Implements 32bit murmur3 hash. ''' 36 | 37 | key = bytearray( xencode(key) ) 38 | 39 | def fmix( h ): 40 | h ^= h >> 16 41 | h = ( h * 0x85ebca6b ) & 0xFFFFFFFF 42 | h ^= h >> 13 43 | h = ( h * 0xc2b2ae35 ) & 0xFFFFFFFF 44 | h ^= h >> 16 45 | return h 46 | 47 | length = len( key ) 48 | nblocks = int( length / 4 ) 49 | 50 | h1 = seed 51 | 52 | c1 = 0xcc9e2d51 53 | c2 = 0x1b873593 54 | 55 | # body 56 | for block_start in range( 0, nblocks * 4, 4 ): 57 | # ??? big endian? 58 | k1 = key[ block_start + 3 ] << 24 | \ 59 | key[ block_start + 2 ] << 16 | \ 60 | key[ block_start + 1 ] << 8 | \ 61 | key[ block_start + 0 ] 62 | 63 | k1 = ( c1 * k1 ) & 0xFFFFFFFF 64 | k1 = ( k1 << 15 | k1 >> 17 ) & 0xFFFFFFFF # inlined ROTL32 65 | k1 = ( c2 * k1 ) & 0xFFFFFFFF 66 | 67 | h1 ^= k1 68 | h1 = ( h1 << 13 | h1 >> 19 ) & 0xFFFFFFFF # inlined ROTL32 69 | h1 = ( h1 * 5 + 0xe6546b64 ) & 0xFFFFFFFF 70 | 71 | # tail 72 | tail_index = nblocks * 4 73 | k1 = 0 74 | tail_size = length & 3 75 | 76 | if tail_size >= 3: 77 | k1 ^= key[ tail_index + 2 ] << 16 78 | if tail_size >= 2: 79 | k1 ^= key[ tail_index + 1 ] << 8 80 | if tail_size >= 1: 81 | k1 ^= key[ tail_index + 0 ] 82 | 83 | if tail_size > 0: 84 | k1 = ( k1 * c1 ) & 0xFFFFFFFF 85 | k1 = ( k1 << 15 | k1 >> 17 ) & 0xFFFFFFFF # inlined ROTL32 86 | k1 = ( k1 * c2 ) & 0xFFFFFFFF 87 | h1 ^= k1 88 | 89 | #finalization 90 | unsigned_val = fmix( h1 ^ length ) 91 | if unsigned_val & 0x80000000 == 0: 92 | return unsigned_val 93 | else: 94 | return -( (unsigned_val ^ 0xFFFFFFFF) + 1 ) 95 | 96 | 97 | def hash128( key, seed = 0x0, x64arch = True ): 98 | ''' Implements 128bit murmur3 hash. ''' 99 | def hash128_x64( key, seed ): 100 | ''' Implements 128bit murmur3 hash for x64. ''' 101 | 102 | def fmix( k ): 103 | k ^= k >> 33 104 | k = ( k * 0xff51afd7ed558ccd ) & 0xFFFFFFFFFFFFFFFF 105 | k ^= k >> 33 106 | k = ( k * 0xc4ceb9fe1a85ec53 ) & 0xFFFFFFFFFFFFFFFF 107 | k ^= k >> 33 108 | return k 109 | 110 | length = len( key ) 111 | nblocks = int( length / 16 ) 112 | 113 | h1 = seed 114 | h2 = seed 115 | 116 | c1 = 0x87c37b91114253d5 117 | c2 = 0x4cf5ad432745937f 118 | 119 | #body 120 | for block_start in range( 0, nblocks * 8, 8 ): 121 | # ??? big endian? 122 | k1 = key[ 2 * block_start + 7 ] << 56 | \ 123 | key[ 2 * block_start + 6 ] << 48 | \ 124 | key[ 2 * block_start + 5 ] << 40 | \ 125 | key[ 2 * block_start + 4 ] << 32 | \ 126 | key[ 2 * block_start + 3 ] << 24 | \ 127 | key[ 2 * block_start + 2 ] << 16 | \ 128 | key[ 2 * block_start + 1 ] << 8 | \ 129 | key[ 2 * block_start + 0 ] 130 | 131 | k2 = key[ 2 * block_start + 15 ] << 56 | \ 132 | key[ 2 * block_start + 14 ] << 48 | \ 133 | key[ 2 * block_start + 13 ] << 40 | \ 134 | key[ 2 * block_start + 12 ] << 32 | \ 135 | key[ 2 * block_start + 11 ] << 24 | \ 136 | key[ 2 * block_start + 10 ] << 16 | \ 137 | key[ 2 * block_start + 9 ] << 8 | \ 138 | key[ 2 * block_start + 8 ] 139 | 140 | k1 = ( c1 * k1 ) & 0xFFFFFFFFFFFFFFFF 141 | k1 = ( k1 << 31 | k1 >> 33 ) & 0xFFFFFFFFFFFFFFFF # inlined ROTL64 142 | k1 = ( c2 * k1 ) & 0xFFFFFFFFFFFFFFFF 143 | h1 ^= k1 144 | 145 | h1 = ( h1 << 27 | h1 >> 37 ) & 0xFFFFFFFFFFFFFFFF # inlined ROTL64 146 | h1 = ( h1 + h2 ) & 0xFFFFFFFFFFFFFFFF 147 | h1 = ( h1 * 5 + 0x52dce729 ) & 0xFFFFFFFFFFFFFFFF 148 | 149 | k2 = ( c2 * k2 ) & 0xFFFFFFFFFFFFFFFF 150 | k2 = ( k2 << 33 | k2 >> 31 ) & 0xFFFFFFFFFFFFFFFF # inlined ROTL64 151 | k2 = ( c1 * k2 ) & 0xFFFFFFFFFFFFFFFF 152 | h2 ^= k2 153 | 154 | h2 = ( h2 << 31 | h2 >> 33 ) & 0xFFFFFFFFFFFFFFFF # inlined ROTL64 155 | h2 = ( h1 + h2 ) & 0xFFFFFFFFFFFFFFFF 156 | h2 = ( h2 * 5 + 0x38495ab5 ) & 0xFFFFFFFFFFFFFFFF 157 | 158 | #tail 159 | tail_index = nblocks * 16 160 | k1 = 0 161 | k2 = 0 162 | tail_size = length & 15 163 | 164 | if tail_size >= 15: 165 | k2 ^= key[ tail_index + 14 ] << 48 166 | if tail_size >= 14: 167 | k2 ^= key[ tail_index + 13 ] << 40 168 | if tail_size >= 13: 169 | k2 ^= key[ tail_index + 12 ] << 32 170 | if tail_size >= 12: 171 | k2 ^= key[ tail_index + 11 ] << 24 172 | if tail_size >= 11: 173 | k2 ^= key[ tail_index + 10 ] << 16 174 | if tail_size >= 10: 175 | k2 ^= key[ tail_index + 9 ] << 8 176 | if tail_size >= 9: 177 | k2 ^= key[ tail_index + 8 ] 178 | 179 | if tail_size > 8: 180 | k2 = ( k2 * c2 ) & 0xFFFFFFFFFFFFFFFF 181 | k2 = ( k2 << 33 | k2 >> 31 ) & 0xFFFFFFFFFFFFFFFF # inlined ROTL64 182 | k2 = ( k2 * c1 ) & 0xFFFFFFFFFFFFFFFF 183 | h2 ^= k2 184 | 185 | if tail_size >= 8: 186 | k1 ^= key[ tail_index + 7 ] << 56 187 | if tail_size >= 7: 188 | k1 ^= key[ tail_index + 6 ] << 48 189 | if tail_size >= 6: 190 | k1 ^= key[ tail_index + 5 ] << 40 191 | if tail_size >= 5: 192 | k1 ^= key[ tail_index + 4 ] << 32 193 | if tail_size >= 4: 194 | k1 ^= key[ tail_index + 3 ] << 24 195 | if tail_size >= 3: 196 | k1 ^= key[ tail_index + 2 ] << 16 197 | if tail_size >= 2: 198 | k1 ^= key[ tail_index + 1 ] << 8 199 | if tail_size >= 1: 200 | k1 ^= key[ tail_index + 0 ] 201 | 202 | if tail_size > 0: 203 | k1 = ( k1 * c1 ) & 0xFFFFFFFFFFFFFFFF 204 | k1 = ( k1 << 31 | k1 >> 33 ) & 0xFFFFFFFFFFFFFFFF # inlined ROTL64 205 | k1 = ( k1 * c2 ) & 0xFFFFFFFFFFFFFFFF 206 | h1 ^= k1 207 | 208 | #finalization 209 | h1 ^= length 210 | h2 ^= length 211 | 212 | h1 = ( h1 + h2 ) & 0xFFFFFFFFFFFFFFFF 213 | h2 = ( h1 + h2 ) & 0xFFFFFFFFFFFFFFFF 214 | 215 | h1 = fmix( h1 ) 216 | h2 = fmix( h2 ) 217 | 218 | h1 = ( h1 + h2 ) & 0xFFFFFFFFFFFFFFFF 219 | h2 = ( h1 + h2 ) & 0xFFFFFFFFFFFFFFFF 220 | 221 | return ( h2 << 64 | h1 ) 222 | 223 | def hash128_x86( key, seed ): 224 | ''' Implements 128bit murmur3 hash for x86. ''' 225 | 226 | def fmix( h ): 227 | h ^= h >> 16 228 | h = ( h * 0x85ebca6b ) & 0xFFFFFFFF 229 | h ^= h >> 13 230 | h = ( h * 0xc2b2ae35 ) & 0xFFFFFFFF 231 | h ^= h >> 16 232 | return h 233 | 234 | length = len( key ) 235 | nblocks = int( length / 16 ) 236 | 237 | h1 = seed 238 | h2 = seed 239 | h3 = seed 240 | h4 = seed 241 | 242 | c1 = 0x239b961b 243 | c2 = 0xab0e9789 244 | c3 = 0x38b34ae5 245 | c4 = 0xa1e38b93 246 | 247 | #body 248 | for block_start in range( 0, nblocks * 16, 16 ): 249 | k1 = key[ block_start + 3 ] << 24 | \ 250 | key[ block_start + 2 ] << 16 | \ 251 | key[ block_start + 1 ] << 8 | \ 252 | key[ block_start + 0 ] 253 | 254 | k2 = key[ block_start + 7 ] << 24 | \ 255 | key[ block_start + 6 ] << 16 | \ 256 | key[ block_start + 5 ] << 8 | \ 257 | key[ block_start + 4 ] 258 | 259 | k3 = key[ block_start + 11 ] << 24 | \ 260 | key[ block_start + 10 ] << 16 | \ 261 | key[ block_start + 9 ] << 8 | \ 262 | key[ block_start + 8 ] 263 | 264 | k4 = key[ block_start + 15 ] << 24 | \ 265 | key[ block_start + 14 ] << 16 | \ 266 | key[ block_start + 13 ] << 8 | \ 267 | key[ block_start + 12 ] 268 | 269 | k1 = ( c1 * k1 ) & 0xFFFFFFFF 270 | k1 = ( k1 << 15 | k1 >> 17 ) & 0xFFFFFFFF # inlined ROTL32 271 | k1 = ( c2 * k1 ) & 0xFFFFFFFF 272 | h1 ^= k1 273 | 274 | h1 = ( h1 << 19 | h1 >> 13 ) & 0xFFFFFFFF # inlined ROTL32 275 | h1 = ( h1 + h2 ) & 0xFFFFFFFF 276 | h1 = ( h1 * 5 + 0x561ccd1b ) & 0xFFFFFFFF 277 | 278 | k2 = ( c2 * k2 ) & 0xFFFFFFFF 279 | k2 = ( k2 << 16 | k2 >> 16 ) & 0xFFFFFFFF # inlined ROTL32 280 | k2 = ( c3 * k2 ) & 0xFFFFFFFF 281 | h2 ^= k2 282 | 283 | h2 = ( h2 << 17 | h2 >> 15 ) & 0xFFFFFFFF # inlined ROTL32 284 | h2 = ( h2 + h3 ) & 0xFFFFFFFF 285 | h2 = ( h2 * 5 + 0x0bcaa747 ) & 0xFFFFFFFF 286 | 287 | k3 = ( c3 * k3 ) & 0xFFFFFFFF 288 | k3 = ( k3 << 17 | k3 >> 15 ) & 0xFFFFFFFF # inlined ROTL32 289 | k3 = ( c4 * k3 ) & 0xFFFFFFFF 290 | h3 ^= k3 291 | 292 | h3 = ( h3 << 15 | h3 >> 17 ) & 0xFFFFFFFF # inlined ROTL32 293 | h3 = ( h3 + h4 ) & 0xFFFFFFFF 294 | h3 = ( h3 * 5 + 0x96cd1c35 ) & 0xFFFFFFFF 295 | 296 | k4 = ( c4 * k4 ) & 0xFFFFFFFF 297 | k4 = ( k4 << 18 | k4 >> 14 ) & 0xFFFFFFFF # inlined ROTL32 298 | k4 = ( c1 * k4 ) & 0xFFFFFFFF 299 | h4 ^= k4 300 | 301 | h4 = ( h4 << 13 | h4 >> 19 ) & 0xFFFFFFFF # inlined ROTL32 302 | h4 = ( h1 + h4 ) & 0xFFFFFFFF 303 | h4 = ( h4 * 5 + 0x32ac3b17 ) & 0xFFFFFFFF 304 | 305 | #tail 306 | tail_index = nblocks * 16 307 | k1 = 0 308 | k2 = 0 309 | k3 = 0 310 | k4 = 0 311 | tail_size = length & 15 312 | 313 | if tail_size >= 15: 314 | k4 ^= key[ tail_index + 14 ] << 16 315 | if tail_size >= 14: 316 | k4 ^= key[ tail_index + 13 ] << 8 317 | if tail_size >= 13: 318 | k4 ^= key[ tail_index + 12 ] 319 | 320 | if tail_size > 12: 321 | k4 = ( k4 * c4 ) & 0xFFFFFFFF 322 | k4 = ( k4 << 18 | k4 >> 14 ) & 0xFFFFFFFF # inlined ROTL32 323 | k4 = ( k4 * c1 ) & 0xFFFFFFFF 324 | h4 ^= k4 325 | 326 | if tail_size >= 12: 327 | k3 ^= key[ tail_index + 11 ] << 24 328 | if tail_size >= 11: 329 | k3 ^= key[ tail_index + 10 ] << 16 330 | if tail_size >= 10: 331 | k3 ^= key[ tail_index + 9 ] << 8 332 | if tail_size >= 9: 333 | k3 ^= key[ tail_index + 8 ] 334 | 335 | if tail_size > 8: 336 | k3 = ( k3 * c3 ) & 0xFFFFFFFF 337 | k3 = ( k3 << 17 | k3 >> 15 ) & 0xFFFFFFFF # inlined ROTL32 338 | k3 = ( k3 * c4 ) & 0xFFFFFFFF 339 | h3 ^= k3 340 | 341 | if tail_size >= 8: 342 | k2 ^= key[ tail_index + 7 ] << 24 343 | if tail_size >= 7: 344 | k2 ^= key[ tail_index + 6 ] << 16 345 | if tail_size >= 6: 346 | k2 ^= key[ tail_index + 5 ] << 8 347 | if tail_size >= 5: 348 | k2 ^= key[ tail_index + 4 ] 349 | 350 | if tail_size > 4: 351 | k2 = ( k2 * c2 ) & 0xFFFFFFFF 352 | k2 = ( k2 << 16 | k2 >> 16 ) & 0xFFFFFFFF # inlined ROTL32 353 | k2 = ( k2 * c3 ) & 0xFFFFFFFF 354 | h2 ^= k2 355 | 356 | if tail_size >= 4: 357 | k1 ^= key[ tail_index + 3 ] << 24 358 | if tail_size >= 3: 359 | k1 ^= key[ tail_index + 2 ] << 16 360 | if tail_size >= 2: 361 | k1 ^= key[ tail_index + 1 ] << 8 362 | if tail_size >= 1: 363 | k1 ^= key[ tail_index + 0 ] 364 | 365 | if tail_size > 0: 366 | k1 = ( k1 * c1 ) & 0xFFFFFFFF 367 | k1 = ( k1 << 15 | k1 >> 17 ) & 0xFFFFFFFF # inlined ROTL32 368 | k1 = ( k1 * c2 ) & 0xFFFFFFFF 369 | h1 ^= k1 370 | 371 | #finalization 372 | h1 ^= length 373 | h2 ^= length 374 | h3 ^= length 375 | h4 ^= length 376 | 377 | h1 = ( h1 + h2 ) & 0xFFFFFFFF 378 | h1 = ( h1 + h3 ) & 0xFFFFFFFF 379 | h1 = ( h1 + h4 ) & 0xFFFFFFFF 380 | h2 = ( h1 + h2 ) & 0xFFFFFFFF 381 | h3 = ( h1 + h3 ) & 0xFFFFFFFF 382 | h4 = ( h1 + h4 ) & 0xFFFFFFFF 383 | 384 | h1 = fmix( h1 ) 385 | h2 = fmix( h2 ) 386 | h3 = fmix( h3 ) 387 | h4 = fmix( h4 ) 388 | 389 | h1 = ( h1 + h2 ) & 0xFFFFFFFF 390 | h1 = ( h1 + h3 ) & 0xFFFFFFFF 391 | h1 = ( h1 + h4 ) & 0xFFFFFFFF 392 | h2 = ( h1 + h2 ) & 0xFFFFFFFF 393 | h3 = ( h1 + h3 ) & 0xFFFFFFFF 394 | h4 = ( h1 + h4 ) & 0xFFFFFFFF 395 | 396 | return ( h4 << 96 | h3 << 64 | h2 << 32 | h1 ) 397 | 398 | key = bytearray( xencode(key) ) 399 | 400 | if x64arch: 401 | return hash128_x64( key, seed ) 402 | else: 403 | return hash128_x86( key, seed ) 404 | 405 | 406 | def hash64( key, seed = 0x0, x64arch = True ): 407 | ''' Implements 64bit murmur3 hash. Returns a tuple. ''' 408 | 409 | hash_128 = hash128( key, seed, x64arch ) 410 | 411 | unsigned_val1 = hash_128 & 0xFFFFFFFFFFFFFFFF 412 | if unsigned_val1 & 0x8000000000000000 == 0: 413 | signed_val1 = unsigned_val1 414 | else: 415 | signed_val1 = -( (unsigned_val1 ^ 0xFFFFFFFFFFFFFFFF) + 1 ) 416 | 417 | unsigned_val2 = ( hash_128 >> 64 ) & 0xFFFFFFFFFFFFFFFF 418 | if unsigned_val2 & 0x8000000000000000 == 0: 419 | signed_val2 = unsigned_val2 420 | else: 421 | signed_val2 = -( (unsigned_val2 ^ 0xFFFFFFFFFFFFFFFF) + 1 ) 422 | 423 | return ( int( signed_val1 ), int( signed_val2 ) ) 424 | 425 | 426 | def hash_bytes( key, seed = 0x0, x64arch = True ): 427 | ''' Implements 128bit murmur3 hash. Returns a byte string. ''' 428 | 429 | hash_128 = hash128( key, seed, x64arch ) 430 | 431 | bytestring = '' 432 | 433 | for i in range(0, 16, 1): 434 | lsbyte = hash_128 & 0xFF 435 | bytestring = bytestring + str( chr( lsbyte ) ) 436 | hash_128 = hash_128 >> 8 437 | 438 | return bytestring 439 | 440 | 441 | if __name__ == "__main__": 442 | import argparse 443 | 444 | parser = argparse.ArgumentParser( 'pymurmur3', 'pymurmur [options] "string to hash"' ) 445 | parser.add_argument( '--seed', type = int, default = 0 ) 446 | parser.add_argument( 'strings', default = [], nargs='+') 447 | 448 | opts = parser.parse_args() 449 | 450 | for str_to_hash in opts.strings: 451 | sys.stdout.write( '"%s" = 0x%08X\n' % ( str_to_hash, hash( str_to_hash ) ) ) 452 | -------------------------------------------------------------------------------- /sample_images/bunny_CryptoAsset.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/sample_images/bunny_CryptoAsset.exr -------------------------------------------------------------------------------- /sample_images/bunny_CryptoMaterial.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/sample_images/bunny_CryptoMaterial.exr -------------------------------------------------------------------------------- /sample_images/bunny_CryptoObject.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/sample_images/bunny_CryptoObject.exr -------------------------------------------------------------------------------- /sample_images/cornellBox_CryptoWildcard.0001.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/sample_images/cornellBox_CryptoWildcard.0001.exr -------------------------------------------------------------------------------- /sample_images/cornellBox_CryptoWildcard.0002.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/sample_images/cornellBox_CryptoWildcard.0002.exr -------------------------------------------------------------------------------- /sample_images/debug_images/about.md: -------------------------------------------------------------------------------- 1 | These are small, low resolution images for testing features of Cryptomatte. 2 | 3 | * multichannel.exr: Contains three cryptomattes. 4 | * sidecar_manifest.exr: Has a sidecar manifest. 5 | * special_chars.exr: Contains UTF-8 characters, as well as spaces, quotes, and commas. 6 | 7 | -------------------------------------------------------------------------------- /sample_images/debug_images/multichannel.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/sample_images/debug_images/multichannel.exr -------------------------------------------------------------------------------- /sample_images/debug_images/sidecar_manifest.crypto_asset.json: -------------------------------------------------------------------------------- 1 | {"default":"42c9679f","default_111":"ad58c73a","default_222":"24c0148d","default_333":"64fdaf51","default_444":"8ca57ad9","default_555":"56c1f4ca","leftAsset":"ce406a47","rightAsset":"8f3ccbde"} -------------------------------------------------------------------------------- /sample_images/debug_images/sidecar_manifest.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/sample_images/debug_images/sidecar_manifest.exr -------------------------------------------------------------------------------- /sample_images/debug_images/special_chars.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/sample_images/debug_images/special_chars.exr -------------------------------------------------------------------------------- /sample_images/sidecar_manifest/bunny_CryptoObject.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/sample_images/sidecar_manifest/bunny_CryptoObject.exr -------------------------------------------------------------------------------- /sample_images/testGrid_CryptoObject.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/sample_images/testGrid_CryptoObject.exr -------------------------------------------------------------------------------- /specification/IDmattes_poster.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/specification/IDmattes_poster.pdf -------------------------------------------------------------------------------- /specification/cryptomatte_specification.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Psyop/Cryptomatte/968d5e4b6171e29ba5f89d554117132a164e747e/specification/cryptomatte_specification.pdf --------------------------------------------------------------------------------