2

我正在使用 NukeX9.0v8、Adobe Premiere Pro CC 2015 和 nukes 内部 python 中断器。

# Result: 2.7.3 (default, Jul 24 2013, 15:50:23) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]

我是一名 vfx 艺术家,我正试图围绕解析 xml 文件的最佳方法展开思考,以便:创建文件夹结构,批量创建 .nk comp 文件,并在我制作 .nk 文件时插入特定部分中的数据。 nk 补偿。我对如何孤立地做这些事情有一点了解,但是把它们放在一起,并试图找到关于这种复杂解析的教程,让我停了下来。

我知道这个范围很大,但任何小的建议都会受到赞赏。

现在我有一个 nuke comp,它有一个节点树,它接收相机输入并将它们拼接成 360 度视频的 latlong 图像,我将把它包装成一个小工具,用于每种不同类型的钻机配置。这只是简化了创建的 .nk 文件,并且我可以公开我可以将数据输入的小玩意的部分。

每天我们都会收到大量来自拍摄的素材,我们必须为每个镜头制作一个新的 .nk 合成并设置为立即渲染。我想做的是让现场的人创建一个首映项目并根据这个文件夹结构组织文件。该首映项目将导出为 .xml 文件。

首映时的结构设计。

  • Day_01(拍摄当天)
  • -^-R001(镜头的卷号。R指相机类型)
  • --^-R001_C001(镜头名称)
  • ---^-Acamera 剪辑(文件名路径,视频点为帧#)
  • ---^-Bcamera 剪辑(文件名路径,视频点为帧#)
  • ---^-Ccamera 剪辑(文件名路径,视频点为帧#)

现在,在 Nuke 内的脚本面板中,我可以输入要查找哪一天的 xml 在哪里的信息。然后假设查看卷的每个文件夹名称,并使用第一个字母(R 代表 RED 摄像机)并在里面查找剪辑文件夹。然后,它使用 pathurl 目录存储驱动器上的相机文件,并且还可以获取数据,如 xml 中存在的入点和出点。如果我更新缝合过程,我还需要输入模板版本的积分。这将告诉 nuke comp 使用哪个 gizmo。

这是我在 Nuke 中的面板。

def sesquixmlparse():
'''
This imports the xml file from premiere. It looks for the bin that it is working for today and starts looking in what is inside the bins
It then sees the bins inside and uses them to create nuke scripts with these as inputs
It asks what template version to use for the rig. things change or maybe even get better
'''

# Lets build the Nuke Panel that tells us our inputs
p = nuke.Panel("Sesqui XML Parse for Dailies")
xml_file = 'Daily XML'
daynumber = 'Day_##'
nk_output_dir = 'Directory to build VFX folder structure'
dnx_render_dir = 'Directory for write nodes'
r_template_vr = 'VER1'
g_template_vr = 'VER1'
c_template_vr = 'VER1'

p.addFilenameSearch("Daily XML", xml_file)
p.addSingleLineInput("Bin to process", daynumber)
p.addFilenameSearch("Directory to build VFX folder structure", nk_output_dir)
p.addFilenameSearch("Directory to render from write nodes", dnx_render_dir)
p.addSingleLineInput("3 Red stmap version", r_template_vr)
p.addSingleLineInput("6 Gopro stmap verison", g_template_vr)
p.addSingleLineInput("5 Canon stmap verison", c_template_vr)
p.setWidth(600)
print "Panel created"
if not p.show():
    return

# Assign var from nuke panel user-entered data
xml_file = p.value("Daily XML")
daynumber = p.value("Bin to process")
nk_output_dir = p.value("Directory to build VFX folder structure")
dnx_render_dir = p.value("Directory to render from write nodes")
r_template_vr = p.value("3 Red stmap version")
g_template_vr = p.value("6 Gopro stmap verison")
c_template_vr = p.value("5 Canon stmap verison")
print "var's assigned from panel"

# Create paths for render directory if it does not exist
if not os.path.isdir(dnx_render_dir):
    os.mkdir(dnx_render_dir)
    print  dnx_render_dir + " directory created"
if not os.path.isdir(nk_output_dir):
    os.mkdir(nk_output_dir)
    print nk_output_dir + " directory created"

我不知道如何最好地阅读 xml 文件。我看过的所有教程都非常基础DOMelementtree处理直接代码以读取已知的 XML 标记并将数据分解为简单的str输出。

我需要输入变量,然后将解析限制在树的特定部分,然后进入未知的层次结构设置并查看内部内容,然后决定如何处理找到的内容。

这是我的测试 XML 文件的示例。最终的计划是使用其他不同类型的卷筒来引用不同的摄影机类型,但现在我只使用 3 个摄影机红色装备。

这是一个非常大的文件,所以这里是一个 pastebin:http://pastebin.com/vLaRA0X8

基本上我想将脚本限制为在我的变量中查找<bin><name>'daynumber'</name>~~~~</bin>.在这种情况下,在Day_00bin 中查找。如果根层次结构中还有其他内容,我想将其作为序列忽略,未使用的剪辑和其他数据可能会变得非常非常大。然后我想daynumbernk_output_dir&中创建目录,dnx_render_dir以便拍摄当天的所有内容都包含在该文件夹中。

XML 文件中令人讨厌的部分是 bin 的名称是其<bin>自身的子项,因此一旦找到 bin 名称,<children>该 bin 中的任何一个都将与<name>. 我找不到定位标签的示例代码,然后寻找使用同一分支中的标签而不是它的子标签。

现在它已经找到了当天的垃圾箱,我希望它开始查找<children></children>. 例如,在我在&中<bin><name>R001</name>~~~</bin>创建的 Day_00 文件夹中为它在结构的这一部分中找到的每个 bin 创建目录。每次相机重新加载时,都会累积到 R002、R003 等。Gopros 等不同的相机类型也会创建 G001、G002、G003。nk_output_dirdnx_render_dir

然后我想在<children>上面的垃圾箱中查找并找到里面的所有垃圾箱,并在daynumber<bin><name>R001_C001</name>~~~</bin>中创建文件夹。这是用户创建的卷号和剪辑号。(R001_C001、R001_C002 等)这将是新剪辑名称、将生成的 .nk 压缩文件的名称以及写入节点上渲染的文件名。nk_output_dir\\~whatever bin this is contained~\~name of this bin~\

这里的目标是在我选择的目录中重新创建 bin 文件夹结构nk_output_dir

dnx_render_dir是为了稍后插入我的 nuke 脚本的写入节点到文件应该呈现到的位置。它是分开的,因为我有一个不同的 RAID 驱动器,它会随着它们填满而改变。渲染只需要放在一个目录中,daynumber\~rollnumber~但不需要限制到剪辑名称的文件夹中。

这是我真正迷路的地方。现在,因为我必须考虑用户错误,所以我不能完全确定我需要在树中走多深。我知道我想要<pathurl>~</pathurl>可以插入到我制作的 .nk (nuke) 脚本中的那个。对于红色相机文件,它们可以是直接在此处的 .R3D 文件,也可以是 2-3 个 bin 深度的文件夹结构。我知道我不能 100% 依赖片场的人在制作这个垃圾箱的方式上保持一致。

我可以信任他们做的就是确保它们按正确的字母顺序排列。如果您查看 xml,那么它们的顺序很重要。我也知道我是否正在查看我需要 3 个的 R### 滚动箱,<pathurl></pathurl>如果我正在查看 G### 内部,我需要 6 个,而对于 C###,我只需要 5 个。

它们的顺序很重要,因为它们可以重命名 `~~~~ 中的名称标签以重命名错误设置的摄像机,而无需重命名源文件。(这会破坏其他程序所需的重要元数据)

在树的这一部分中,我还想<clip id=~><in>###</in>抓住标记帧偏移量。如果摄像机不同步,可以设置它们的起点。但当然这个标签不是孩子的<pathurl></pathurl>,实际上是 3 父母!而且这个标签不会出现在每个剪辑上,所以我不能先寻找它!

   <clip id="masterclip-40" explodedTracks="true" frameBlend="FALSE">
    <uuid>85f87acc-308f-401e-bf82-55e8ea41e55a</uuid>
    <masterclipid>masterclip-40</masterclipid>
    <ismasterclip>TRUE</ismasterclip>
    <duration>5355</duration>
    <rate>
        <timebase>30</timebase>
        <ntsc>TRUE</ntsc>
    </rate>
    <in>876</in>
    <name>B002_C002_0216AM_002.R3D</name>
    <media>
    <video>
        <track>
            <clipitem id="clipitem-118" frameBlend="FALSE">
                <masterclipid>masterclip-40</masterclipid>
                <name>B002_C002_0216AM_002.R3D</name>
                <rate>
                    <timebase>30</timebase>
                    <ntsc>TRUE</ntsc>
                </rate>
                <alphatype>none</alphatype>
                <pixelaspectratio>square</pixelaspectratio>
                <anamorphic>FALSE</anamorphic>
                <file id="file-40">
                    <name>B002_C002_0216AM_002.R3D</name>
                    <pathurl>file://localhost/Volumes/REDLAB_3A/SESQUI/MASTER_FILES/DAY_00/RED/R002/B002/B002_0216G4.RDM/B002_C002_0216AM.RDC/B002_C002_0216AM_002.R3D</pathurl>

因此,一旦我解析了所有这些,我想要的信息就是。

  • .xml 文件中包含的 XML 的原始 bin 文件夹结构daynumber。取 bin 的名称并在nk_output_dir(Day_00/R001/R001_C001 等)中构建相同的文件夹结构
  • 我还想daynumber在文件夹中创建一个目录,dnx_render_dir并为每个引用相机胶卷的 bin 创建一个目录。
  • 根据剪辑名称是否以 R、G 或 CI 开头,希望能够访问该名称以选择制作哪种 .nk。
  • 我想要每个引用剪辑和插头的 bin 的 pathurl 信息。<in>如果该剪辑有任何信息,我也想要任何信息。这样我就可以将它插入到我的 nuke gizmo 的读取节点信息中。

我想一旦我弄清楚如何解析如此复杂的 xml 树,我就可以大惊小怪地摸索剩下的过程。

我真的很难找到解析这样一个复杂的 XML 文件的示例。

4

1 回答 1

0

每当遇到复杂的 XML 时,请考虑使用 XSLT 脚本将您的 XML 转换为更简单的结构。作为信息,XSLT是一种特殊用途的声明性语言(与 SQL 相同的类型),旨在将 XML 转换为各种结构以满足最终使用需求。与其他通用语言一样,Python 维护一个 XSLT 处理器,特别是在其lxml模块中。

虽然这种转换不能满足您的全部需求,但您可以解析更简单的结构以满足您的 Nuke 应用程序需求。目录和名称被简化并标记为 daynumber、rollnumber、shotnames 和带有 pathurl 的剪辑。

XSLT脚本(另存为 .xsl 或 .xslt 以在下面的 .py 脚本中引用)

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>

<xsl:key name="idkey" match="ctype" use="@id" />

  <xsl:template match="/">
    <root>
      <xsl:apply-templates select="*"/>
    </root>
  </xsl:template>

  <xsl:template match="xmeml/bin">
    <daynumber><xsl:value-of select="name"/></daynumber>
    <xsl:apply-templates select="children/bin"/>
  </xsl:template>

  <xsl:template match="xmeml/bin/children/bin">
    <roll>
      <rollnumber><xsl:value-of select="name"/></rollnumber>
      <rollnumberdir><xsl:value-of select="concat(ancestor::bin/name, 
                                                  '/', name)"/></rollnumberdir>
      <xsl:apply-templates select="children/bin"/>
    </roll>
  </xsl:template>

  <xsl:template match="xmeml/bin/children/bin/children/bin">
    <shot>
      <shotname><xsl:value-of select="name"/></shotname>
      <shotnamedir><xsl:value-of select="concat(/xmeml/bin/name, '/', 
                               /xmeml/bin/children/bin/name, '/', name)"/></shotnamedir>
      <xsl:apply-templates select="descendant::clip[position() &lt; 4]"/>
    </shot>
  </xsl:template>

  <xsl:template match="clip">
    <clip>
      <clipname><xsl:value-of select="descendant::name"/></clipname>
      <xsl:copy-of select="in"/>
      <pathurl><xsl:value-of select="descendant::pathurl"/></pathurl>
    </clip>
  </xsl:template>

</xsl:transform>

Python脚本 (转换、解析和导出更简单的结构)

#!/usr/bin/python
import lxml.etree as ET

# LOAD INPUT XML AND XSLT
dom = ET.parse('Input.xml'))
xslt = ET.parse('XSLTScript.xsl')

# TRANSFORM XML (SIMPLER NEWDOM CAN BE FURTHER PARSED: ITER(), FINDALL(), XPATH())
transform = ET.XSLT(xslt)
newdom = transform(dom)

# XPATH EXPRESSIONS (LIST OUTPUTS)
daynumber = newdom.xpath('//daynumber/text()')
# ['Day_00']
rolls = newdom.xpath('//rollnumber/text()')
# ['R001', 'R002']
shots = newdom.xpath('//shotname/text()')
# ['R001_C001', 'R002_C001', 'R002_C002']

# CONVERT TO STRING (IF NEEDED)
tree_out = ET.tostring(newdom, encoding='UTF-8', pretty_print=True,  xml_declaration=True)
print(tree_out.decode("utf-8"))

# OUTPUT TO FILE (IF NEEDED)
xmlfile = open('Output.xml'),'wb')
xmlfile.write(tree_out)
xmlfile.close()

TRANSFORMED XML (包含在 .py 脚本中的 newdom 对象中)

<?xml version='1.0' encoding='UTF-8'?>
<root>
  <daynumber>Day_00</daynumber>
  <roll>
    <rollnumber>R001</rollnumber>
    <rollnumberdir>Day_00/R001</rollnumberdir>
    <shot>
      <shotname>R001_C001</shotname>
      <shotnamedir>Day_00/R001/R001_C001</shotnamedir>
      <clip>
        <clipname>A002_C001_0216MW_001.R3D</clipname>
        <pathurl>file://localhost/Volumes/REDLAB_3A/SESQUI/MASTER_FILES/DAY_00/RED/R001/A002/A002_0216FE.RDM/A002_C001_0216MW.RDC/A002_C001_0216MW_001.R3D</pathurl>
      </clip>
      <clip>
        <clipname>A002_C001_0216MW_002.R3D</clipname>
        <pathurl>file://localhost/Volumes/REDLAB_3A/SESQUI/MASTER_FILES/DAY_00/RED/R001/A002/A002_0216FE.RDM/A002_C001_0216MW.RDC/A002_C001_0216MW_002.R3D</pathurl>
      </clip>
      <clip>
        <clipname>A002_C001_0216MW_003.R3D</clipname>
        <pathurl>file://localhost/Volumes/REDLAB_3A/SESQUI/MASTER_FILES/DAY_00/RED/R001/A002/A002_0216FE.RDM/A002_C001_0216MW.RDC/A002_C001_0216MW_003.R3D</pathurl>
      </clip>
    </shot>
  </roll>
  <roll>
    <rollnumber>R002</rollnumber>
    <rollnumberdir>Day_00/R002</rollnumberdir>
    <shot>
      <shotname>R002_C001</shotname>
      <shotnamedir>Day_00/R001/R002_C001</shotnamedir>
      <clip>
        <clipname>A003_C001_0216XI_001.R3D</clipname>
        <pathurl>file://localhost/Volumes/REDLAB_3A/SESQUI/MASTER_FILES/DAY_00/RED/R002/A003/A003_0216XO.RDM/A003_C001_0216XI.RDC/A003_C001_0216XI_001.R3D</pathurl>
      </clip>
      <clip>
        <clipname>B002_C001_02169H_002.R3D</clipname>
        <pathurl>file://localhost/Volumes/REDLAB_3A/SESQUI/MASTER_FILES/DAY_00/RED/R002/B002/B002_0216G4.RDM/B002_C001_02169H.RDC/B002_C001_02169H_002.R3D</pathurl>
      </clip>
      <clip>
        <clipname>C002_C001_02168R_001.R3D</clipname>
        <pathurl>file://localhost/Volumes/REDLAB_3A/SESQUI/MASTER_FILES/DAY_00/RED/R002/C002/C002_0216RL.RDM/C002_C001_02168R.RDC/C002_C001_02168R_001.R3D</pathurl>
      </clip>
    </shot>
    <shot>
      <shotname>R002_C002</shotname>
      <shotnamedir>Day_00/R001/R002_C002</shotnamedir>
      <clip>
        <clipname>C002_C002_0216M9_001.R3D</clipname>
        <pathurl>file://localhost/Volumes/REDLAB_3A/SESQUI/MASTER_FILES/DAY_00/RED/R002/C002/C002_0216RL.RDM/C002_C002_0216M9.RDC/C002_C002_0216M9_001.R3D</pathurl>
      </clip>
      <clip>
        <clipname>C002_C002_0216M9_002.R3D</clipname>
        <pathurl>file://localhost/Volumes/REDLAB_3A/SESQUI/MASTER_FILES/DAY_00/RED/R002/C002/C002_0216RL.RDM/C002_C002_0216M9.RDC/C002_C002_0216M9_002.R3D</pathurl>
      </clip>
      <clip>
        <clipname>C002_C002_0216M9_003.R3D</clipname>
        <pathurl>file://localhost/Volumes/REDLAB_3A/SESQUI/MASTER_FILES/DAY_00/RED/R002/C002/C002_0216RL.RDM/C002_C002_0216M9.RDC/C002_C002_0216M9_003.R3D</pathurl>
      </clip>
    </shot>
  </roll>
</root>
于 2016-03-03T01:45:49.773 回答