dayu_path——轻松处理序列帧文件路径

鬼猫猫原创
  • 开源库
  • Python
  • 开源
  • dayu
  • 大禹
  • 影视
  • Pipeline
大约 8 分钟

dayu_path——轻松处理序列帧文件路径

闲言碎语不要讲,本篇是开源系列正式第一篇,思来想去,决定把这个重要的首篇留给了 dayu_path

背景

我们在日常开发中经常面临路径、文件名处理的问题,比如:

  • 把某个文件夹下所有的·文件拿出来,有时需要一直递归下去
  • 三个平台(win、mac、linux)的路径转换
  • 提取出文件名中的文件类型(拓展名)
  • 提取出文件名中的帧号
  • 文件夹的相对路径花式跳转

配合os sys glob模块,这些还算在可控范围。但遇到文件序列帧处理,就真的想跪了。

  • 识别我们是同一套序列帧
  • 有缺失帧的处理
  • Nuke 软件的 %04d 风格、Houdini 软件的 $F4和其他####风格转换
  • 递归扫描出指定文件下,某几种指定格式的序列帧文件
  • 把序列帧文件展开成一帧一帧(执行拷贝操作)

还有一个遗憾,使用os处理,这很不面向对象。

安装

使用pip安装

pip install -U dayu-path

导入库

from dayu_path import DayuPath

技术细节介绍

不 care 这些的请直接调到下一节

dayu_path最初版是基于unipath库进行拓展的。

后来觉得安装dayu_path还得安装unipath太烦了。

于是就合并了unipath的功能,发展到了现在的样子。

dayu_path是继承

  • str(python 3.x) 或
  • unicode(python 2.x)

类进行拓展开发的。

os模块里面的相关的函数,添加到dayu_path类上。

也就是说 str/unicode 有的函数它都有,os有的函数它也(大部分)有。

并根据影视动画行业的文件特点,增加了序列文件的处理功能。

基础用法

from dayu_path import DayuPath
my_file = DayuPath('d:/test_dayu_path/a/exr/pl_0010_comp_master_v0001.1001.exr')
# 返回文件的名字
print( my_file.name )
# pl_0010_comp_master_v0001.1001.exr

# 返回文件的拓展名
print (my_file.ext)
# .exr

# 返回去掉name最后一个 .之后的内容
print (my_file.stem)
# pl_0010_comp_master_v0001.1001
print (my_file.stem.stem)
# pl_0010_comp_master_v0001

# 返回文件名中符合 `.v{}.` 的部分 
print (my_file.version)
# v0001 

# 返回文件名中包含数字且不合符version格式的最后一组匹配到的数字 们
print (my_file.frame)
# 1001 

# 返回文件所在的文件夹
my_file.parent 
# d:/test_dayu_path/a/exr
my_folder = my_file.parent.parent 
# d:/test_dayu_path/a

# 返回文件夹的子文件/文件夹
my_folder.child('exr') 
# d:/test_dayu_path/a/exr

# 判断文件/文件夹是否存在
my_folder.exists() 
# True
my_folder.child('hahaha').exists() 
# False

# 让操作系统打开文件夹
my_folder.show() 
# windows就弹出文件资源管理器啦~
my_file.show() 
# 打开文件所在文件夹

# 一些 os 模块的函数展示
my_folder.listdir() 
# [u'd:/test_dayu_path/a/exr']

处理序列帧 我们假设个场景,外包/DI 发回来的一大堆的 exr 序列帧文件,结果如下

from_vendor/
  pl_0010/
    exr/
      pl_0010_comp_v0007.1001.exr
      pl_0010_comp_v0007.1002.exr
      pl_0010_comp_v0007.1005.exr
      pl_0010_comp_v0007.1006.exr
    mov/
      pl_0010_comp_v0007.mov
      something_other_format.mp4
  20190306/
    20190306.csv
    exr/
      A001C001_170922_E4FB.7272829.exr
      A001C001_170922_E4FB.7272830.exr
      A001C001_170922_E4FB.7272831.exr
      A001C001_170922_E4FB.7272832.exr
      something_other_format.jpg

制片大大要把这些文件上传到公司内部服务器上。

于是请你来帮忙写个小工具:

  • 自动识别到 exrmov 文件,其他文件忽略掉
  • 能在文件序列有缺失帧的时候提示他
  • 只想指定from_vendor文件夹
  • 可以自行决定是否要把文件的帧号从10010001开始(比如重新给 DI 发来的文件排帧号)
  • 当有缺帧的时候,可以自行决定是否保持帧号(比如外包只提交了某几个关键帧)
  • 拷贝到公司服务器符合规则的路径下

