fix: 短时重复伪影也整条删除(判据补重复次数上限)
真实产物里出现 3.7 秒的 cue 被同一个假名填满 111 次(`は`×111),此前判据要求 "时长 ≥15 秒"才删,于是它漏到翻译层,最终变成 56 个"哈"进中文成品字幕。 - 判据改为:重复段占正文 ≥70% 且重复 ≥6 次,并且**时长 ≥15 秒或重复 ≥20 次**。 - 短条走"极端重复"档:真实呻吟的重复次数实测 ≤15(ぇ×15 占 3.2 秒),不会误删。 - 测试数据取自真实产物(A 的日语转写里那条 112 字短伪影)。
This commit is contained in:
+18
-10
@@ -64,11 +64,13 @@ MOAN_CHARS = frozenset(
|
||||
# /んふふ)有效假名 ≤3;>3(如ああああ)或含非呻吟字符的一律保留。
|
||||
DEFAULT_MOAN_MAX_CHARS = 3
|
||||
|
||||
# 重复伪影判据(whisper 窗口内重复循环会输出整条重复到 30 秒的 cue):
|
||||
# 单元最长 6 字(更长的重复单元在真实数据里未见),至少重复 6 次,
|
||||
# 且重复段占正文 ≥70%。真实呻吟重复次数也在 6–15 之间,靠时长区分。
|
||||
# 重复伪影判据(whisper 循环解码会把一个单元写满整条 cue):
|
||||
# 单元最长 6 字(更长的重复单元在真实数据里未见),重复段占正文 ≥70%。
|
||||
# 长度维度两档:长条(≥15s)重复 ≥6 次即删;短条则要求极端重复(≥20 次)——
|
||||
# 真实呻吟重复次数实测 ≤15(ぇ×15 / しゅ×7),而短时伪影实测 3.7 秒填了 111 次。
|
||||
REPETITION_UNIT_MAX_CHARS = 6
|
||||
REPETITION_MIN_REPEATS = 6
|
||||
REPETITION_HARD_REPEATS = 20
|
||||
REPETITION_MIN_COVERAGE = 0.7
|
||||
|
||||
|
||||
@@ -92,20 +94,25 @@ def _is_repetition_artifact(text: str, duration: float, threshold_seconds: float
|
||||
min_repeats: int, min_coverage: float) -> bool:
|
||||
"""判断一条 cue 是否为 whisper 重复循环伪影(整条删除判据)。
|
||||
|
||||
判据(同时满足):展示时长 ≥ 阈值、同一 1–6 字单元连续重复 ≥ min_repeats 次、
|
||||
重复段占正文 ≥ min_coverage。短促重复(“ぇ”×15 只占 3 秒)是真实发声,靠
|
||||
时长区分,与寒暄幻觉同一套“长条才删”思路。
|
||||
同时满足:同一 1–6 字单元连续重复 ≥ min_repeats 次、重复段占正文
|
||||
≥ min_coverage,且**时长 ≥ 阈值或重复次数 ≥ REPETITION_HARD_REPEATS**。
|
||||
长度维度分两档是必要的:只按"长条才删"会漏掉短时循环(实测 3.7 秒的 cue
|
||||
被填了 111 个假名,翻译后变成 56 个"哈"进成品);而真实呻吟的重复次数实测
|
||||
≤15(ぇ×15 占 3.2 秒),所以短条用"极端重复"判据即可区分,不会误删。
|
||||
threshold_seconds ≤ 0 关闭过滤。
|
||||
"""
|
||||
if threshold_seconds <= 0 or duration < threshold_seconds:
|
||||
if threshold_seconds <= 0:
|
||||
return False
|
||||
stripped = text.replace("\n", "")
|
||||
found = _longest_repeated_run(stripped, REPETITION_UNIT_MAX_CHARS)
|
||||
if found is None:
|
||||
return False
|
||||
run, repeats = found
|
||||
if repeats < min_repeats or not stripped:
|
||||
if not stripped or repeats < min_repeats:
|
||||
return False
|
||||
return len(run) / len(stripped) >= min_coverage
|
||||
if len(run) / len(stripped) < min_coverage:
|
||||
return False
|
||||
return duration >= threshold_seconds or repeats >= REPETITION_HARD_REPEATS
|
||||
|
||||
def _moan_chars(text: str) -> int:
|
||||
"""返回 text 中'有效假名字符'数量(呻吟判据的一部分)。
|
||||
@@ -196,7 +203,8 @@ def remove_repetition_entries(
|
||||
模型在窗口内卡住重复时会把同一单元写满整条 cue(实测 30 秒、重复 74–446
|
||||
次):这种正文会让下游 LLM 跟着重复、把预算耗在思考上而报结构错误,也会
|
||||
直接渲染成超长字幕行,因此在产生处整条删除。判据见 _is_repetition_artifact:
|
||||
长条(时长 ≥ 阈值)+ 同一短单元高频重复且占满正文,真实短促呻吟不会命中。
|
||||
长条(时长 ≥ 阈值)或极端重复(≥ REPETITION_HARD_REPEATS 次)+ 同一短单元
|
||||
占满正文;真实短促呻吟(重复 ≤15 次)不会命中。
|
||||
threshold_seconds ≤ 0 时关闭过滤。纯函数,不修改输入。
|
||||
"""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user