Browse Source

sceneControl. Fixed issue if element is not set.

sceneControl. Improved debugging.
sceneControl. Deletion now also deletes Kenv
sceneControl. Write Nodes get a new label with the used exr compression type.
sceneControl. Fixed issue when unloading plugin
sceneControl. Simplified kenv queries
sceneControl. updateWriteNodeConfig() for updating the Keller tab on write nodes
ffmpeg_write. Initial version. NNode with correct names and burn-ins
our_write. Write nodes now have a compression label and a custom color to see which exr compression type is set.
added search and replace panel.
Denoise with noice. Initial version.
ocio config, image formats, fps and favorites are now set from KEnv project_config.py.

fixed missing folders and files, added nuke.bat
Martin Sächsinger 3 years ago
parent
commit
043d7ac956

+ 60
- 0
Nuke12.2v5_GEN.bat View File

@@ -0,0 +1,60 @@
1
+@echo off
2
+
3
+:SHARED_LIBS
4
+set KELLER_PY_DISABLE=bcrypt;boto3;botocore;cffi;cryptography;deadline;deepdiff;ExecortexAlembic;FileMaker;getmac;jmespath;jsonpaht_rw;jsonpickle;lxml;Muster;Muster8;nacl;OpenGL;paramiko;ply;psutil;pycparser;s3transfer;scandir;shotgun_api3;tqdm;vrml;win32;ws3build
5
+
6
+:NUKE_VARIABLES
7
+set NUKE_VER=12.2v5
8
+set NUKE_MAJOR=12
9
+set NUKE_MINOR=2
10
+set NUKE_INSTALL_PATH=C:\Program Files\Nuke%NUKE_VER%
11
+
12
+:USER
13
+set USER=ms
14
+set NUKE_TEMP_DIR=E:\_CACHE\NUKE
15
+set RV=c:\mrtn\tools\RV\_versions\_current\bin\
16
+:/USER
17
+
18
+:INACTIVE
19
+rem set LENSMODEL_PATH=%NUKE_PATH%\3delens\
20
+
21
+:WORKGROUP
22
+set NUKE_PATH=\\hades\p\env\nuke\_workgroup\%NUKE_MAJOR%.%NUKE_MINOR%\
23
+set NUKE_PLUGINS=%NUKE_PATH%
24
+set OFX_PLUGIN_PATH=%NUKE_PATH%\ofx
25
+
26
+:DEV MODE
27
+set DEV=0
28
+
29
+:JOB SPECIFIC:
30
+set JOB=GEN
31
+set PROJECT=%JOB%
32
+set PROJECT_ROOT_3D=\\hades\p\_projekte\%JOB%
33
+set PROJECT_ROOT_2D=\\calculon\o\_projekte\%JOB%
34
+set FTRACK_API_PYTHONPATH=%PROJECT_ROOT_3D%\000_env\python\keller-ftrack-connect
35
+
36
+REM if %DEV%==1 (set NUKE_PATH=%JOB%\000_dev\nuke\) else (set NUKE_PATH=%JOB%\000_env\nuke\)
37
+
38
+:LICENSE
39
+set foundry_LICENSE=4101@192.168.10.25;49172@192.168.10.205;5053@192.168.0.223
40
+set peregrinel_LICENSE=5053@192.168.0.223
41
+set keller_LICENSE=5053@192.168.0.223
42
+
43
+:START
44
+title Nuke %NUKE_VER% Console
45
+echo ###################################################################################################
46
+echo
47
+echo Starting Nuke %NUKE_VER% ...
48
+echo StartPath: %NUKE_INSTALL_PATH%
49
+echo Workgroup: %NUKE_PATH%
50
+echo Devmode: %DEV%
51
+echo Project_Root_2D: %PROJECT_ROOT_2D%
52
+echo Project_Root_3D: %PROJECT_ROOT_3D%
53
+echo Job / Project: %PROJECT%
54
+echo User: %USER%
55
+echo User Temp Dir: %NUKE_TEMP_DIR%
56
+echo User RV Install Path: %RV%
57
+echo Ftrack Api Python Path: %FTRACK_API_PYTHONPATH%
58
+echo
59
+echo ###################################################################################################
60
+"%NUKE_INSTALL_PATH%\nuke12.2.exe" -V --disable-nuke-frameserver %*

BIN
doc/channelSelect.jpg (Stored with Git LFS) View File

2
+oid sha256:9ccbaf70b4036c7adc4a4cf7177373fb79ba987d9eb22062726c6be7bc324676
3
+size 63434

BIN
doc/ffmpegWrite.jpg (Stored with Git LFS) View File

2
+oid sha256:93723d8341cd2767e274f7f8b2970a2cfb376279c7a2bc27560064570fb53181
3
+size 77334

BIN
doc/musterSubmit2.jpg (Stored with Git LFS) View File

2
+oid sha256:1d060f8c94096395673d08a4b2aad066a35d444661544515610831f09d162a18
3
+size 35092

BIN
doc/nukeNoice.jpg (Stored with Git LFS) View File

2
+oid sha256:9fd1c309167c679cc2ac5ee01daca76f4e738fa21257f5c0962ea74d038f95f9
3
+size 132493

BIN
doc/ourWrite.jpg (Stored with Git LFS) View File

2
+oid sha256:f433699a3cbd4fbb23098a7ab916d3584aec01e6864b4bc085c08cd8e16a5279
3
+size 49438

BIN
doc/sceneControl.jpg (Stored with Git LFS) View File

2
+oid sha256:ee1187d72b6ab8cbbf5aa10da17919dffc3e958e40fe8d4ddd825b5ce07d7991
3
+size 73740

BIN
doc/sceneControl_m0.jpg (Stored with Git LFS) View File

2
+oid sha256:35ccb42fba3ae1c4c7989ac3bc07a788c4cdf51bb74b84566885035c7557fb55
3
+size 21393

BIN
doc/sceneControl_m1.jpg (Stored with Git LFS) View File

2
+oid sha256:072d6d461ca66cf1b104b1c390f5ef7493c721ca425f9ac3d730caea8ec4389a
3
+size 22804

BIN
doc/sceneControl_m2.jpg (Stored with Git LFS) View File

2
+oid sha256:389131bc6ea21ef6c265fc8e31c8c5d2235aab320d31a1a9ae5eb4422b5b2a7d
3
+size 18077

+ 1478
- 0
gimmicks/Gimmicks/_KF/KF_GRAIN.gimmick
File diff suppressed because it is too large
View File


+ 71
- 0
gimmicks/Gimmicks/_KF/KF_Handles.gimmick View File

@@ -0,0 +1,71 @@
1
+set cut_paste_input [stack 0]
2
+version 10.5 v3
3
+push $cut_paste_input
4
+Group {
5
+name KF_Handles1
6
+selected true
7
+xpos -198
8
+ypos 523
9
+addUserKnob {20 User}
10
+addUserKnob {3 Handles}
11
+Handles 24
12
+}
13
+Input {
14
+inputs 0
15
+name Input1
16
+xpos -476
17
+ypos 213
18
+}
19
+set N3b3f800 [stack 0]
20
+push $N3b3f800
21
+Text2 {
22
+font_size_toolbar 100
23
+font_width_toolbar 100
24
+font_height_toolbar 100
25
+cliptype none
26
+message Handles
27
+old_message {{72 97 110 100 108 101 115}
28
+  }
29
+box {{width/2} 100 {width/2} 10}
30
+xjustify center
31
+yjustify center
32
+transforms {{0 2}
33
+  }
34
+font_size_values {{0 100 1 100 2 100 3 100 4 100 5 100 6 100 0 25 1 25 2 25 3 25 4 25 5 25 6 25}
35
+  }
36
+scale {1 1}
37
+center {1024 778}
38
+cursor_initialised true
39
+autofit_bbox false
40
+initial_cursor_position {{756 102.5}
41
+  }
42
+group_animations {{0} imported: 0 selected: 0 items: "root transform/"}
43
+animation_layers {{1 11 1024 778 0 0 1 1 0 0 0 0}
44
+  }
45
+name Text1
46
+xpos -405
47
+ypos 271
48
+}
49
+set N9d5fc800 [stack 0]
50
+Switch {
51
+inputs 2
52
+which {{frame>root.first_frame+(parent.Handles-1)?frame<root.last_frame-(parent.Handles-1)?1:0:0}}
53
+name Switch1
54
+xpos -476
55
+ypos 325
56
+}
57
+Output {
58
+name Output1
59
+xpos -476
60
+ypos 377
61
+}
62
+push $N9d5fc800
63
+Viewer {
64
+frame_range 1001-1101
65
+name Viewer1
66
+xpos -291
67
+ypos 384
68
+}
69
+end_group
70
+# Creation Time=Thu Jun 07 17:42:55 2018
71
+# Creator=ne

+ 84
- 0
gimmicks/Templates/_KF/KF_Bokeh.gimmick View File

@@ -0,0 +1,84 @@
1
+set cut_paste_input [stack 0]
2
+version 10.5 v3
3
+BackdropNode {
4
+ inputs 0
5
+ name BackdropNode1
6
+ tile_color 0x388e8e00
7
+ note_font_size 42
8
+ selected true
9
+ xpos -224
10
+ ypos -68
11
+ bdwidth 269
12
+ bdheight 196
13
+}
14
+push $cut_paste_input
15
+Dot {
16
+ name IMAGE_INPUT
17
+ label IMAGE_INPUT
18
+ note_font Verdana
19
+ note_font_size 15
20
+ note_font_color 0xffffffff
21
+ selected true
22
+ xpos -180
23
+ ypos -129
24
+}
25
+Copy {
26
+ from0 rgba.red
27
+ to0 depth.Z
28
+ name Copy1
29
+ selected true
30
+ xpos -214
31
+ ypos 10
32
+}
33
+pgBokeh {
34
+ focalPlane {{LensMetaData.LensFocus+LensMetaData.FocusOffset}}
35
+ realWorldLens true
36
+ focalLength {{LensMetaData.LensFocalLength}}
37
+ fStop {{LensMetaData.LensIris+LensMetaData.IrisOffset}}
38
+ worldScale m
39
+ filmFormat Custom
40
+ apertureWidth 54.12
41
+ apertureHeight 25.58
42
+ name pgBokeh1
43
+ selected true
44
+ xpos -214
45
+ ypos 96
46
+}
47
+Dot {
48
+ name IMAGE_OUTPUT
49
+ label IMAGE_OUTPUT
50
+ note_font Verdana
51
+ note_font_size 15
52
+ note_font_color 0xffffffff
53
+ selected true
54
+ xpos -180
55
+ ypos 175
56
+}
57
+Dot {
58
+ inputs 0
59
+ name METADATA_INPUT
60
+ label METADATA_INPUT
61
+ note_font Verdana
62
+ note_font_size 15
63
+ note_font_color 0xffffffff
64
+ selected true
65
+ xpos 23
66
+ ypos -91
67
+}
68
+Dot {
69
+ name LensMetaData
70
+ selected true
71
+ xpos 23
72
+ ypos 101
73
+ addUserKnob {20 User}
74
+ addUserKnob {7 LensFocalLength l "Lens Focal Length"}
75
+ LensFocalLength {{"\[metadata \"ext/Lens Focal Length\"]"}}
76
+ addUserKnob {7 LensFocus l "Lens Focus Distance"}
77
+ LensFocus {{"\[metadata \"ext/Lens Focus Distance\"]"}}
78
+ addUserKnob {7 LensIris l "Lens Iris"}
79
+ LensIris {{"\[metadata \"ext/Lens Iris\"]"}}
80
+ addUserKnob {7 FocusOffset l "Lens Focus Distance Offset"}
81
+ addUserKnob {7 IrisOffset l "Lens Iris Offset"}
82
+}
83
+# Creation Time=Mon Jun 11 15:40:27 2018
84
+# Creator=ne

