首次提交
This commit is contained in:
111
tools/检查srt质量.py
Normal file
111
tools/检查srt质量.py
Normal file
@@ -0,0 +1,111 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
扫描 SRT 文件, 报告字幕质量指标 + 抽样, 帮你快速判断OCR字幕好不好。
|
||||
纯标准库。
|
||||
|
||||
用法:
|
||||
python3 tools/检查srt质量.py ./srt # 扫目录下所有 .srt
|
||||
python3 tools/检查srt质量.py a.srt b.srt # 扫指定文件
|
||||
python3 tools/检查srt质量.py ./srt --sample 8 # 多抽几条看
|
||||
"""
|
||||
import argparse
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
|
||||
CJK = re.compile(r'[一-鿿]')
|
||||
|
||||
|
||||
def parse_srt(path):
|
||||
"""返回 [(idx, start, end, text), ...]"""
|
||||
try:
|
||||
raw = open(path, encoding='utf-8', errors='ignore').read()
|
||||
except OSError:
|
||||
return []
|
||||
blocks = re.split(r'\n\s*\n', raw.strip())
|
||||
out = []
|
||||
for b in blocks:
|
||||
lines = [x for x in b.splitlines() if x.strip()]
|
||||
if len(lines) < 2:
|
||||
continue
|
||||
m = re.search(r'(\d+):(\d+):(\d+)[,.](\d+)\s*-->\s*(\d+):(\d+):(\d+)[,.](\d+)', b)
|
||||
if not m:
|
||||
continue
|
||||
g = list(map(int, m.groups()))
|
||||
st = g[0]*3600 + g[1]*60 + g[2] + g[3]/1000.0
|
||||
en = g[4]*3600 + g[5]*60 + g[6] + g[7]/1000.0
|
||||
ti = next((i for i, l in enumerate(lines) if '-->' in l), 0)
|
||||
text = ' '.join(lines[ti+1:]).strip()
|
||||
out.append((st, en, text))
|
||||
return out
|
||||
|
||||
|
||||
def has_self_repeat(s, minlen=4):
|
||||
"""检测整条里前半段==后半段这种自我重复(如 AABB → AB 重复)。"""
|
||||
n = len(s)
|
||||
if n < minlen * 2:
|
||||
return False
|
||||
h = n // 2
|
||||
return s[:h] == s[h:2*h] and len(s[:h]) >= minlen
|
||||
|
||||
|
||||
def scan(paths, sample):
|
||||
files = []
|
||||
for p in paths:
|
||||
if os.path.isdir(p):
|
||||
files += glob.glob(os.path.join(p, '**', '*.srt'), recursive=True)
|
||||
elif p.endswith('.srt'):
|
||||
files.append(p)
|
||||
if not files:
|
||||
print("没找到 .srt")
|
||||
return
|
||||
|
||||
tot_e = empty = dup = selfrep = tooshort = lowcjk = badtime = 0
|
||||
chars = 0
|
||||
samples = []
|
||||
for f in files:
|
||||
subs = parse_srt(f)
|
||||
prev = None
|
||||
for st, en, text in subs:
|
||||
tot_e += 1
|
||||
chars += len(text)
|
||||
if not text:
|
||||
empty += 1
|
||||
if prev is not None and text and text == prev:
|
||||
dup += 1
|
||||
if has_self_repeat(text):
|
||||
selfrep += 1
|
||||
if text and len(text) <= 1:
|
||||
tooshort += 1
|
||||
if text and len(CJK.findall(text)) < len(text) * 0.3 and len(text) > 4:
|
||||
lowcjk += 1 # 中文占比过低 → 可能乱码/纯符号
|
||||
if en <= st or (en - st) > 30:
|
||||
badtime += 1
|
||||
prev = text
|
||||
if subs and len(samples) < sample:
|
||||
mid = len(subs) // 2
|
||||
for s in subs[mid:mid+2]:
|
||||
samples.append((os.path.basename(f), s))
|
||||
|
||||
print("=" * 58)
|
||||
print(f"SRT 文件 {len(files)} 个 | 字幕条目 {tot_e} 条 | 平均 {chars/max(tot_e,1):.1f} 字/条")
|
||||
print("-" * 58)
|
||||
def pct(x): return f"{x} ({x/max(tot_e,1)*100:.1f}%)"
|
||||
print(f" 空条目: {pct(empty)} <- 应≈0")
|
||||
print(f" 连续重复(同上条): {pct(dup)} <- 高=去重没做好")
|
||||
print(f" 自我重复(AABB): {pct(selfrep)} <- 高=自重复bug")
|
||||
print(f" 单字条目: {pct(tooshort)}")
|
||||
print(f" 低中文占比(疑乱码):{pct(lowcjk)} <- 高=OCR乱/纯符号")
|
||||
print(f" 时间轴异常: {pct(badtime)} <- 重叠/0时长/超30s")
|
||||
print("=" * 58)
|
||||
print("抽样字幕:")
|
||||
for fn, (st, en, text) in samples[:sample]:
|
||||
print(f" [{st:6.1f}-{en:6.1f}] {text[:50]} ({fn})")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument('paths', nargs='+')
|
||||
ap.add_argument('--sample', type=int, default=6)
|
||||
a = ap.parse_args()
|
||||
scan(a.paths, a.sample)
|
||||
Reference in New Issue
Block a user