示例成品 看全文 ↓

对齐剪辑和调色的时间码

帧率、起始码或离线版本差异会让调色意见落到错误帧。

免费看示例成品:表格做表格 ›

有人这样说「这版剪辑又换了帧率,调色时间码全对不上,急得我直挠头。」

能替你选的都选好了。不想动,直接点下面「交给 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_

别人还这样问

同类还能办

全部 ›

同样出做表格的

首页