From 767fa3ac08a240c524b058e0c7a129b44e92024a Mon Sep 17 00:00:00 2001 From: hoelee Date: Mon, 12 Oct 2026 21:44:57 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE:=20=E5=AF=B9=E9=BD=90=E4=BF=AE?= =?UTF-8?q?=E5=A5=BD=E4=BA=86=E9=85=8D=E5=AF=B9=EF=BC=8C=E5=88=A4=E5=86=B3?= =?UTF-8?q?=E5=8D=B4=E7=94=A8=E6=97=A7=E9=85=8D=E5=AF=B9=E7=9A=84=E5=B8=A7?= =?UTF-8?q?=E5=8D=A0=E6=AF=94=EF=BC=880912=20=E6=B4=9E=E8=A7=81=20?= =?UTF-8?q?=E5=81=87=E6=8B=92=E7=BB=9D=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 洞见在 crf 24 上被判 'mean 0.9974 但 20/1199 帧低于 0.85' → 拒。实测: 0.9974/0.9936 是对齐后的数字, 而 20 帧是直配的旧计数 —— 两个数字来自不同配对。三向对照测试(本地,同一 60 s 窗口): 健康对 min 0.9940 mean 0.9971 0 坏帧 → 走快路径(无对齐搜索),通过 ✓ 错位对 'alignment repaired the pairing: 20 frame(s) below 0.85 before, 0 after' min 0.9940 mean 0.9972 1199 帧 0 坏帧 → 通过 ✓ ← 与洞见判决书 20/1199 逐项吻合 真缺陷对 mean 0.9612 151 帧(10.07%) → 仍被拒 ✓(没变橡皮章) 改动: - 触发条件补上帧占比: 原来只看 min<0.985 / mean<0.995,帧占比超标不触发搜索; - _paired_ssim / _perframe_best 现在返回 below 计数,并同样剔除近平坦帧(之前对齐后的数字 含黑场帧,会让对齐看起来比直配还差); - 采用对齐结果时同步更新 stats['below'](判决用的就是它),并打印 'X frames below before, Y after'。 --- scripts/library_run.py | 60 +++++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/scripts/library_run.py b/scripts/library_run.py index 82c83f9..d5d435f 100644 --- a/scripts/library_run.py +++ b/scripts/library_run.py @@ -742,7 +742,12 @@ def ssim_vs_source(src_local, out_local, info, workdir, tag, dur=SSIM_WINDOW): # 2. coarse, STEP 10 over +-100 - unchanged, for a file that has drifted by tens of frames; # 3. per-frame best match over +-ALIGN_PERFRAME, for a window whose displacement varies. used_off = 0 # the alignment the VMAF observation below is measured at - if n and (lo is None or lo < SSIM_TARGET or mean is None or mean < SSIM_MEAN_MIN): + # Trigger on the FRAME-SHARE as well, not just min/mean: a window can pair wrongly for a stretch + # of frames while its min stays above SSIM_TARGET, and then the share is the only symptom. That + # was 0912 洞见 (20 of 1199 frames below 0.85 - refused on the share, never searched). + direct_below = bad + if n and (lo is None or lo < SSIM_TARGET or mean is None or mean < SSIM_MEAN_MIN + or bad > MAX_BAD_SHARE * n): best = None nf = min(n, 1200) pid = os.getpid() # two lanes share the stage dir: a fixed tag let them overwrite @@ -751,9 +756,10 @@ def ssim_vs_source(src_local, out_local, info, workdir, tag, dur=SSIM_WINDOW): for off in offs: if off == 0: continue - mn, mm, k = _paired_ssim(ref, out, off, nf, workdir, "%d%s%d" % (pid, stage, abs(off))) + mn, mm, k, bl = _paired_ssim(ref, out, off, nf, workdir, + "%d%s%d" % (pid, stage, abs(off)), flat) if mn is not None and (best is None or mn > best[0]): - best = (mn, mm, k, off, stage) + best = (mn, mm, k, off, stage, bl) if best and best[0] >= 0.90 and best[1] >= 0.98: break # the fine stage already explains it; don't pay for the rest if best and best[0] >= 0.90 and best[1] >= 0.98: @@ -767,12 +773,24 @@ def ssim_vs_source(src_local, out_local, info, workdir, tag, dur=SSIM_WINDOW): % (best[3], best[4])) lo, mean, n = best[0], best[1], best[2] used_off = best[3] + # The share MUST come from the same pairing as the min/mean that were just adopted. + # Leaving the direct count in place refused an encode whose pictures had already been + # verified at the aligned offset (0912 洞见: min 0.9936 mean 0.9974 after realignment, + # refused by "20 of 1199 frames below 0.85" from the WRONG pairing). + if best[5] is not None: + stats["below"] = best[5] + log(" alignment repaired the pairing: %d frame(s) below %.2f before, %s after" + % (direct_below, BAD_FRAME_SSIM, stats.get("below"))) else: - per = _perframe_best(ref, out, nf, workdir, "%d" % pid) + per = _perframe_best(ref, out, nf, workdir, "%d" % pid, flat) if per and per[1] >= 0.90 and per[2] >= 0.98: stats["align_note"] = ("per-frame best match (+-%d frames) - displacement varies " "inside the window" % ALIGN_PERFRAME) lo, mean, n = per[1], per[2], per[0] + if per[3] is not None: + stats["below"] = per[3] + log(" per-frame realignment: %d frame(s) below %.2f before, %s after" + % (direct_below, BAD_FRAME_SSIM, stats.get("below"))) else: log(" no alignment explains the mismatch (best constant offset %s," " per-frame best-match %s) - keeping the direct-pairing verdict" @@ -901,18 +919,34 @@ def vmaf_of_pair(ref_raw, out_raw, off, nf, workdir, tag): pass -def _paired_ssim(ref_raw, out_raw, off, nf, workdir, tag): - """(min, mean, frames) for one constant offset - see `_ssim_series` for the sign convention.""" +def _paired_ssim(ref_raw, out_raw, off, nf, workdir, tag, flat=()): + """(min, mean, frames, below) for one constant offset - see `_ssim_series` for the sign. + + `flat` holds the indices of near-flat source frames; they are dropped here exactly as the direct + pairing drops them, so the alignment is judged on the same frame set as the verdict that follows + (without this a black fade dragged the aligned mean below the direct one and the search looked + useless on files that were merely dark). + `below` is what the frame-share test actually consumes - returning only min/mean meant a repaired + alignment still got refused by the share count taken from the WRONG pairing. + """ vals = _ssim_series(ref_raw, out_raw, off, nf, workdir, tag) - return (min(vals), sum(vals) / len(vals), len(vals)) if vals else (None, None, 0) + base = max(0, off) + keep = [v for i, v in enumerate(vals) if (base + i) not in flat] + if not keep: + return (None, None, 0, 0) + return (min(keep), sum(keep) / len(keep), len(keep), + sum(1 for v in keep if v < BAD_FRAME_SSIM)) -def _perframe_best(ref_raw, out_raw, nf, workdir, tag): - """(frames, min, mean) where each frame scores against the best of its +-ALIGN_PERFRAME - neighbours. +def _perframe_best(ref_raw, out_raw, nf, workdir, tag, flat=()): + """(frames, min, mean, below) where each frame scores against the best of its +-ALIGN_PERFRAME + neighbours, over the non-flat frames only. For a window whose displacement is not one constant shift. A wrong-picture defect cannot be - rescued by this - such frames have no good neighbour - while a duplicate-frame stretch can. + rescued by this - such frames have no good neighbour - while a duplicate-frame stretch can, and + that is precisely the shape a false frame-share refusal has (measured 2026-10-12: 0912 洞见 + scored 20 of 1199 frames below 0.85 with a 0.9974 mean by direct pairing; shifting the encode by + ONE frame produced 22 such frames on the same content and a correct pairing produced none). """ per = {} for off in range(-ALIGN_PERFRAME, ALIGN_PERFRAME + 1): @@ -920,12 +954,14 @@ def _perframe_best(ref_raw, out_raw, nf, workdir, tag): base = max(0, off) for i, v in enumerate(vals): j = base + i + if j in flat: + continue if v > per.get(j, -1.0): per[j] = v if not per: return None s = [per[k] for k in sorted(per)] - return len(s), min(s), sum(s) / len(s) + return len(s), min(s), sum(s) / len(s), sum(1 for v in s if v < BAD_FRAME_SSIM) def pin_to_e_cores(mask=None):