+ 51
- 0
gimmicks/Templates/_KF/KF_Thumbnails_first.gimmick View File

@@ -0,0 +1,51 @@
1
+set cut_paste_input [stack 0]
2
+version 10.5 v3
3
+BackdropNode {
4
+ inputs 0
5
+ name BackdropNode1
6
+ tile_color 0x999999ff
7
+ gl_color 0x999999ff
8
+ label "first_frame\n"
9
+ note_font_size 15
10
+ selected true
11
+ xpos -181
12
+ ypos -131
13
+ bdheight 200
14
+}
15
+push $cut_paste_input
16
+Reformat {
17
+ type "to box"
18
+ box_width 500
19
+ name Reformat1
20
+ selected true
21
+ xpos -171
22
+ ypos -51
23
+}
24
+Vectorfield {
25
+ vfield_file O:/LUTs/K1S1_3D_Neutral.cube
26
+ version 3
27
+ file_type cube
28
+ colorspaceIn AlexaV3LogC
29
+ name Vectorfield1
30
+ selected true
31
+ xpos -171
32
+ ypos -25
33
+}
34
+Write {
35
+ channels rgba
36
+ file "//calculon/o/_projekte/KalteFuesse/000_A_TrelloThumbs/\[file tail \[lrange \[split \[file rootname \[value root.name]] ] 0 1]].####.jpg"
37
+ raw true
38
+ file_type jpeg
39
+ first {{root.first_frame}}
40
+ last {{root.first_frame}}
41
+ use_limit true
42
+ checkHashOnRead false
43
+ version 1
44
+ beforeRender createWriteDir.createWriteDir()
45
+ name Write1
46
+ selected true
47
+ xpos -171
48
+ ypos 11
49
+}
50
+# Creation Time=Tue Jun 05 18:16:39 2018
51
+# Creator=ne

+ 51
- 0
gimmicks/Templates/_KF/KF_Thumbnails_last.gimmick View File

@@ -0,0 +1,51 @@
1
+set cut_paste_input [stack 0]
2
+version 10.5 v3
3
+BackdropNode {
4
+ inputs 0
5
+ name BackdropNode3
6
+ tile_color 0x999999ff
7
+ gl_color 0x999999ff
8
+ label "last frame\n"
9
+ note_font_size 15
10
+ selected true
11
+ xpos 147
12
+ ypos -134
13
+ bdheight 202
14
+}
15
+push $cut_paste_input
16
+Reformat {
17
+ type "to box"
18
+ box_width 500
19
+ name Reformat3
20
+ selected true
21
+ xpos 157
22
+ ypos -54
23
+}
24
+Vectorfield {
25
+ vfield_file O:/LUTs/K1S1_3D_Neutral.cube
26
+ version 4
27
+ file_type cube
28
+ colorspaceIn AlexaV3LogC
29
+ name Vectorfield3
30
+ selected true
31
+ xpos 157
32
+ ypos -28
33
+}
34
+Write {
35
+ channels rgba
36
+ file "//calculon/o/_projekte/KalteFuesse/000_A_TrelloThumbs/\[file tail \[lrange \[split \[file rootname \[value root.name]] ] 0 1]].####.jpg"
37
+ raw true
38
+ file_type jpeg
39
+ first {{root.last_frame}}
40
+ last {{root.last_frame}}
41
+ use_limit true
42
+ checkHashOnRead false
43
+ version 1
44
+ beforeRender createWriteDir.createWriteDir()
45
+ name Write3
46
+ selected true
47
+ xpos 157
48
+ ypos 11
49
+}
50
+# Creation Time=Tue Jun 05 18:17:13 2018
51
+# Creator=ne

+ 51
- 0
gimmicks/Templates/_KF/KF_Thumbnails_middle.gimmick View File

@@ -0,0 +1,51 @@
1
+set cut_paste_input [stack 0]
2
+version 10.5 v3
3
+BackdropNode {
4
+ inputs 0
5
+ name BackdropNode2
6
+ tile_color 0x999999ff
7
+ gl_color 0x999999ff
8
+ label "mid_frame\n"
9
+ note_font_size 15
10
+ selected true
11
+ xpos -22
12
+ ypos -132
13
+ bdheight 198
14
+}
15
+push $cut_paste_input
16
+Reformat {
17
+ type "to box"
18
+ box_width 500
19
+ name Reformat2
20
+ selected true
21
+ xpos -12
22
+ ypos -52
23
+}
24
+Vectorfield {
25
+ vfield_file O:/LUTs/K1S1_3D_Neutral.cube
26
+ version 4
27
+ file_type cube
28
+ colorspaceIn AlexaV3LogC
29
+ name Vectorfield2
30
+ selected true
31
+ xpos -12
32
+ ypos -26
33
+}
34
+Write {
35
+ channels rgba
36
+ file "//calculon/o/_projekte/KalteFuesse/000_A_TrelloThumbs/\[file tail \[lrange \[split \[file rootname \[value root.name]] ] 0 1]].####.jpg"
37
+ raw true
38
+ file_type jpeg
39
+ first {{int((root.first_frame+root.last_frame)/2)}}
40
+ last {{int((root.first_frame+root.last_frame)/2)}}
41
+ use_limit true
42
+ checkHashOnRead false
43
+ version 1
44
+ beforeRender createWriteDir.createWriteDir()
45
+ name Write2
46
+ selected true
47
+ xpos -12
48
+ ypos 10
49
+}
50
+# Creation Time=Tue Jun 05 18:17:00 2018
51
+# Creator=ne

+ 184
- 0
pythonpluginsUI/SearchReplacePanel/SearchReplacePanel.py View File

