dayu_path——轻松处理序列帧文件路径
- 开源库
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
制片大大要把这些文件上传到公司内部服务器上。
于是请你来帮忙写个小工具:
- 自动识别到
exr
和mov
文件,其他文件忽略掉 - 能在文件序列有缺失帧的时候提示他
- 只想指定
from_vendor
文件夹 - 可以自行决定是否要把文件的帧号从
1001
或0001
开始(比如重新给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
哟~
比如说,你们公司的文件夹层级结构非常地规范,一个文件的全路径,包含了project
、shot
环节等等各种信息。
那么,拿到这个路径后,如何基于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
,可以识别和展开
项目地址
欢迎 watch
star
fork
三连击
重大提示
『人生苦短,我用 dayu_path
』