首次提交
This commit is contained in:
128
tools/回溯过滤.py
Normal file
128
tools/回溯过滤.py
Normal file
@@ -0,0 +1,128 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
回溯过滤: 对【已跑出来】的成功视频, 用低置信占比信号找出"非字幕内容"(幻灯片/代码/满屏文字),
|
||||
和新加的实时过滤同一套标准。默认只列出(dry-run); --delete 才删 COS 上的垃圾 SRT。
|
||||
|
||||
用法:
|
||||
python3 tools/回溯过滤.py # 扫全部成功视频, 只列出
|
||||
python3 tools/回溯过滤.py --recent 200 # 只扫最近200个(快速试)
|
||||
python3 tools/回溯过滤.py --frac 0.35 --thr 80 # 阈值(同config默认)
|
||||
python3 tools/回溯过滤.py --delete # ★删掉判定为非字幕的 COS SRT★(不可逆!)
|
||||
产物: 非字幕清单.txt (video_cos_path, 低置信占比, srt路径)
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from qcloud_cos import CosConfig, CosS3Client
|
||||
from config.config_utils import config
|
||||
|
||||
cos = config.get_cos_config()
|
||||
B = cos["bucket"]
|
||||
_client = CosS3Client(CosConfig(Region=cos["region"], SecretId=cos["secret_id"],
|
||||
SecretKey=cos["secret_key"]))
|
||||
|
||||
|
||||
def key_of(cos_url):
|
||||
return cos_url.split("://", 1)[1].split("/", 1)[1]
|
||||
|
||||
|
||||
def lowconf_ratio(composite_cos_path, thr):
|
||||
"""下载该视频的 ocr_result.jsonl, 返回 (低置信占比, 检测数); 失败返回 (None, 0)。"""
|
||||
key = key_of(composite_cos_path).rstrip("/") + "/ocr_result.jsonl"
|
||||
try:
|
||||
body = _client.get_object(Bucket=B, Key=key)["Body"].get_raw_stream().read()
|
||||
confs = []
|
||||
for line in body.decode("utf-8", "ignore").splitlines():
|
||||
line = line.strip()
|
||||
if not line.startswith("{"):
|
||||
continue
|
||||
try:
|
||||
d = json.loads(line)
|
||||
except Exception:
|
||||
continue
|
||||
for det in d.get("TextDetections", []):
|
||||
if det.get("DetectedText"):
|
||||
confs.append(det.get("Confidence", 0))
|
||||
if not confs:
|
||||
return None, 0
|
||||
return sum(1 for c in confs if c < thr) / len(confs), len(confs)
|
||||
except Exception:
|
||||
return None, 0
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--jsonl", default="数据.jsonl")
|
||||
ap.add_argument("--frac", type=float, default=0.6)
|
||||
ap.add_argument("--thr", type=int, default=80)
|
||||
ap.add_argument("--min-det", type=int, default=200)
|
||||
ap.add_argument("--recent", type=int, default=0, help="只扫最近N个(0=全部)")
|
||||
ap.add_argument("--workers", type=int, default=16)
|
||||
ap.add_argument("--delete", action="store_true", help="删掉非字幕视频的 COS SRT(不可逆!)")
|
||||
ap.add_argument("--out", default="非字幕清单.txt")
|
||||
args = ap.parse_args()
|
||||
|
||||
latest = {}
|
||||
for line in open(args.jsonl, encoding="utf-8"):
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
r = json.loads(line)
|
||||
except Exception:
|
||||
continue
|
||||
v = r.get("video_cos_path")
|
||||
if v:
|
||||
latest[v] = r
|
||||
vids = [r for r in latest.values()
|
||||
if r.get("status") == "success" and r.get("composite_cos_path")]
|
||||
if args.recent:
|
||||
vids = vids[-args.recent:]
|
||||
print(f"待扫描成功视频: {len(vids)} | 阈值: 低置信<{args.thr} 占比>{args.frac*100:.0f}% | "
|
||||
f"{'★删除模式★' if args.delete else 'dry-run(只列出)'}")
|
||||
|
||||
bad, done, errored = [], 0, 0
|
||||
with ThreadPoolExecutor(max_workers=args.workers) as ex:
|
||||
fut = {ex.submit(lowconf_ratio, r["composite_cos_path"], args.thr): r for r in vids}
|
||||
for f in as_completed(fut):
|
||||
r = fut[f]
|
||||
ratio, ndet = f.result()
|
||||
done += 1
|
||||
if ratio is None:
|
||||
errored += 1
|
||||
elif ndet >= args.min_det and ratio > args.frac:
|
||||
bad.append((r["video_cos_path"], ratio, ndet, r.get("srt_cos_path")))
|
||||
if done % 100 == 0:
|
||||
print(f" ...{done}/{len(vids)} 已扫, 命中非字幕 {len(bad)}")
|
||||
|
||||
bad.sort(key=lambda x: -x[1])
|
||||
with open(args.out, "w", encoding="utf-8") as f:
|
||||
for v, ratio, ndet, srt in bad:
|
||||
f.write(f"{v}\t低置信{ratio*100:.0f}%\t{ndet}检测\t{srt}\n")
|
||||
print(f"\n扫描完成: {done} 个 | 非字幕(应过滤) {len(bad)} 个 | 读取失败 {errored} 个")
|
||||
print(f"清单 → {args.out}")
|
||||
print("命中样例(低置信占比最高的几个):")
|
||||
for v, ratio, ndet, srt in bad[:8]:
|
||||
print(f" {ratio*100:3.0f}% {ndet:5d}检测 {os.path.basename(v)}")
|
||||
|
||||
if args.delete and bad:
|
||||
print(f"\n★删除模式: 删除 {len(bad)} 个非字幕视频的 COS SRT...")
|
||||
dn = 0
|
||||
for v, ratio, ndet, srt in bad:
|
||||
if srt:
|
||||
try:
|
||||
_client.delete_object(Bucket=B, Key=key_of(srt))
|
||||
dn += 1
|
||||
except Exception as e:
|
||||
print(" 删除失败", srt, e)
|
||||
print(f"已删除 {dn} 个 COS SRT。(清单已存 {args.out}, 需要可重跑这些视频)")
|
||||
elif bad:
|
||||
print(f"\n这是 dry-run。确认清单后, 加 --delete 删除这些垃圾SRT。")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user