@@ -0,0 +1,184 @@
1
+from xml.etree import ElementTree
2
+import nuke
3
+import nukescripts
4
+import os
5
+import re
6
+
7
+class SearchReplacePanel( nukescripts.PythonPanel ):
8
+    def __init__( self, historyFile='~/.nuke/srhistory.xml', maxSteps=10 ):
9
+        '''
10
+        Search and Replace panel
11
+        args:
12
+           historyFile  -  file to manage recent search&replace actions
13
+           maxSteps  -  amount of steps to keep in history file
14
+        '''
15
+        nukescripts.PythonPanel.__init__( self, 'Search and Replace', 'com.ohufx.SearchReplace')
16
+
17
+        # VARS
18
+        self.historyFile = os.path.expandvars( os.path.expanduser( historyFile ) )
19
+        self.maxSteps = maxSteps
20
+        self.delimiter = ' |>>| '
21
+        # CREATE KNOBS
22
+        self.nodesChoice = nuke.Enumeration_Knob( 'nodes', 'Source Nodes', ['all', 'selected'])
23
+        self.nodesChoice.setTooltip( 'Chose to perform action on all nodes with file knobs or only selected ones' )
24
+        self.history = nuke.Enumeration_Knob( 'history', 'Recent Searches', self.loadHistory() )
25
+        self.history.setTooltip( 'Use the history to quicky access previous search&replace actions.\n By default the history file is stored as "~/.nuke/srhistory.xml" but this can be changed via the "historyFile" argument when creating the panel object. It is also possible to change the size of the history via the "maxSteps" argument to the panel object. Default is 10' )
26
+        self.case = nuke.Boolean_Knob( 'case', 'case sensitive' )
27
+        self.case.setFlag( nuke.STARTLINE )
28
+        self.case.setValue( True )
29
+        self.case.setTooltip( 'Set whether or not the search should be case sensitive' )
30
+        self.searchStr = nuke.String_Knob('searchStr', 'Search for:')
31
+        self.searchStr.setTooltip( 'The text to search for' )
32
+        self.update = nuke.PyScript_Knob('update', 'Update')
33
+        self.update.setTooltip( 'update the search result and preview. This is automaticaly performed and usually should only be required when the node selection has canged' )
34
+        self.replaceStr = nuke.String_Knob('replaceStr', 'Replace with:')
35
+        self.replaceStr.setTooltip( 'Text to replace the found text with' )
36
+        self.replace = nuke.PyScript_Knob('replace', 'Replace')
37
+        self.replace.setTooltip( 'Perform replace action. The preview will update afterwards and the action is added to the history' )
38
+        self.info = nuke.Multiline_Eval_String_Knob('info', 'Found')
39
+        self.info.setEnabled( False )
40
+        self.info.setTooltip( 'See the search results and a preview of the replace action before it is performed' )
41
+        # ADD KNOBS
42
+        for k in ( self.nodesChoice, self.history, self.case, self.searchStr, self.update, self.replaceStr, self.replace, self.info):
43
+            self.addKnob( k )
44
+        self.matches = None
45
+
46
+    def loadHistory( self ):
47
+        '''load history file to update knob'''
48
+        print("loading search&replace history")
49
+        # GET EXISTING HISTORY
50
+        if not os.path.isfile( self.historyFile ):
51
+            return []
52
+        # READ EXISTING FILE
53
+        xmlTree = ElementTree.parse( self.historyFile )
54
+        itemList = ['%s%s%s' % ( n.attrib['search'], self.delimiter, n.attrib['replace'] ) for n in xmlTree.findall( 'ITEM' )][-self.maxSteps:]
55
+        itemList.reverse()
56
+        itemList.insert( 0, '-- select --' )
57
+        return itemList
58
+    
59
+    def updateHistory( self, sString, rString ):
60
+        '''
61
+        updates history file
62
+        args:
63
+           sString  -  search string to add to history
64
+           rString  -  replace string to add to history
65
+        TODO  -  IMPLEMENT MAX VALUE
66
+        '''
67
+        itemList = []
68
+        # READ EXISTING FILE
69
+        if os.path.isfile( self.historyFile ):
70
+            xmlTree = ElementTree.parse( self.historyFile )
71
+            for n in xmlTree.findall( 'ITEM' ):
72
+                attr = n.attrib
73
+                itemList.append( attr )
74
+       
75
+        # IGNORE ATTRIBUTES THAT ARE ALREADY IN HISTORY
76
+        entryExists = False
77
+        for i in itemList:
78
+            if i['search'] == sString and i['replace']==rString:
79
+                entryExists = True
80
+                break
81
+
82
+        # IF ATTRIBUTES DONT EXIST IN HISTORY, ADD THEM
83
+        if not entryExists:
84
+            # PREP DICTIONARY FOR XML DUMP
85
+            srItem = dict(search=sString, replace=rString )
86
+            itemList.append( srItem )
87
+            # BUILD XML TREE
88
+            root = ElementTree.Element( 'SearchReplacePanel')
89
+            for i in itemList[-self.maxSteps:]:
90
+                ElementTree.SubElement( root, 'ITEM', attrib=i )
91
+            tree = ElementTree.ElementTree( root )
92
+            # DUMP XML TREE
93
+            print("WRITING TO:", self.historyFile)
94
+            tree.write( self.historyFile )
95
+
96
+    def search( self, searchstr, nodes ):
97
+        """ Search in nodes with file knobs. """
98
+        fileKnobNodes = [i for i in nodes if self.__NodeHasKnobWithName(i, 'file')]
99
+        proxyKnobNodes = [i for i in nodes if self.__NodeHasKnobWithName(i, 'proxy')]
100
+        if not fileKnobNodes and not proxyKnobNodes: raise ValueError("No file nodes selected")
101
+        nodeMatches = []
102
+        knobMatches = []
103
+        for i in fileKnobNodes:
104
+            if self.__findNode(searchstr, i['file'] ):
105
+                nodeMatches.append( i )
106
+                knobMatches.append( i['file'] )            
107
+        for i in proxyKnobNodes:
108
+            if self.__findNode(searchstr, i['proxy'] ):
109
+                nodeMatches.append( i )
110
+                knobMatches.append( i['proxy'] )
111
+        return nodeMatches, knobMatches        
112
+        
113
+    def getSearchResults( self, nodes ):
114
+        # PERFORM SEARCH AND UPDATE INFO KNOB
115
+        nodeMatches, knobMatches = self.search( self.searchStr.value(), nodes )
116
+        nodes = [n.name() for n in nodeMatches]
117
+        infoStr1 = '%s node(s) found:\n\t%s' % ( len(nodes), ', '.join( nodes ) )
118
+        infoStr2 = ''
119
+        for k in knobMatches:
120
+            newStr = nukescripts.replaceHashes( self.__doReplace( k ) )
121
+            # CHECK IF PATH IS VALID FOR CURRENT FRAME
122
+            curFrame = int( nuke.Root()['frame'].value() ) # there is a bug which prevents nuke.frame() to work properly inside of python panels (6.3v5)
123
+            try:
124
+                curPath = newStr % curFrame
125
+            except:
126
+                curPath = newStr
127
+            exists = {True:'  VALID PATH AT FRAME %s' % curFrame, False:'  !!! PATH IS INVALID AT CURRENT FRAME (%s)!!!' % curFrame}[os.path.exists( curPath )]
128
+            # BUILD INFO STRING
129
+            infoStr2 += '%s.%s:\n\tbefore\t%s\n\tafter\t%s  %s\n' % ( k.node().name(), k.name(), k.value(), newStr, exists )
130
+        self.info.setValue( '\n'.join( [infoStr1, infoStr2] ) )
131
+        return knobMatches
132
+
133
+    def __NodeHasKnobWithName( self, node, name):
134
+        try:
135
+            node[name]
136
+        except NameError:
137
+            return False
138
+        return True
139
+    
140
+    def __findNode( self, searchstr, knob ):
141
+        v = knob.value()
142
+        if not self.case.value():
143
+            v = v.lower()
144
+            searchstr = searchstr.lower()
145
+        if v and searchstr and searchstr in v:
146
+            return True    
147
+    
148
+    def __doSearch( self ):
149
+        # LAUNCH SEARCH
150
+        srcNodes = { 'all': nuke.allNodes(), 'selected': nuke.selectedNodes() }
151
+        self.matches = self.getSearchResults( srcNodes[self.nodesChoice.value()] )
152
+
153
+    def __doReplace( self, knob ):
154
+        # PERFORM REPLACE
155
+        if self.case.value():
156
+            # CASE SENSITIVE
157
+            newStr = re.sub( self.searchStr.value(), self.replaceStr.value(), knob.value() )
158
+        else:
159
+            # IGNORE CASE
160
+            newStr = knob.value()
161
+            for m in re.findall( self.searchStr.value(), knob.value(), re.IGNORECASE ):
162
+                newStr = re.sub( m, self.replaceStr.value(), newStr )
163
+
164
+        return newStr
165
+
166
+    def knobChanged( self, knob ):
167
+        if knob in ( self.searchStr, self.replaceStr, self.update, self.nodesChoice, self.case ):
168
+            # PERFORM SEARCH
169
+            self.__doSearch()
170
+        elif knob is self.replace and self.matches is not None:
171
+            # PERFORM REPLACE AND UPDATE HISTORY
172
+            print("replacing")
173
+            for k in self.matches:
174
+                k.setValue( self.__doReplace( k ) )
175
+
176
+            self.updateHistory( self.searchStr.value(), self.replaceStr.value() )
177
+            self.history.setValues( self.loadHistory() )
178
+            self.history.setValue( 0 )
179
+            self.__doSearch()
180
+        elif knob is self.history:
181
+            search, replace = knob.value().split( self.delimiter )
182
+            self.searchStr.setValue( search )
183
+            self.replaceStr.setValue( replace )
184
+            self.__doSearch()

+ 414
- 0
pythonpluginsUI/denoice/denoice.py View File

