通过python有两种方式读取图像的像素数据:
- 创建分析图像的节点(比如,MinColor, CurveTool),执行,然后读取其结果的knob数值
- sample方法可以直接抽样节点的像素。
使用CurveTool
创建CurveTool节点,执行后删除,并显示其分析结果:
noise = nuke.createNode('Noise')
noise['zoffset'].setExpression( 'frame*100 ' )
ct = nuke.createNode('CurveTool')
nuke.execute( ct, nuke.frame(), nuke.frame() )
dat = ct['intensitydata'].value()
nuke.delete(ct)
nuke.message( 'average luminance is %s' % dat )
使用sample方法
创建Noise节点,在x=500,y=600抽样红色通道,并输出:
noise = nuke.createNode('Noise')
print noise.sample('red', 500, 600)
下面代码结果一样:
print nuke.sample(noise, 'red', 500, 600)
例子
getMinMax
此工具可以抽取通道中的最大最小值。最近nuke的python api中没有现成的方法,一般利用MinColor的功能来实现。因此工作流中创建一
些临时节点,在当前帧上执行(为我们计算),然后删除。
刚开始,先创建一个测试方案:创建Ramp节点,将Grade节点挂到上面。确保Grade的black clamp knob禁止,然后可以用任意值来测试lift和gain knob。
创建MinColor节点,挂到Grade节点,MinColor的channels knob设置成默认rgba就行,调整其他knob。当目标knob被设置为0时,在当前
帧执行。输入节点的rgba层的delta knob保存了最小值。delta knob的值和你在Grade节点的lift knob的值很接近,但为负的。
想获取最大值,那么反转图像先,使用MinColor节点再走一边上面的流程。
反转图像意味着像素值从1到1-x,需要给delta knob加上1才能获得原始图像的最大值(方法很多)
现在知道怎么获取最大最小值。删除MinColor,反转节点,写一段脚本来干此事。
先从DAG图中抓取Grade1节点,然后添加MinColor节点,其target knob通过python设置为0:
srcNode = nuke.toNode('Grade1')
nuke.nodes.MinColor( target=0 )
其toNode()可以直接获取DAG中的节点。nuke.nodes可以替代nuke.createNode()方法。不同之处在于createNode()和从菜单创建是一样
的。这意味着:
- 将选择的焦点切换到新节点
- 尝试链接新节点
- 自动给新节点定位
- 挂起当前视图缓冲区
- 打开默认panel控制面板
nuke.nodes就不是手工操作了,创建节点后,啥也不改变。我们要创建临时节点,后面还要删除,这样用户的节点图就不会因为临时节点
改变。麻烦就是需要手工链接新节点:
srcNode = nuke.toNode('Grade1')
nuke.nodes.MinColor( target=0, inputs=[srcNode])
inputs参数接收一个列表,因为会有多输入。这样,可以一次将其全部链接。
现在需要赋一个参数来定义不需要分析的通道,同时我们不想限制到rgba上。将新节点赋值给变量,随后可以在代码中使用:
srcNode = nuke.toNode('Grade1')
channel = 'rgba.red'
MinColor = nuke.nodes.Mincolor( channels=channel, target=0, inputs=[srcNode])
上面的代码仅分析red通道。
现在创建新MinColor,并反转图像,确保链接对应的输入,并在对应的通道上工作(或许想删除前面的临时节点):
srcNode = nuke.toNode('Grade1')
channel = 'rgba.red'
MinColor = nuke.nodes.MinColor( channels = channel, target=0, inputs=[srcNode] )
Inv = nuke.nodes.Invert( channels = channel, inputs=[srcNode])
MaxColor = nuke.nodes.MinColor( channels = channel, target=0, inputs[Inv] )
结果如下:
现在,节点树已有,执行MinColor节点来获取我们所需。nuke.execute()来办事:
curFrame = nuke.frame()
nuke.execute( MinColor, curFrame, curFrame)
可以看到,需要给节点提供起始帧作为参数,可以用FrameRanges对象替代。在pixeldelta knob中可以找到最小颜色。记得取反才是正确值。
minV = -MinColor['pixeldelta'].value()
现在第二个MinColor干一样的事情,连接到Invert节点(我们存在了MaxColor里面),记得给结果加上1来取得真正的最大值:
nuke.execute(MaxColor, curFrame, curFrame)
maxV = MaxColor['pixeldelta'].value() + 1
现在是清理时间,让我们删除这些节点:
for n in ( MinColor, MaxColor, Inv ):
nuke.delete(n)
完成代码如下:
srcNode = nuke.toNode(' Grade1')
channel = 'rgba.red'
MinColor = nuke.nodes.MinColor( channels=channel, target=0, inputs=[srcNode] )
Inv = nuke.nodes.Invert( channels = channel, inputs= [srcNode])
MaxColor = nuke.nodes.MinColor( channels =channel, target=0, inputs=[Inv] )
curFrame = nuke.frame()
nuke.execute( MinColor, curFrame, curFrame)
minV = -MinColor['pixeldelta'].value()
nuke.execute( MaxColor, curFrame, curFrame)
maxV = MaxColor['pixeldelta'].value + 1
for n in ( MinColor, MaxColor, Inv):
nuke.delete(n)
将代码封装成函数,参数为node和channel,默认分析深度通道:
import nuke
def getMinMax( srcNode, channel='depth.Z' ):
'''
Return the min and max values of a given node's image as a tuple
args:
srcNode - node to analyse
channels - channels to analyse. This can either be a channel or layer name
'''
MinColor = nuke.nodes.MinColor( channels=channel, target=0, inputs=[srcNode] )
Inv = nuke.nodes.Invert( channels=channel, inputs=[srcNode])
MaxColor = nuke.nodes.MinColor( channels=channel, target=0, inputs=[Inv] )
curFrame = nuke.frame()
nuke.execute( MinColor, curFrame, curFrame )
minV = -MinColor['pixeldelta'].value()
nuke.execute( MaxColor, curFrame, curFrame )
maxV = MaxColor['pixeldelta'].value() + 1
for n in ( MinColor, MaxColor, Inv ):
nuke.delete( n )
return minV, maxV
将次模块放到插件路径下,用black 和white点创建Grade节点,并设置为深度通道的最大最小值,其会自动归一化:
minV, maxV = getMinMax(nuke.selectedNode())
grade =nuke.createNode('Grade')
grade['blackpoint'].setValue(minV)
grade['whitepoint'].setValue(maxV)
更加简洁的做法:
nuke.createNode('Grade', 'channels depth.Z blackpoint %s whitepoint %s' % examples.getMinMax(nuke.selectedNode()))
使用Sample
下面是一个LUT的例子:
import nuke
def getLUT( size=1024 ):
'''
Get the current viewer process node and generate a simple lut from it
args:
size - size of resulting lut (default=1024)
'''
vpNode = nuke.ViewerProcess.node()
vp = eval( 'nuke.nodes.%s()' % vpNode.Class() )
_copyKnobsFromScriptToScript( vpNode, vp )
ramp = nuke.nodes.Ramp()
ramp['p0'].setValue( (0, 0) )
ramp['p1'].setValue( (size, 0) )
vp.setInput(0, ramp )
saturation = nuke.nodes.Saturation( saturation = 0 )
saturation.setInput(0, vp )
lut = [ saturation.sample("rgba.red", i+.5, 0.5) for i in xrange( 0, size )]
nuke.delete( saturation )
nuke.delete( ramp )
nuke.delete( vp )
return lut
def createLutNode( lut ):
'''
Create a ColorLookup node to hold lut. The values are normalised.
args:
lut - list of floating point numbers
'''
lutNode = nuke.createNode( 'ColorLookup' )
lutKnob = lutNode['lut']
for i, y in enumerate( lut ):
x = float(i) / len(lut)
lutKnob.setValueAt( y, x )
def _copyKnobsFromScriptToScript( srcNode, trgNode):
'''
Copy knobs between nodes.
This function can also be found in the default menu.py
args:
srcNode - node to copy values from
trgNode - node to copy values to
'''
srcKnobs = srcNode.knobs()
trgKnobs = trgNode.knobs()
excludedKnobs = ["name", "xpos", "ypos"]
intersection = dict([ (item, srcKnobs[ item ]) for item in srcKnobs.keys() if item not in excludedKnobs and trgKnobs.has_key( item ) ])
for k in intersection.keys():
trgNode[ k ].fromScript( srcNode[ k ].toScript() )