先得到符合要求的文件们

from dayu_path import DayuPath

root_folder = DayuPath('/path/to/from_vendor')

# 使用 scan 函数进行扫盘操作
# recursive 代表是否要递归扫盘
# ext_filters 传入要过滤的文件格式
for seq_file in root_folder.scan(recursive=True, ext_filters=('mov','exr')):
    print (seq_file) 

    # 我们就得到了符合要求的序列文件,他们被合并成了一个 DayuPath
    # /path/to/from_vendor/20190306/exr/A001C001_170922_E4FB.%07d.exr
    # /path/to/from_vendor/pl_0010/exr/pl_0010_comp_v0007.%04d.exr
    # /path/to/from_vendor/pl_0010/mov/pl_0010_comp_v0007.mov

查看序列帧的帧数信息

# 我们拿这个序列文件举例 my_seq_file
# /path/to/from_vendor/pl_0010/exr/pl_0010_comp_v0007.%04d.exr

# 获取序列中所有的帧号
my_seq_file.frames
# [1001, 1002, 1005, 1006]

# 获取序列中丢失的帧
my_seq_file.missing
# [1003, 1004]

把序列帧展开,获得每个单个文件

for f in my_seq_file.frames:
    my_seq_file.restore_pattern(f)
    # /path/to/from_vendor/pl_0010/exr/pl_0010_comp_v0007.1001.exr
    # /path/to/from_vendor/pl_0010/exr/pl_0010_comp_v0007.1002.exr
    # /path/to/from_vendor/pl_0010/exr/pl_0010_comp_v0007.1005.exr
    # /path/to/from_vendor/pl_0010/exr/pl_0010_comp_v0007.1006.exr

符合要求的文件都拿到了,下面就是要拷贝重命名操作了。

这里我留个作业,请好学的自行你探索dayu_path里面的:

  • rename_sequence
  • copy_sequence

几种 pattern 风格转换

my_seq_file.to_pattern()
# /path/to/from_vendor/pl_0010/exr/pl_0010_comp_v0007.%04d.exr

my_seq_file.to_pattern('#')
# /path/to/from_vendor/pl_0010/exr/pl_0010_comp_v0007.####.exr

my_seq_file.to_pattern('$')
# /path/to/from_vendor/pl_0010/exr/pl_0010_comp_v0007.$F4.exr

其他场景应用

情景:需要用户指定一组 jpg 序列帧,只需要用户随意选择其中一帧

# 用户通过界面选择了 hahahah.1044.jpg 文件
single_file = DayuPath('d:/temp/hahahah.1044.jpg')

# 不递归扫盘
seq_file = list(single_file.scan())[0]

print (seq_file)
# d:/temp/hahahah.%04d.jpg

情景:maya软件里面 file 节点的file属性

# maya 界面显示的就是 d:/temp/hahahah.%04d.jpg 文件
fake_seq_file = DayuPath('d:/temp/hahahah.%04d.jpg')
fake_seq_file.frames
# [] 空列表

# 不递归扫盘
real_seq_file = list(fake_seq_file.scan())[0]
real.frames
# [..., 1044, 1055, ...] 有了!

更高级的用法

你可以拓展 dayu_path哟~

比如说,你们公司的文件夹层级结构非常地规范,一个文件的全路径,包含了projectshot环节等等各种信息。

那么,拿到这个路径后,如何基于dayu_path,得到这些信息呢?

from dayu_path import DayuPath

work_file = DayuPath('x:/projects/my_awsome_project/shots/pl/0010/ani/v0001/pl_0010_ani_v0001.ma')

# 我想这样访问信息
work_file.project() 
# 返回 my_awsome_project

work_file.sequence() 
# 返回 pl

work_file.shot() 
# 返回 0010 或者 pl_0010

work_file.step() 
# 返回 ani

# 等等

我们可以给dayu_path添加plugin,比如上面的例子,可以这样做

from dayu_path import DayuPath
from dayu_path.plugin import DayuPathPlugin

# 定义干活的函数
def project(self):
   # 我粗暴地演示下,不为运行失败负责哟~
   return self.split('/')[2]

# 注册为 dayu_path 的属性
DayuPathPlugin.register_func(project)

# 测试一下吧
DayuPath('x:/projects/my_awsome_project/shots/pl/0010/ani/v0001/pl_0010_ani_v0001.ma').project()
# 我觉得应该打印出 my_awsome_project

未来新功能

  • 支持udim,可以识别和展开

项目地址

Githubopen in new window

欢迎 watch star fork 三连击

重大提示

『人生苦短,我用 dayu_path

上次编辑于:
贡献者: muyanru