@@ -0,0 +1,414 @@
1
+import os
2
+import nuke
3
+import nukescripts
4
+import re
5
+import subprocess
6
+import tempfile
7
+
8
+# martin@keller.io
9
+# numpy install:
10
+# https://jurajtomori.wordpress.com/2018/11/28/nuke-tip-installing-numpy-for-nuke-python-on-windows/
11
+
12
+'''noice 6.2.1.0 [359aaa66] - the Arnold denoiser
13
+Usage:  noice [options] ...
14
+  -i,    --input %s           Input image, multiple images can be included for stability, only first input image will be denoised
15
+  -ef,   --extraframes %d     Number of frames to pad before and after for temporal stability (0 - 2, default:0)
16
+  -f,    --frames %d          Number of frames to denoise in the sequence (default:1)
17
+  -fe,   --feature %s         Use this AOV as a denoising feature
18
+  -pr,   --patchradius %d     Neighborhood patch radius, size of pixel neighborhood to compare (0 - 6, default:3)
19
+  -sr,   --searchradius %d    Search radius, higher values mean a wider search for similar pixel neighborhoods (6 - 21, default:9)
20
+  -v,    --variance %f        Variance threshold, higher values mean more aggressive denoising (0.0 - 1.0, default:0.25)
21
+  -l,    --aov %s             Light AOV name to be co-denoised, multiple can be specified
22
+  -o,    --output %s          Output image
23
+  -t,    --threads %d         Number of threads (0: use all cores, negative numbers specify number of idle cores)
24
+  -irgba,--ignore_rgba        Ignore RGBA layer: will not denoise it or copy it to output file
25
+  -n,    --notices            Print copyright notices'''
26
+
27
+class denoice(KellerNukePlugin):
28
+
29
+    def configurePlugin(self):
30
+        menubar = nuke.menu("Nuke")
31
+        self.m = menubar.addMenu("&Render")
32
+        self.m.addCommand('Denoise with Noice', 'import denoice; denoice.addPanel()')
33
+
34
+
35
+    def unconfigurePlugin(self):
36
+        self.m.removeItem("Denoise with Noice")
37
+
38
+
39
+
40
+#######################################################################################
41
+#
42
+# GLOBALS
43
+#
44
+#######################################################################################
45
+DISABLE_AOVS = ['default', 'variance', 'Matte', 'matte', 'Crypto', 'crypto', 'denoise_albedo']
46
+# Muster
47
+MSTRSERVER = "192.168.0.251"
48
+SUBMISSION_USER ="admin"
49
+PASSWORD = ""
50
+MRTOOL = "\\\\hades\\p\\env\\muster9\\Mrtool.exe"
51
+USERNAME = "admin"
52
+TEMPLATE = "2008"
53
+MUSTERFOLDER = "34758"
54
+POOL = "arnold_16thread"
55
+PSIZE = 1
56
+PRIO = 10
57
+
58
+
59
+def find_node_layers(node):
60
+    node_channels = node.channels()
61
+    store_layers = []
62
+    for channel in node_channels:
63
+        cut = channel.split(".")
64
+        last = cut[0]
65
+        store_layers.append(last)
66
+    node_layers = list(set(store_layers))
67
+    node_layers.sort()
68
+
69
+    # put on top if rgb in layers
70
+    if 'rgba' in node_layers:
71
+        node_layers.remove("rgba")
72
+        node_layers.insert(0, "rgba")
73
+    return node_layers
74
+
75
+
76
+class denoicePanel(nukescripts.PythonPanel):
77
+    def __init__(self):
78
+        nukescripts.panels.PythonPanel.__init__(self, "KellerDenoicePanel", scrollable=True)
79
+
80
+
81
+        self.node = nuke.selectedNode()
82
+        self.nodeLayers = find_node_layers(self.node)
83
+        self.layerKnobs = []
84
+
85
+        # just disable layers
86
+        disable = [lay for lay in self.nodeLayers if any(x in lay for x in DISABLE_AOVS)]
87
+        num = len(self.nodeLayers)
88
+        for layer in self.nodeLayers:
89
+            self.layer = nuke.Boolean_Knob(layer, layer, True)
90
+            self.layer.setFlag(nuke.STARTLINE)
91
+            self.addKnob(self.layer)
92
+            self.layerKnobs.append(self.layer)
93
+            if layer in disable:
94
+                self.layer.setValue(False)
95
+
96
+        # interface selection
97
+        self.div1 = nuke.Text_Knob("div1", " ", "")
98
+        self.addKnob(self.div1)
99
+        self.all = nuke.PyScript_Knob("select_all", " Select All ")
100
+        self.addKnob(self.all)
101
+        self.invert = nuke.PyScript_Knob("invert_selection", " Invert Selection ")
102
+        self.addKnob(self.invert)
103
+        self.clear = nuke.PyScript_Knob("clear_selection", " Clear Selection ")
104
+        self.addKnob(self.clear)
105
+        self.div2 = nuke.Text_Knob("div2", " ", "")
106
+        self.addKnob(self.div2)
107
+
108
+        # settings
109
+        # variance
110
+        self.variance = nuke.Double_Knob("variance", "Variance", arraysize=1)
111
+        self.variance.setRange(0,1)
112
+        self.variance.setTooltip(
113
+            'Default: 0.25\nThe strength of the filter is determined by the variance parameter, the higher the variance the more forceful the denoising will be. '
114
+            'For variance maybe 0.25 (the default) / 0.5 / 0.75 are good low/min/max values. How aggressive the Arnold denoiser is in removing noise can be controlled by '
115
+            'setting a variance threshold with the command line argument -variance (-v). The default value is 0.25, higher values will make the denoising more aggressive by '
116
+            'considering similar neighborhoods that have bigger color disparities.')
117
+        self.addKnob(self.variance)
118
+        self.variance.setValue(0.25)
119
+
120
+        # patchradius
121
+        self.patchradius = nuke.Int_Knob("patchradius", "Patchradius", arraysize=1)
122
+        self.patchradius.setRange(0, 10)
123
+        self.patchradius.setTooltip(
124
+            'Default: 3\nThis increases the softness of the denoising (while always preserving the features). The default is 3 (on the big side), but a low value would be maybe 0 or 1, middle 3, and high 5. '
125
+            'For every pixel, the Arnold denoiser will consider its neighborhood patch and look for other pixels with similar neighborhood patches. The radius of this neighborhood can be controlled with the -patchradius '
126
+            ' (or -pr) command-line argument. The default value is set to 3, which gives a 7x7 square neighborhood.')
127
+        self.addKnob(self.patchradius)
128
+        self.patchradius.setValue(3)
129
+
130
+        # searchradius
131
+        self.searchradius = nuke.Int_Knob("searchradius", "Searchradius", arraysize=1)
132
+        self.searchradius.setRange(0, 50)
133
+        self.searchradius.setTooltip(
134
+            'Default: 9\nThis is the area over which similar neighborhoods are found. The higher the better, but it will increase '
135
+            'the cost of denoising. For every pixel noice will search a square area with a radius set with the '
136
+            'command line argument -searchradius (-sr). The bigger this area the bigger the denoising stability and '
137
+            'the higher the chance that similar neighborhoods to be considered will be found. The default value is 9, '
138
+            'which gives a 19x19 square neighborhood. Setting it to 21 (a search window of 42 x 42) will look over a '
139
+            'pixel area equivalent to loading 5 frames.')
140
+        self.addKnob(self.searchradius)
141
+        self.searchradius.setValue(9)
142
+
143
+        # extraFrames
144
+        self.temporalFrames = nuke.Int_Knob("temporalFrames", "ExtraFrames", arraysize=1)
145
+        self.temporalFrames.setRange(0, 2)
146
+        self.temporalFrames.setTooltip('This argument (-ef n or --extraframes n) specifies how many additional source frames before and after the current one should be used, for improved stability in animation sequences.\n(0-2. default:0)')
147
+        self.addKnob(self.temporalFrames)
148
+        self.temporalFrames.setValue(0)
149
+
150
+        # threads
151
+        self.threads = nuke.Int_Knob("threads", "Threads", arraysize=1)
152
+        self.threads.setTooltip('Number of threads (0: use all cores, negative numbers specify number of idle cores)')
153
+        self.addKnob(self.threads)
154
+        self.threads.setValue(-2)
155
+
156
+        # div
157
+        self.div3 = nuke.Text_Knob("div3", " ", "")
158
+        self.addKnob(self.div3)
159
+
160
+        # filepatn in
161
+        self.inputpath = nuke.File_Knob("input", "Input")
162
+        self.addKnob(self.inputpath)
163
+        self.inputpath.setTooltip('Needs to be in #### syntax. Not in %04d')
164
+        self.inputpath.setValue(nuke.selectedNode().knobs()['file'].value().replace('%04d','####'))
165
+
166
+        # filepath out
167
+        self.outputpath = nuke.File_Knob("output", "Output")
168
+        file = nuke.selectedNode().knobs()['file'].value().replace('%04d', '####')
169
+        file = file.replace('####', 'denoise.####')
170
+        self.outputpath.setValue(file)
171
+        self.addKnob(self.outputpath)
172
+
173
+        # set path
174
+        self.setpath = nuke.PyScript_Knob("setpath", " Set Path ")
175
+        self.layer.setFlag(nuke.STARTLINE)
176
+        self.addKnob(self.setpath)
177
+
178
+        # startframe
179
+        self.startframe = nuke.Int_Knob("startframe", "Startframe")
180
+        self.addKnob(self.startframe)
181
+
182
+        # endframe
183
+        self.endframe = nuke.Int_Knob("endframe", "Endframe")
184
+        self.addKnob(self.endframe)
185
+        self.endframe.clearFlag(nuke.STARTLINE)
186
+
187
+        # set to input
188
+        self.setToInput = nuke.PyScript_Knob("setToInput", " Set to Input ")
189
+        self.addKnob(self.setToInput)
190
+        selected = nuke.selectedNode()
191
+        fr = selected.frameRange()
192
+        start = fr.first()
193
+        end = fr.last()
194
+        self.startframe.setValue(start)
195
+        self.endframe.setValue(end)
196
+        self.setToInput.clearFlag(nuke.STARTLINE)
197
+
198
+        # set framerange
199
+        self.setFrameRange = nuke.PyScript_Knob("setFrameRange", " Set Framerange ")
200
+        self.addKnob(self.setFrameRange)
201
+        self.setFrameRange.clearFlag(nuke.STARTLINE)
202
+
203
+        # filepath
204
+        self.arnoldBinPath = nuke.File_Knob("arnoldBinPath", " Arnold Bin Path ")
205
+        self.addKnob(self.arnoldBinPath)
206
+        self.arnoldBinPath.setValue('$env(PROJECT_ROOT_3D)/000_env/arnold/_noice/bin/')
207
+        self.arnoldBinPath.setTooltip('Please be sure to select the bin folder with a "/" at the end.')
208
+
209
+        # div
210
+        self.div4 = nuke.Text_Knob("div4", " ", "")
211
+        self.addKnob(self.div4)
212
+
213
+        # render mode
214
+        self.renderChoice = nuke.Enumeration_Knob('renderChoice', 'Render On', ['muster', 'local_machine'])
215
+        self.renderChoice.setTooltip('local_machine: creates a .bat file with all frames, saves it to the temp directory and runs it on the local machine.\nmuster: sends the job to muster.')
216
+        self.addKnob(self.renderChoice)
217
+
218
+        # muster pool
219
+        self.musterPool = nuke.String_Knob("musterPool", "Muster Pool")
220
+        self.musterPool.setValue(POOL)
221
+        self.addKnob(self.musterPool)
222
+
223
+        # muster pool
224
+        self.musterFolder = nuke.String_Knob("musterFolder", "Muster Folder ID")
225
+        self.musterFolder.setValue(MUSTERFOLDER)
226
+        self.addKnob(self.musterFolder)
227
+
228
+        # div
229
+        self.div5 = nuke.Text_Knob("div5", " ", "")
230
+        self.addKnob(self.div5)
231
+
232
+        # window
233
+        height = len(self.layerKnobs) * 20
234
+        width = 800
235
+        #if height > 1200:
236
+        #    width = 700
237
+        #    height = 1200
238
+        self.setMinimumSize(width, height)
239
+        #self.setMaximumSize(800, 1050)
240
+
241
+
242
+    def knobChanged(self, knob):
243
+        if knob.name() == "select_all":
244
+            for layer in self.layerKnobs:
245
+                layer.setValue(True)
246
+
247
+        if knob.name() == "invert_selection":
248
+            selected = []
249
+            for layer in self.layerKnobs:
250
+                if layer.value() is True:
251
+                    selected.append(layer)
252
+                    layer.setValue(False)
253
+            inverse = list(set(self.layerKnobs).difference(set(selected)))
254
+            for check in inverse:
255
+                check.setValue(True)
256
+
257
+        if knob.name() == "clear_selection":
258
+            for layer in self.layerKnobs:
259
+                layer.setValue(False)
260
+
261
+        if knob.name() == "setToInput":
262
+            selected = nuke.selectedNode()
263
+            fr = selected.frameRange()
264
+            start = fr.first()
265
+            end = fr.last()
266
+            self.startframe.setValue(start)
267
+            self.endframe.setValue(end)
268
+
269
+        if knob.name() == "setFrameRange":
270
+            self.startframe.setValue(int(nuke.root()['first_frame'].value()))
271
+            self.endframe.setValue(int(nuke.root()['last_frame'].value()))
272
+
273
+        if knob.name() == "setpath":
274
+            file = nuke.selectedNode().knobs()['file'].value().replace('%04d','####')
275
+            file = file.replace('####','denoise.####')
276
+            self.outputpath.setValue(file)
277
+
278
+        if knob.name() == "renderChoice":
279
+            if self.renderChoice.value() == "local_machine":
280
+                self.musterPool.setVisible(False)
281
+                self.musterFolder.setVisible(False)
282
+            if self.renderChoice.value() == "muster":
283
+                self.musterPool.setVisible(True)
284
+                self.musterFolder.setVisible(True)
285
+
286
+
287
+        if knob.name() == "OK":
288
+            if self.renderChoice.value() == "local_machine":
289
+                print('Denoising locally......')
290
+                self.executeNoiceLocally()
291
+            if self.renderChoice.value() == "muster":
292
+                print('Denoising on Muster......')
293
+                self.sendToMuster()
294
+
295
+
296
+    def executeNoiceLocally(self):
297
+        # saving selected aovs
298
+        light_aov_names = []
299
+        for layer in self.layerKnobs:
300
+            if layer.value() is True:
301
+                light_aov_names.append(layer.name())
302
+
303
+        binpath = self.arnoldBinPath.evaluate().replace('\\','/')
304
+
305
+        filename_input_sequence_number = re.sub(r'#+', lambda m: r'{{:0{}d}}'.format(len(m.group(0))), self.inputpath.value().replace('%04d','####'))
306
+        filename_output_sequence_number = re.sub(r'#+', lambda m: r'{{:0{}d}}'.format(len(m.group(0))), self.outputpath.value().replace('%04d','####'))
307
+
308
+        command = ''
309
+        for i in range(self.startframe.value(), self.endframe.value() + 1):
310
+            light_aov_string = ""
311
+
312
+            for k in light_aov_names:
313
+                print('Denoice: Info. AOV: %s') % k
314
+                light_aov_string += "-aov " + str(k) + " "
315
+
316
+            command += '"' + binpath + '/noice.exe' + '"' + ' ' \
317
+                  + '-patchradius ' + str(self.patchradius.value()) + ' ' \
318
+                  + '-searchradius ' + str(self.searchradius.value()) + ' ' \
319
+                  + '-variance ' + str(self.variance.value()) + ' ' \
320
+                  + light_aov_string \
321
+                  + '-i ' + filename_input_sequence_number.format(i) + ' ' \
322
+                  + '-t ' + str(self.threads.value()) + ' ' \
323
+                  + '-ef ' + str(self.temporalFrames.value()) + ' ' \
324
+                  + '-output ' + filename_output_sequence_number.format(i)
325
+
326
+            print('_________________________________________________________________________________________________')
327
+            print('')
328
+            print('--------------------------------> Denoise Command for Frame %s <---------------------------') %i
329
+            print('_________________________________________________________________________________________________')
330
+            print('')
331
+            print('')
332
+            print('')
333
+
334
+            if i is not self.endframe.value():
335
+                command += "\n"
336
+        print(command)
337
+
338
+        temp = tempfile.NamedTemporaryFile(prefix='Noice_', suffix='.bat', delete=False)
339
+        temp.write(command)
340
+        print('Created temp file is:', temp.name)
341
+        temp.close()
342
+
343
+        filepath = 'cmd /c start "Denoice " "%s"' %temp.name
344
+        p = subprocess.call(filepath, shell=True, stdout=subprocess.PIPE)
345
+
346
+
347
+    def fixfilename(self,infile):
348
+
349
+        infile = infile.replace("p:/", "//hades/p/")
350
+        infile = infile.replace("P:/", "//hades/p/")
351
+        infile = infile.replace("o:/", "//calculon/o/")
352
+        infile = infile.replace("O:/", "//calculon/o/")
353
+        infile = infile.replace("/", "\\")
354
+
355
+        return infile
356
+
357
+
358
+    def sendToMuster(self):
359
+        # jobname
360
+        jobname = nuke.selectedNode().knobs()['file'].value().split("/")[-1].replace('.%04d.exr',"__denoise")
361
+        # aovs
362
+        # saving selected aovs
363
+        light_aov_names = []
364
+        light_aov_string = ""
365
+        for layer in self.layerKnobs:
366
+            if layer.value() is True:
367
+                light_aov_names.append(layer.name())
368
+        for k in light_aov_names:
369
+            print('Denoice: Info. AOV: %s') % k
370
+            light_aov_string += "-l " + str(k) + " "
371
+        light_aov_string = light_aov_string.rstrip()
372
+        light_aov_string = '"' + light_aov_string + '"'
373
+
374
+        a_bin_path = self.fixfilename(self.arnoldBinPath.evaluate())
375
+        a_input_path = self.fixfilename(self.inputpath.evaluate().replace('%04d','####'))
376
+        a_output_path = self.fixfilename(self.outputpath.evaluate().replace('%04d','####'))
377
+
378
+        # building Command
379
+        cmd = '%s -s %s -u %s -b -e %s -n %s' % (MRTOOL, MSTRSERVER, USERNAME, TEMPLATE, jobname)
380
+        cmd += ' -pk %i -pr %i -pool "%s" -parent %i' % (int(PSIZE), int(PRIO), self.musterPool.value(), int(self.musterFolder.value()))
381
+        cmd += ' -attr "NOICEBIN" %s 0' % a_bin_path
382
+        cmd += ' -attr "PATCHRADIUS" %i 0' % int(self.patchradius.value())
383
+        cmd += ' -attr "SEARCHRADIUS" %i 0' % int(self.searchradius.value())
384
+        cmd += ' -attr "VARIANCE" %s 0' % self.variance.value()
385
+        cmd += ' -sf %i' % int(self.startframe.value())
386
+        cmd += ' -ef %i' % int(self.endframe.value())
387
+        cmd += ' -bf %i' % 1
388
+        cmd += ' -f %s' % a_input_path
389
+
390
+        cmd += ' -attr "LIGHTAOVSTRING" %s 0' % light_aov_string
391
+        cmd += ' -attr "TEMPORALFRAMES" %i 0' % int(self.temporalFrames.value())
392
+        cmd += ' -attr "DENOISETHREADS" %i 0' % int(self.threads.value())
393
+        cmd += ' -attr "OUTPUTFILE" %s 0' % a_output_path
394
+
395
+        # send
396
+        print (cmd)
397
+        print (os.system(cmd))
398
+        return
399
+
400
+
401
+def addPanel():
402
+    try:
403
+        selected = nuke.selectedNode()
404
+    except:
405
+        nuke.message("Denoice: Error. Please select a Read node with EXR file")
406
+        return
407
+    if selected.Class() == "Read":
408
+        if os.path.splitext(selected["file"].value())[1] == ".exr":
409
+            launch_panel = denoicePanel()
410
+            launch_panel.showModalDialog()
411
+        else:
412
+            nuke.message("Denoice: Error. Please select a Read node with EXR file")
413
+    else:
414
+        nuke.message("Denoice: Error. Please select a Read node")

