对齐剪辑和调色的时间码
帧率、起始码或离线版本差异会让调色意见落到错误帧。
有人这样说「这版剪辑又换了帧率,调色时间码全对不上,急得我直挠头。」
能替你选的都选好了。不想动,直接点下面「交给 AI 办」,点一下就开始做;想改,就点一下要的那个。
已替你选好想改就点一下
更多选项(一般不用动)
只有你知道的不填就先空着,成品里留【待填】
还想怎么改可以不选
开工前请确认
高级选项一般不用动 · 这件事通常 1–3 分钟做好
示例成品全文按上面这组示例选项真跑出来的
缺两个文件我没法直接给出偏移表:你的 Premiere XML 和 DaVinci CSV 现在不在对话里。
下面给你一个可直接跑通的脚本,拿来文件后一条命令出表。
运行方式
pip install openpyxl
python align_tc.py timeline.xml color.csv --fps 23.976 --out aligned_tc.xlsx如果 CSV 列名不标准,可指定列名:
python align_tc.py timeline.xml color.csv --id-col "Shot ID" --tc-col "Color Timecode"输出规则
- 23.976 按非丢帧处理,时间码按 24 名义时基 计算,不会出现小数帧
- XML 源时间码取:
文件起始时间码 + 入点 - 偏移帧数:
调色时间码 - XML源时间码 0= 对齐正数= 调色时间码比 XML 晚负数= 调色时间码比 XML 早- 偏移 ≠ 0、缺失、重复 ID,整行标红高亮
- 默认按绝对偏移从大到小排序,最离谱的排最上面
脚本 align_tc.py
import argparse
import csv
import re
import sys
from xml.etree import ElementTree as ET
try:
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill
except ImportError:
print("缺少 openpyxl,请先执行:pip install openpyxl")
sys.exit(1)
def normalize_id(s):
if not s:
return ""
s = str(s).strip()
s = re.sub(r"\s+", "", s)
return s.lower()
def normalize_col(s):
if not s:
return ""
s = str(s).strip()
s = re.sub(r"[^0-9a-zA-Z\u4e00-\u9fff]", "", s)
return s.lower()
def get_timebase(fps):
fps = float(fps)
if abs(fps - 23.976) < 0.01 or abs(fps - 24.0) < 0.01:
return 24.0
if abs(fps - 25.0) < 0.01:
return 25.0
if abs(fps - 29.97) < 0.01:
return 30.0
return round(fps)
def tc_to_frames(tc, tb=24.0):
if tc is None:
return None
tc = str(tc).strip()
parts = re.split(r"[:;]", tc)
if len(parts) == 4:
hh, mm, ss, ff = map(int, parts)
elif len(parts) == 3:
hh, mm, ss = map(int, parts)
ff = 0
else:
raise ValueError(f"无法解析时间码:{tc}")
tb_int = int(round(tb))
return (hh * 3600 + mm * 60 + ss) * tb_int + ff
def frames_to_tc(frames, tb=24.0):
if frames is None:
return ""
neg = frames < 0
frames = abs(int(frames))
tb_int = int(round(tb))
ff = frames % tb_int
total_seconds = frames // tb_int
ss = total_seconds % 60
mm = (total_seconds // 60) % 60
hh = total_seconds // 3600
tc = f"{hh:02d}:{mm:02d}:{ss:02d}:{ff:02d}"
return "-" + tc if neg else tc
def parse_premiere_xml(path, tb):
tree = ET.parse(path)
root = tree.getroot()
records = []
for clip in root.iter("clipitem"):
clip_id = clip.get("id", "").strip()
name = ""
name_el = clip.find("name")
if name_el is not None and name_el.text:
name = name_el.text.strip()
file_el = clip.find("file")
if file_el is None:
file_el = clip.find(".//file")
file_name = ""
if file_el is not None:
fn_el = file_el.find("name")
if fn_el is not None and fn_el.text:
file_name = fn_el.text.strip()
file_tc_str = ""
file_tc_frames = None
if file_el is not None:
tc_str_el = file_el.find("timecode/string")
if tc_str_el is not None and tc_str_el.text:
file_tc_str = tc_str_el.text.strip()
file_tc_frames = tc_to_frames(file_tc_str, tb)
else:
tc_frame_el = file_el.find("timecode/frame")
if tc_frame_el is not None and tc_frame_el.text:
file_tc_frames = int(tc_frame_el.text.strip())
file_tc_str = frames_to_tc(file_tc_frames, tb)
in_frames = 0
in_el = clip.find("in")
if in_el is not None and in_