maliang / scripts /char_cmp704.py
teawhite's picture
Add files using upload-large-folder tool
51ac7cc verified
Raw History Blame Contribute Delete
5.82 kB
#!/usr/bin/env python3
"""角色清晰度:480p(s8000)vs 704p(--variant)在原生 1280×704 坐标上比(不依赖 GC_RES:480p 的裁剪/缩放规则这里写死)。
每个 clip、每 8 帧,按 480p ids 的玩家 / Boss 掩码取框 → 映回源坐标;480p 的帧(生成 / VAE 重建)先 INTER_CUBIC 放回 1220×704
再贴到 x0=30;704p 的帧原样。指标(框内,相对原生 GT):detail = |Laplacian| 均值之比(res_test 同款),sharp = Sobel 梯度
均值之比,psnr。另出 player_crops_480_vs_704.jpg(同尺度裁剪,×2 最近邻放大)与 compare_480_vs_704.mp4(整帧三联)。
usage: char_cmp704.py --variant s5000_704 [--v480 s8000_cfg1]"""
import argparse
import json
import os
import sys
import cv2
import imageio.v3 as iio
import numpy as np
M = "/opt/dlami/nvme/zhiyangdeng/maliang"
D = f"{M}/outputs/char_diag"
ROLES = {"player": (10, 12, 13, 14, 15, 16), "boss": (11, 17, 18, 19, 20, 21)}
X0, CW, SW, SH, W4, H4 = 30, 1220, 1280, 704, 832, 480
PAD, MIN_PX, UP = 10, 600, 2
def up480(fr):
"""832×480 → 源 1280×704 画布(两侧 30 px 黑边)。"""
out = np.zeros((SH, SW, 3), np.uint8)
out[:, X0:X0 + CW] = cv2.resize(fr, (CW, SH), interpolation=cv2.INTER_CUBIC)
return out
def src_box(mask):
ys, xs = np.nonzero(mask)
x0, x1 = xs.min() - PAD, xs.max() + PAD + 1
y0, y1 = ys.min() - PAD, ys.max() + PAD + 1
sx0, sx1 = X0 + x0 * CW / W4, X0 + x1 * CW / W4
sy0, sy1 = y0 * SH / H4, y1 * SH / H4
return int(max(0, sy0)), int(min(SH, np.ceil(sy1))), int(max(X0, sx0)), int(min(X0 + CW, np.ceil(sx1)))
def stats(img):
g = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY).astype(np.float32)
gx, gy = cv2.Sobel(g, cv2.CV_32F, 1, 0), cv2.Sobel(g, cv2.CV_32F, 0, 1)
return float(np.abs(cv2.Laplacian(g, cv2.CV_32F)).mean()), float(np.sqrt(gx ** 2 + gy ** 2).mean())
def psnr(a, b):
return 10 * np.log10(255.0 ** 2 / max(float(np.mean((a.astype(np.float32) - b.astype(np.float32)) ** 2)), 1e-6))
def label(img, text):
out = img.copy()
cv2.rectangle(out, (0, 0), (out.shape[1], 18), (0, 0, 0), -1)
cv2.putText(out, text, (3, 13), cv2.FONT_HERSHEY_SIMPLEX, 0.42, (255, 230, 120), 1, cv2.LINE_AA)
return out
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--variant", required=True)
ap.add_argument("--v480", default="s8000_cfg1")
ap.add_argument("--video_clips", type=int, default=3)
ap.add_argument("--n", type=int, default=0, help="只用前 n 个 clip(测试用)")
a = ap.parse_args()
clips = json.load(open(f"{D}/clips.json"))
clips = clips[:a.n] if a.n else clips
keys = ["recon480", "gen480", "recon704", "gen704"]
st = {r: {k: [] for k in keys} for r in ROLES}
rows, vid = [], []
for ci, cid in enumerate(clips):
ref = np.load(f"{D}/_ref/{cid}.npz")
gt, ids = ref["gt_src"], ref["ids"]
fr = {"recon480": ref["recon"], "gen480": np.load(f"{D}/{a.v480}/{cid}.npz")["frames"],
"recon704": np.load(f"{D}/_ref704/{cid}.npz")["recon"], "gen704": np.load(f"{D}/{a.variant}/{cid}.npz")["frames"]}
assert fr["gen704"].shape == gt.shape, (fr["gen704"].shape, gt.shape)
for role, pids in ROLES.items():
for t in range(8, 81, 8):
m = np.isin(ids[t], pids)
if m.sum() < MIN_PX:
continue
y0, y1, x0, x1 = src_box(m)
g = gt[t, y0:y1, x0:x1]
lg, sg = stats(g)
crops = {}
for k in keys:
full = up480(fr[k][t]) if k.endswith("480") else fr[k][t]
c = full[y0:y1, x0:x1]
crops[k] = c
l, s = stats(c)
st[role][k].append((l / max(lg, 1e-6), s / max(sg, 1e-6), psnr(c, g)))
if role == "player" and t in (40, 72) and len(rows) < 14:
big = lambda x: cv2.resize(x, (x.shape[1] * UP, x.shape[0] * UP), interpolation=cv2.INTER_NEAREST)
names = {"recon480": "VAE recon 480p", "gen480": "gen 480p s8000", "recon704": "VAE recon 704p",
"gen704": f"gen 704p {a.variant}"}
tiles = [label(big(g), "GT native 1280x704")] + [label(big(crops[k]), names[k]) for k in keys]
rows.append(np.concatenate([cv2.copyMakeBorder(x, 0, 0, 0, 4, cv2.BORDER_CONSTANT) for x in tiles], 1))
if ci < a.video_clips:
for t in range(81):
p = [label(gt[t], "GT native"), label(up480(fr["gen480"][t]), "gen 480p (upscaled)"),
label(fr["gen704"][t], f"gen 704p {a.variant}")]
vid.append(np.concatenate(p, 1))
summary = {r: {k: {"n": len(v), "detail_ratio": round(float(np.mean([x[0] for x in v])), 3),
"sharp_ratio": round(float(np.mean([x[1] for x in v])), 3),
"psnr": round(float(np.mean([x[2] for x in v])), 2)} for k, v in d.items() if v} for r, d in st.items()}
json.dump(summary, open(f"{D}/summary_480_vs_704.json", "w"), indent=1)
print(json.dumps(summary, indent=1))
if rows:
w = max(r.shape[1] for r in rows)
sheet = np.concatenate([cv2.copyMakeBorder(r, 0, 6, 0, w - r.shape[1], cv2.BORDER_CONSTANT) for r in rows], 0)
cv2.imwrite(f"{D}/player_crops_480_vs_704.jpg", sheet[:, :, ::-1], [cv2.IMWRITE_JPEG_QUALITY, 93])
print("sheet", sheet.shape)
if vid:
iio.imwrite(f"{D}/compare_480_vs_704.mp4", np.stack(vid), fps=16, codec="libx264", quality=None, macro_block_size=16,
ffmpeg_params=["-crf", "18", "-pix_fmt", "yuv420p", "-movflags", "+faststart"])
print("video", len(vid))
if __name__ == "__main__":
sys.exit(main())