+ 210
- 0
pythonpluginsUI/denoice/muster_template.py.txt View File

@@ -0,0 +1,210 @@
1
+''' Muster 8 Generic script template class '''
2
+
3
+''' Import Muster base modules '''
4
+import MClientAPI
5
+import MTemplateAPI
6
+
7
+''' Import Python base lib modules '''
8
+import os.path
9
+import re
10
+
11
+''' Get the template manager singleton '''
12
+manager = MTemplateAPI.MManager.getUniqueClass()
13
+
14
+''' Define a new class derived from MTemplateAPI.MTemplate base class , override required methods '''
15
+
16
+
17
+class ShellTemplate(MTemplateAPI.MTemplate):
18
+    def __init__(self):
19
+        MTemplateAPI.MTemplate.__init__(self)
20
+        self.setID(2008)
21
+        self.setName("Denoise_Noice")
22
+        self.setDescription("Denoise with Noice")
23
+        self.setTemplateLogic(MTemplateAPI.kTemplateMultiframe)
24
+        self.setDefaultPriority(1)
25
+        self.setDefaultPools("arnold_16thread")
26
+        self.setDefaultExcludedPools("")
27
+        self.setMaximumLicenses(0)
28
+        self.setEnableAdditionalFlagsField(1)
29
+
30
+        ''' Submission form items allocation '''
31
+        NoiceBin = MTemplateAPI.MTemplateItemFolder("NOICEBIN", "Noice Bin Path", "Specifies the Noice bin Path", "", 0,
32
+                                                    1, 1)
33
+        patchRadius = MTemplateAPI.MTemplateItemInteger("PATCHRADIUS", "PatchRadius", "Specify the patch radius", 3, 0,
34
+                                                        0, 1)
35
+        searchRadius = MTemplateAPI.MTemplateItemInteger("SEARCHRADIUS", "PatchRadius", "Specify the patch radius", 9,
36
+                                                         0, 0, 1)
37
+        variance = MTemplateAPI.MTemplateItemDouble("VARIANCE", "Variance", "Specifies the variance", 0.25, 0, 0, 1)
38
+        lightAovString = MTemplateAPI.MTemplateItemString("LIGHTAOVSTRING", "Light AOV String",
39
+                                                          "Specifies the AOVs to be denoised. Syntax: -aov aovname1 -aov aovname2 -aov aovname3",
40
+                                                          "-aov rgba", 0, 0, 1)
41
+        temporalFrames = MTemplateAPI.MTemplateItemInteger("TEMPORALFRAMES", "Temporal Frames",
42
+                                                           "Specifies the temporal frames", 0, 0, 0, 1)
43
+        denoiseThreads = MTemplateAPI.MTemplateItemInteger("DENOISETHREADS", "Threads", "Specify the denoise threads",
44
+                                                           -2, 1, 0, 1)
45
+        startFrame = MTemplateAPI.MTemplateItemInteger("NOICESTARTFRAME", "Start frame",
46
+                                                       "Specifies the starting frame for the rendering job", 1001, 0, 0,
47
+                                                       1)
48
+        endFrame = MTemplateAPI.MTemplateItemInteger("NOICEENDFRAME", "End frame",
49
+                                                     "Specifies the ending frame for the rendering job", 1010, 0, 0, 1)
50
+        inputFile = MTemplateAPI.MTemplateItemFile("INPUTFILE", "Input File", "Specifies the File to be denoised", "",
51
+                                                   0, 1, 1, "*.exr")
52
+        outputFile = MTemplateAPI.MTemplateItemFile("OUTPUTFILE", "Output File", "Specifies the output File", "", 0, 1,
53
+                                                    1, "*.exr")
54
+
55
+        self.addSubmissionItem(NoiceBin)
56
+        self.addSubmissionItem(patchRadius)
57
+        self.addSubmissionItem(searchRadius)
58
+        self.addSubmissionItem(variance)
59
+        self.addSubmissionItem(lightAovString)
60
+        self.addSubmissionItem(temporalFrames)
61
+        self.addSubmissionItem(denoiseThreads)
62
+        self.addSubmissionItem(startFrame)
63
+        self.addSubmissionItem(endFrame)
64
+        self.addSubmissionItem(inputFile)
65
+        self.addSubmissionItem(outputFile)
66
+
67
+        ''' items mapping to Muster default tags '''
68
+        self.addMapping("INPUTFILE", "job_file")
69
+        self.addMapping("NOICESTARTFRAME", "start_frame")
70
+        self.addMapping("NOICEENDFRAME", "end_frame")
71
+
72
+        ''' Windows support '''
73
+        self.platformWindows.setPlatformEnabled(1)
74
+        self.platformWindows.setEnableErrorCheck(1)
75
+        self.platformWindows.setEnabledByDefault(1)
76
+        self.platformWindows.setDetectionLogic(MTemplateAPI.kProcessChild)
77
+        #self.platformWindows.setDetectionLogic(MTemplateAPI.kProcessDirect)
78
+        self.platformWindows.setDetectionLogicProcessName("noice.exe")
79
+
80
+        ''' OSX support '''
81
+        self.platformOSX.setPlatformEnabled(0)
82
+        self.platformOSX.setEnableErrorCheck(1)
83
+        self.platformOSX.setEnabledByDefault(1)
84
+        self.platformOSX.setDetectionLogic(MTemplateAPI.kProcessDirect)
85
+
86
+        applicationPath = MTemplateAPI.MTemplateItemFile("SHELLCMD", "Script interpreter executable",
87
+                                                         "Specifies the OS X script executable", "/bin/sh", 0, 0, 1,
88
+                                                         "*.exe")
89
+        self.platformOSX.addClientConfigurationItem(applicationPath)
90
+
91
+        ''' Linux support '''
92
+        self.platformLinux.setPlatformEnabled(0)
93
+        self.platformLinux.setEnableErrorCheck(1)
94
+        self.platformLinux.setEnabledByDefault(1)
95
+        self.platformLinux.setDetectionLogic(MTemplateAPI.kProcessDirect)
96
+
97
+        applicationPath = MTemplateAPI.MTemplateItemFile("SHELLCMD", "Script interpreter executable",
98
+                                                         "Specifies the Linux script executable", "/bin/sh", 0, 0, 1,
99
+                                                         "*.exe")
100
+        self.platformLinux.addClientConfigurationItem(applicationPath)
101
+
102
+    ''' virtual functions overrides '''
103
+
104
+    def onBuildCommandLine(self, platform, job, chunk, clientTemplatePreferences, instanceNum):
105
+        # sequence with sequence string bla.1001.exr
106
+        fileIn = job.attributeGetString(manager.resolveMappingToJob(self.getID(), "INPUTFILE"))
107
+        fileOut = job.attributeGetString(manager.resolveMappingToJob(self.getID(), "OUTPUTFILE"))
108
+
109
+        # regex
110
+        patternSeq = re.compile(r'\.\d\d\d+\.')
111
+
112
+        # extract frame substring
113
+        frame = patternSeq.findall(fileIn)
114
+        # if there are more matches due to non-conform naming
115
+        frame = frame[-1]
116
+        chunkFrame = "." + str(int(chunk.getStartFrame())) + "."
117
+
118
+        # replace padding with the startFrame of the chunk
119
+        fileIn = fileIn.replace(frame, chunkFrame)
120
+        fileOut = fileOut.replace(frame, chunkFrame)
121
+
122
+        renderCmd = ""
123
+        if platform == MClientAPI.kPlatformWindows:
124
+            renderCmd += "/c "
125
+            renderCmd += "\""
126
+            renderCmd += job.attributeGetString("NOICEBIN") + "noice.exe"
127
+            renderCmd += " -patchradius %s" % job.attributeGetInt("PATCHRADIUS")
128
+            renderCmd += " -searchradius %s" % job.attributeGetInt("SEARCHRADIUS")
129
+            renderCmd += " -variance %s" % job.attributeGetFloat("VARIANCE")
130
+            renderCmd += " " + job.attributeGetString("LIGHTAOVSTRING")  # '-aov aovname1 -aov aovname2'
131
+            renderCmd += " -i " + fileIn
132
+            renderCmd += " -t " + job.attributeGetString("DENOISETHREADS")
133
+            renderCmd += " -ef %s" % job.attributeGetInt("TEMPORALFRAMES")
134
+            renderCmd += " -output " + fileOut
135
+            renderCmd += "\""
136
+
137
+            addFlags = job.attributeGetString("ADD_FLAGS")
138
+            if len(addFlags) > 0:
139
+                renderCmd += " " + addFlags
140
+        return renderCmd
141
+
142
+    def onCheckLog(self, job, chunk, clientTemplatePreferences, log, warnings, errors, silencedWarnings,
143
+                   silencedErrors):
144
+        ## WARNINGS
145
+        noWarnings = []
146
+        foundset = []
147
+
148
+        for line in log.splitlines():
149
+            if line.lower().find("warning") != -1:
150
+                foundset.append(line)
151
+        # remove no warning strings
152
+        result = [x for x in foundset if not any(y in x for y in noWarnings)]
153
+        if len(result) != 0:
154
+            return MTemplateAPI.MTemplateError(2, "Warning keyword found",
155
+                                               MTemplateAPI.MTemplateError.kTemplateErrorTypeWarning)
156
+        ## ERRORS
157
+        if log.lower().find("error") != -1:
158
+            return MTemplateAPI.MTemplateError(1, "Error keyword found",
159
+                                               MTemplateAPI.MTemplateError.kTemplateErrorTypeError)
160
+        if log.lower().find("no licenses could be found to run this application") != -1:
161
+            return MTemplateAPI.MTemplateError(1, "No license available",
162
+                                               MTemplateAPI.MTemplateError.kTemplateErrorTypeError)
163
+        return MTemplateAPI.MTemplateError()
164
+
165
+    def onCheckLogLine(self, job, chunk, clientTemplatePreferences, line, lineNum, warnings, errors, silencedWarnings,
166
+                       silencedErrors):
167
+        return MTemplateAPI.MTemplateError()
168
+
169
+    def onBuildEnvironment(this, job, chunk, clientTemplatePreferences, existingEnvironment):
170
+        return existingEnvironment
171
+
172
+    def onGetApplicationPath(self, job, chunk, clientTemplatePreferences, pathOut):
173
+        platform = MClientAPI.GetPlatform()
174
+        if platform == MClientAPI.kPlatformWindows:
175
+            pathOut.setString("cmd.exe")
176
+        else:
177
+            pathOut.setString("/bin/sh")
178
+        return MTemplateAPI.MTemplateError()
179
+
180
+    def onGetApplicationStartingFolder(self, job, chunk, clientTemplatePreferences, pathOut):
181
+        pathStart = "c:\\Windows\\System32"
182
+        platform = MClientAPI.GetPlatform()
183
+        if platform == MClientAPI.kPlatformWindows:
184
+            #pathOut.setString(job.attributeGetString("NOICEBIN"))
185
+            pathOut.setString(pathStart)
186
+        else:
187
+            pass
188
+        return MTemplateAPI.MTemplateError()
189
+
190
+    def onCheckForSubframeAdvancingString(self, job, chunk, line):
191
+        return 0
192
+
193
+    def onCheckForSubframeProgress(self, job, chunk, line, progressOut):
194
+        return 0
195
+
196
+    def onCheckExitCode(self, job, chunk, clientTemplatePreferences, exitCode):
197
+        if exitCode != 0:
198
+            return MTemplateAPI.MTemplateError(exitCode, "%d Exit code different from expected 0 value" % exitCode,
199
+                                               MTemplateAPI.MTemplateError.kTemplateErrorTypeWarning)
200
+        return MTemplateAPI.MTemplateError()
201
+
202
+    def onApplicationFinder(self, moduleRegExp, moduleTag):
203
+        return MTemplateAPI.kTemplateScanNone
204
+
205
+    def onFindApplication(self, basePath, clientTemplatePreferences):
206
+        pass
207
+
208
+''' Create an instance of the template class and install it into the template manager context '''
209
+template = ShellTemplate()
210
+manager.installPyTemplate(template)

+ 185
- 0
pythonpluginsUI/nukeffmpeg/Preview_QT.nk View File

@@ -0,0 +1,185 @@
1
+set cut_paste_input [stack 0]
2
+version 12.2 v5
3
+push $cut_paste_input
4
+Group {
5
+ name Preview_QT
6
+ tile_color 0xbfbf00ff
7
+ label "h264 intra\n\[value nodeLabel]"
8
+ selected true
9
+ xpos 151
10
+ ypos 68
11
+ addUserKnob {20 ffmpeg}
12
+ addUserKnob {35 presets M {"Prores 422" "knobs this \{ffmpeg_args \"-c:v prores_ks -profile:v 2 -qscale:v 7 -pix_fmt yuv444p10le -r 24 -vf colormatrix=bt601:bt709 -vendor ap10 -metadata:s encoder=\\\"Apple ProRes 422\\\"\" framerate 24 label \"Prores 422\\n\[value nodeLabel]\"\}" "Prores 422 HQ" "knobs this \{ffmpeg_args \"-c:v prores_ks -profile:v 3 -qscale:v 7 -pix_fmt yuv444p10le -r 24 -vf colormatrix=bt601:bt709 -vendor ap10 -metadata:s encoder=\\\"Apple ProRes 422 HQ\\\"\" framerate 24 label \"Prores 422 HQ\\n\[value nodeLabel]\"\}" "Prores 4444" "knobs this \{ffmpeg_args \"-c:v prores_ks -profile:v 4 -qscale:v 5 -pix_fmt yuv444p10le -r 24 -vf colormatrix=bt601:bt709 -vendor ap10 -metadata:s encoder=\\\"Apple ProRes 4444\\\"\\\"\" framerate 24 label \"Prores 4444\\n\[value nodeLabel]\"\}" "DNxHD 36" "knobs this \{ffmpeg_args \"-c:v dnxhd -profile:v dnxhd -pix_fmt yuv422p -b:v 36M -vf \\\"scale=1920:1080,fps=24000/1001,colormatrix=bt601:bt709\\\"\" framerate 23.976 label \"DNxHD 36\\n\[value nodeLabel]\"\}" "DNxHD 115" "knobs this \{ffmpeg_args \"-c:v dnxhd -profile:v dnxhd -pix_fmt yuv422p -b:v 115M -vf \\\"scale=1920:1080,fps=24000/1001,colormatrix=bt601:bt709\\\"\" framerate 23.976 label \"DNxHD 115\\n\[value nodeLabel]\"\}" "DNxHD 175" "knobs this \{ffmpeg_args \"-c:v dnxhd -profile:v dnxhd -pix_fmt yuv422p -b:v 175M -vf \\\"scale=1920:1080,fps=24000/1001,colormatrix=bt601:bt709\\\"\" framerate 23.976 label \"DNxHD 175\\n\[value nodeLabel]\"\}" "DNxHD 175 10bit" "knobs this \{ffmpeg_args \"-c:v dnxhd -profile:v dnxhd -pix_fmt yuv422p10 -b:v 175M -vf \\\"scale=1920:1080,fps=24000/1001,colormatrix=bt601:bt709\\\"\" framerate 23.976 label \"DNxHD 175 10bit\\n\[value nodeLabel]\"\}" "DNxHD 220 10bit" "knobs this \{ffmpeg_args \"-c:v dnxhd -profile:v dnxhd -pix_fmt yuv422p10 -b:v 220M -vf \\\"scale=1920:1080,fps=24000/1001,colormatrix=bt601:bt709\\\"\" framerate 23.976 label \"DNxHD 220 10bit\\n\[value nodeLabel]\"\}" "DNxHR HQ" "knobs this \{ffmpeg_args \"-c:v dnxhd -profile:v dnxhr_hq -pix_fmt yuv422p -vf \\\"colormatrix=bt601:bt709\\\"\" framerate 24 label \"DNxHR HQ\\n\[value nodeLabel]\"\}" "DNxHR HQX" "knobs this \{ffmpeg_args \"-c:v dnxhd -profile:v dnxhr_hqx -pix_fmt yuv422p10le -vf \\\"colormatrix=bt601:bt709\\\"\" framerate 24 label \"DNxHR HQX\\n\[value nodeLabel]\"\}" "DNxHR 444" "knobs this \{ffmpeg_args \"-c:v dnxhd -profile:v dnxhr_444 -pix_fmt yuv444p10le -vf \\\"colormatrix=bt601:bt709\\\"\" framerate 24 label \"DNxHR 444\\n\[value nodeLabel]\"\}" "h264 standard" "knobs this \{ffmpeg_args \"-c:v libx264 -profile:v high -crf 15 -preset slow -tune film -pix_fmt yuv420p -g 4 -bf 2 -vf colormatrix=bt601:bt709\" framerate 24 label \"h264 standard\\n\[value nodeLabel]\"\}" "h264 intra" "knobs this \{ffmpeg_args \"-c:v libx264 -profile:v high -crf 12 -preset slow -tune film -pix_fmt yuv420p -g 1 -bf 0 -vf colormatrix=bt601:bt709\" framerate 24 label \"h264 intra\\n\[value nodeLabel]\"\}" "h264 intra 444" "knobs this \{ffmpeg_args \"-c:v libx264 -profile:v high444 -crf 12 -preset slow -tune film -pix_fmt yuv444p -g 1 -bf 0 -vf colormatrix=bt601:bt709\" framerate 24 label \"h264 intra 444\\n\[value nodeLabel]\"\}" "h264 intra 444 10bit" "knobs this \{ffmpeg_args \"-c:v libx264 -profile:v high444 -crf 12 -preset slow -tune film -pix_fmt yuv444p10le -g 1 -bf 0 -vf colormatrix=bt601:bt709\" framerate 24 label \"h264 intra 444 10bit\\n\[value nodeLabel]\"\}" "h265 standard 10bit" "knobs this \{ffmpeg_args \"-c:v libx265 -profile:v main10 -crf 15 -preset slow -tune psnr -pix_fmt yuv420p10le -g 2 -bf 0 -vf colormatrix=bt601:bt709\" framerate 24 label \"h265 standard 10bit\\n\[value nodeLabel]\"\}" "h265 intra 422 10bit" "knobs this \{ffmpeg_args \"-c:v libx265 -profile:v main422-10-intra -crf 12 -preset slow -tune psnr -pix_fmt yuv422p10le -g 1 -bf 0 -vf colormatrix=bt601:bt709\" framerate 24 label \"h265 intra 10bit\\n\[value nodeLabel]\"\}" "h265 intra 444 10bit" "knobs this \{ffmpeg_args \"-c:v libx265 -profile:v main444-10-intra -crf 12 -preset slow -tune psnr -pix_fmt yuv444p10le -g 1 -bf 0 -vf colormatrix=bt601:bt709\" framerate 24 label \"h265 intra 444 10bit\\n\[value nodeLabel]\"\}"}}
13
+ addUserKnob {1 ffmpeg_args l "ffmpeg args"}
14
+ ffmpeg_args "-c:v libx264 -profile:v high -crf 12 -preset slow -tune film -pix_fmt yuv420p -g 1 -bf 0 -vf colormatrix=bt601:bt709"
15
+ addUserKnob {26 ""}
16
+ addUserKnob {1 info t "Additional non-mandatory File Info"}
17
+ addUserKnob {2 file t "output file"}
18
+ file "\$env(PROJECT_ROOT_3D)/550_previewQTs/HOT_comp_001_010.comp_v001/HOT_comp_001_010.comp_v001.mov"
19
+ addUserKnob {22 setfilepath l "Set Filepath" t "Creates a valid filename.\nUses the main task of SceneControl and points to the PreviewQT Dir." T "n = nuke.thisNode()\ninfo = n\['info'].value()\nffmpeg_args = n\['ffmpeg_args'].value()\n\nif 'dnxhd' in ffmpeg_args:\n    extension = 'mov'\nif 'libx264' in ffmpeg_args:\n    extension = 'mp4'\nif 'libx265' in ffmpeg_args:\n    extension = 'mp4'\nif 'prores' in ffmpeg_args:\n    extension = 'mov'\n\noutputpath = sceneControl.kenvQuery('PREVIEW_QT_FILE', minor_version=None, info=info, ext=extension)\n\nn.knobs()\[\"file\"].setValue(outputpath)\n\n# create label for displaying the generated output\n# autoLabel = \"n.name() + '\\\\n' +n\['file'].value().split('/')\[-1] +  '\\\\n' + n\['label'].value()\"\n# n\['autolabel'].setValue(autoLabel)" +STARTLINE}
20
+ addUserKnob {26 ""}
21
+ addUserKnob {1 burnNameText l burn-in}
22
+ burnNameText "\[python \{nuke.thisNode().knobs()\['file'].value().split('/')\[-1].replace('.mp4','').replace('.mov','')\}]"
23
+ addUserKnob {20 config n 1}
24
+ config 0
25
+ addUserKnob {1 ffmpeg_cmd l command t "Enter the path to the ffmpeg executable. \nWindows:\nffmpeg.exe uses the ffmpeg provided with the nuke tool.\n\nLinux/MacOS (untested!)\nffmpeg without a path will use the environment."}
26
+ ffmpeg_cmd ffmpeg.exe
27
+ addUserKnob {1 nodeLabel l label t "used for node labelling."}
28
+ nodeLabel "\[python \{nuke.thisNode().knobs()\['file'].value().split('/')\[-1]\}]"
29
+ addUserKnob {1 helper t "Expression string if you want to derive the burn-in via expression from SceneControl config."}
30
+ helper "\[value sceneCtrl.project]_\[value sceneCtrl.dept]_\[value sceneCtrl.sequence]_\[value sceneCtrl.shot].\[value sceneCtrl.task]_v\[format %03s \[value sceneCtrl.major_version]]"
31
+ addUserKnob {1 helper2 l helper t "Default string for burn-in name."}
32
+ helper2 "\[python \{nuke.thisNode().knobs()\['file'].value().split('/')\[-1].replace('.mp4','').replace('.mov','')\}]"
33
+ addUserKnob {7 fontScale l "font scale"}
34
+ fontScale 0.25
35
+ addUserKnob {20 endGroup_defaults l "endGroup Defaults" n -1}
36
+ addUserKnob {6 burnName l "Burn-in Name" +STARTLINE}
37
+ burnName true
38
+ addUserKnob {6 burnFrame l "Burn-in Frame" -STARTLINE}
39
+ burnFrame true
40
+ addUserKnob {26 ""}
41
+ addUserKnob {7 framerate R 23 30}
42
+ framerate {{"\[python \{nuke.root().knobs()\['fps'].value()\}]"}}
43
+ addUserKnob {1 framerange -STARTLINE}
44
+ framerange 1001-1109
45
+ addUserKnob {22 set_framerange l "Set to Input" -STARTLINE T "n = nuke.thisNode()\nfr = n.frameRange()\nn\['framerange'].setValue(\"\{0\}-\{1\}\".format(fr.first(), fr.last()))"}
46
+ addUserKnob {22 set_sc_framerange l "Set Framerange" t "Set framerange from SceneControl." -STARTLINE T "c = nuke.exists(\"sceneCtrl\")\nif c:\n    sc = nuke.toNode('sceneCtrl')\n    \n    n = nuke.thisNode()\n    n\['framerange'].setValue(\"\{0\}-\{1\}\".format(int(sc.knobs()\['fStart'].getValue()), int(sc.knobs()\['fEnd'].getValue())))\nelse:\n    print('No SceneControl found.')\n"}
47
+ addUserKnob {22 render l Render t "Saves your scene and renders.\nSee the progress in the Nuke Console Window." T "import ffmpeg_render\nffmpeg_render.prep()" +STARTLINE}
48
+ addUserKnob {20 help_1 l Help}
49
+ addUserKnob {26 helptext l "Help     " T "1. choose preset\n\n2. set info if needed\n\n3. <Set Filepath>\n\n4. enable or disable burn ins\n\n5. <Render>\n\n6. Look at the Nuke Console for progress\n"}
50
+}
51
+ Input {
52
+  inputs 0
53
+  name Input
54
+  xpos -40
55
+  ypos -112
56
+ }
57
+ Dot {
58
+  name Dot66
59
+  xpos -6
60
+  ypos -16
61
+ }
62
+set N7f514400 [stack 0]
63
+ Text2 {
64
+  font_size_toolbar 100
65
+  font_width_toolbar 100
66
+  font_height_toolbar 100
67
+  message "\[value parent.burnNameText]"
68
+  old_message {{72 79 84 95 99 111 109 112 95 48 48 49 95 48 49 48 46 99 111 109 112 95 118 48 48 49}
69
+    }
70
+  old_expression_markers {{0 25}
71
+    }
72
+  box {15 5 {box.x+600 x1001 525} 100}
73
+  yjustify bottom
74
+  transforms {{0 2}
75
+    }
76
+  cursor_position 27
77
+  font {{ Arial : Regular : arial.ttf : 0 }}
78
+  global_font_scale {{"\[value parent.fontScale]"}}
79
+  scale {1 1}
80
+  cursor_initialised true
81
+  autofit_bbox false
82
+  initial_cursor_position {{27.5 118.5}
83
+    }
84
+  group_animations {{0} imported: 0 selected: items: "root transform/"}
85
+  animation_layers {{1 11 1024 778 0 0 1 1 0 0 0 0}
86
+    }
87
+  name Text_SHOTNAME1
88
+  xpos -163
89
+  ypos -19
90
+ }
91
+push $N7f514400
92
+ Switch {
93
+  inputs 2
94
+  which {{"\[value parent.burnName]"}}
95
+  name switch_name
96
+  xpos -40
97
+  ypos 41
98
+ }
99
+ Dot {
100
+  name Dot1
101
+  xpos -6
102
+  ypos 79
103
+ }
104
+set N434f7400 [stack 0]
105
+ Text2 {
106
+  font_size_toolbar 100
107
+  font_width_toolbar 100
108
+  font_height_toolbar 100
109
+  message "\[format %04s \[frame]]"
110
+  old_message {{48 48 48 49}
111
+    }
112
+  old_expression_markers {{0 3}
113
+    }
114
+  box {{width-15-500} 5 {width-15} 100}
115
+  xjustify right
116
+  yjustify bottom
117
+  transforms {{0 2}
118
+    }
119
+  font_size_values {{0 100 1 100 2 100 3 100}
120
+    }
121
+  baseline_values {{0 0 1 0 2 0 3 0}
122
+    }
123
+  cursor_position 21
124
+  font {{ Arial : Regular : arial.ttf : 0 }}
125
+  global_font_scale {{"\[value parent.fontScale]"}}
126
+  scale {1 1}
127
+  cursor_initialised true
128
+  autofit_bbox false
129
+  initial_cursor_position {{1876.5 146.5}
130
+    }
131
+  group_animations {{0} imported: 0 selected: items: "root transform"}
132
+  animation_layers {{1 11 1024 778 0 0 1 1 0 0 0 0}
133
+    }
134
+  name Text_FRAMECOUNT1
135
+  xpos -164
136
+  ypos 76
137
+ }
138
+push $N434f7400
139
+ Switch {
140
+  inputs 2
141
+  which {{"\[value parent.burnFrame]"}}
142
+  name switch_frame
143
+  xpos -40
144
+  ypos 136
145
+ }
146
+ Write {
147
+  raw true
148
+  file_type tiff
149
+  datatype "16 bit"
150
+  compression none
151
+  checkHashOnRead false
152
+  version 486
153
+  name write_tmp
154
+  selected true
155
+  xpos -40
156
+  ypos 246
157
+  addUserKnob {20 keller l Keller}
158
+  addUserKnob {4 subtask t "Defaults to the Task selected in sceneControl, ignoring Element and Info.\nAll other subtasks are pre-renders and get rendered into the precomp (=prerender) directory." M {scriptTask denoise key neutralgrade prerender stabilize}}
159
+  addUserKnob {22 w_reload l Reload t "Reload Tasks" -STARTLINE T sceneControl.kenvWriteReloadSubTasks(nuke.thisNode())}
160
+  addUserKnob {1 element l Element t "Optional Specifier. For example \[BG]_denoise. \[FG]_denoise."}
161
+  addUserKnob {1 info l Info t "Optional Info before Framenumber.\nPRO_comp3d_001_010.comp3d_v008.\[acescg].%04d.exr"}
162
+  addUserKnob {22 set l Set t "Set Output Path of this node" T sceneControl.kenvWriteSetPath(nuke.thisNode()) +STARTLINE}
163
+  addUserKnob {26 divider1 l "" +STARTLINE}
164
+  addUserKnob {6 ignore l "ignore in sceneControl" -STARTLINE}
165
+  ignore true
166
+  addUserKnob {26 divider2 l "" +STARTLINE}
167
+  addUserKnob {22 playinrv l "Play in RV" t "Opens new instance of RV" T playInRV.playInRV(nuke.thisNode().knob('file').getValue(),0) +STARTLINE}
168
+  addUserKnob {22 pushtorv l "Push to RV" t "Push to tagged RV. Will open RV and uses this instance to directly push sequences." -STARTLINE T playInRV.playInRV(nuke.thisNode().knob('file').getValue(),1)}
169
+  addUserKnob {22 pushtorvappend l "Push to RV (append)" t "Push to RV and append to existing sources" -STARTLINE T playInRV.playInRV(nuke.thisNode().knob('file').getValue(),2)}
170
+  addUserKnob {26 divider3 l "" +STARTLINE}
171
+  addUserKnob {22 explore l Explore t "Open Folder" -STARTLINE T exploreThis.exploreThis()}
172
+ }
173
+ Output {
174
+  name Output
175
+  xpos -40
176
+  ypos 355
177
+ }
178
+ StickyNote {
179
+  inputs 0
180
+  name StickyNote1
181
+  label keep_name!
182
+  xpos 60
183
+  ypos 253
184
+ }
185
+end_group

BIN
pythonpluginsUI/nukeffmpeg/ffmpeg.exe (Stored with Git LFS) View File

2
+oid sha256:cf19f79b8ee6bc1c83438ea8f6d266496681f686cc87bf1d295126621700a1eb
3
+size 112424960

BIN
pythonpluginsUI/nukeffmpeg/ffmpeg_dependencies.zip View File


+ 160
- 0
pythonpluginsUI/nukeffmpeg/ffmpeg_render.py View File

@@ -0,0 +1,160 @@
1
+from __future__ import with_statement
2
+from __future__ import print_function
3
+from __future__ import division
4
+import os, sys
5
+import subprocess, shlex
6
+import argparse
7
+import tempfile
8
+import numpy as np
9
+import nuke
10
+import tifffile
11
+
12
+
13
+class ffmpeg_render(KellerNukePlugin):
14
+
15
+    def configurePlugin(self):
16
+        workgroup = os.getenv('NUKE_PATH')
17
+        nk = os.path.join(workgroup, "pythonpluginsUI/nukeffmpeg", "Preview_QT.nk")
18
+        nk = nk.replace('\\','/')
19
+        nk = "\"" + nk + "\""
20
+        menubar = nuke.menu("Nuke")
21
+        self.m = menubar.addMenu("&Render")
22
+        self.m.addCommand('FFmpeg Render', 'nuke.nodePaste({nk})'.format(nk=nk),shortcut='F6')
23
+
24
+
25
+    def unconfigurePlugin(self):
26
+        self.m.removeItem("FFmpeg Render")
27
+
28
+
29
+from sys import platform as __platform
30
+if __platform == "linux" or __platform == "linux2":
31
+    _platform = 'linux'
32
+elif __platform == "darwin":
33
+    _platform = 'osx'
34
+elif __platform == "win32":
35
+    _platform = 'win'
36
+
37
+
38
+def frames_to_tc(total_frames, frame_rate):
39
+    fps_int = int(round(frame_rate))
40
+    smpte_token = ":"
41
+    hours = int(total_frames / (3600 * fps_int))
42
+    minutes = int(total_frames / (60 * fps_int) % 60)
43
+    seconds = int(total_frames / fps_int % 60)
44
+    frames = int(total_frames % fps_int)
45
+    return "%02d:%02d:%02d%s%02d" % (hours, minutes, seconds, smpte_token, frames)
46
+
47
+def terminal_render():
48
+    parser = argparse.ArgumentParser(description='Render from Nuke to ffmpeg.')
49
+    parser.add_argument("nuke_script",
50
+                        help="Nuke script to render.")
51
+    parser.add_argument("-X", "--write",
52
+                        help="Name of the WriteFFMPEG node to render.")
53
+    parser.add_argument("-F", "--framerange",
54
+                        help="framerange to render. Please specify <start>-<end>.",
55
+                        required=False)
56
+    parser.add_argument("-o", "--output",
57
+                        help="Output qt to render to. Will use the value of the file knob on the WriteFFMPEG node if not specified.",
58
+                        required=False)
59
+    args = parser.parse_args()
60
+    nuke_script = args.nuke_script
61
+    nuke.scriptOpen(nuke_script)
62
+    node = nuke.toNode(args.write)
63
+    node.begin()
64
+    write = nuke.toNode('write_tmp')
65
+    if args.framerange and "-" in args.framerange:
66
+        fr = nuke.FrameRange()
67
+        fr.setLast(int(args.framerange.split('-')[-1]))
68
+        fr.setFirst(int(args.framerange.split('-')[0]))
69
+    else:
70
+        node_framerange = node['framerange'].getValue()
71
+        if node_framerange and "-" in node_framerange:
72
+            fr = nuke.FrameRange()
73
+            fr.setLast(int(node_framerange.split('-')[-1]))
74
+            fr.setFirst(int(node_framerange.split('-')[0]))
75
+        else:
76
+            fr = node.frameRange()
77
+
78
+    tmpimg = tempfile.mkstemp('.tiff', "ffmpeg_temp_")[1]
79
+    if _platform == 'win':
80
+        write['file'].setValue(tmpimg.replace("\\","/"))
81
+    else:
82
+        write['file'].setValue(tmpimg)
83
+    framerate = node['framerate'].getValue()
84
+    output = node['file'].getValue()
85
+
86
+    # create folder if necessary
87
+    dir = os.path.dirname(output)
88
+    osdir = nuke.callbacks.filenameFilter(dir)
89
+    try:
90
+        os.makedirs(osdir)
91
+    except OSError:
92
+        pass
93
+
94
+    tc = frames_to_tc(fr.first(), framerate)
95
+    ffmpeg_args = "ffmpeg -hide_banner -loglevel info -y \
96
+        -f rawvideo -pixel_format rgb48le -video_size {0}x{1} \
97
+        -framerate {2} -i pipe:0 -timecode {3} {4} {5}".format(
98
+            node.width(), node.height(), framerate, tc,
99
+            node['ffmpeg_args'].getValue(), output)
100
+    print(ffmpeg_args)
101
+    ffproc = subprocess.Popen(
102
+        shlex.split(ffmpeg_args),
103
+        stdin=subprocess.PIPE,
104
+        stdout=subprocess.PIPE
105
+        )
106
+    for i, f in enumerate(fr):
107
+        nuke.execute(write, f, f)
108
+        print("Rendering frame \t{0} of {1}".format(i, fr.frames()))
109
+        # tifffile usage
110
+        img = tifffile.imread(tmpimg)
111
+        # numpy usage
112
+        img.tofile(ffproc.stdin)
113
+
114
+    if _platform == 'win':
115
+        try:
116
+            os.remove(tmpimg.replace("\\","/"))
117
+        except:
118
+            pass
119
+    else:
120
+        try:
121
+            os.remove(tmpimg)
122
+        except:
123
+            pass
124
+    result, error = ffproc.communicate()
125
+    print("Rendering done.")
126
+
127
+if __name__=="__main__":
128
+    terminal_render()
129
+
130
+
131
+def prep():
132
+    nuke.scriptSave()
133
+    node = nuke.thisNode()
134
+    ffpy = __file__
135
+    ffpy = ffpy.replace('pyc', 'py')
136
+    node_framerange = node['framerange'].getValue()
137
+
138
+    nk_cmd = "{0} -t {1} {2} --write {3} --output {4}".format(
139
+        "\"" + nuke.EXE_PATH + "\"",
140
+        ffpy,
141
+        nuke.root().knob("name").value(),
142
+        node.fullName(),
143
+        node['file'].getValue())
144
+    print("RENDER COMMAND:\n\t{0}".format(nk_cmd))
145
+    if _platform == "win":
146
+        print (os.system(nk_cmd))
147
+        return
148
+    if _platform == "osx":
149
+        cmd = '''osascript 2>/dev/null <<EOF
150
+                tell application "Terminal"
151
+                  if not (exists window 1) then reopen
152
+                  activate
153
+                  do script "{0}"
154
+                end tell
155
+                EOF'''.format(nk_cmd)
156
+    elif _platform == "linux":
157
+        # cmd = 'xterm -e "bash {0}"'.format(nk_cmd)
158
+        cmd = 'gnome-terminal -e "bash -c \\"{0}; exec bash\\""'.format(nk_cmd)
159
+        #cmd = 'gnome-terminal -e "bash -c \\"{0}\\""'.format(nk_cmd)
160
+    subprocess.Popen(cmd, shell=True)

Loading…
Cancel
Save