maliang / scripts /res_test.py
teawhite's picture
Add files using upload-large-folder tool
51ac7cc verified
Raw History Blame Contribute Delete
5.32 kB
#!/usr/bin/env python3
"""分辨率决策的两个快测:(1) 角色区域的 VAE 重建上限:832×480 / 960×544 / 1280×704(都在源分辨率上和 GT 比);
(2) DiT 前向耗时:21 cell 双向前向在三种分辨率下的相对开销(实时可行性的粗估)。"""
import json
import os
import sys
import time
import cv2
import numpy as np
import torch
import geometry as GEO
import infer as INF
import model as GM
import sample as S
M = "/opt/dlami/nvme/zhiyangdeng/maliang"
D = f"{M}/outputs/char_diag"
RES = {"832x480": (832, 480), "960x544": (960, 544), "1024x576": (1024, 576), "1152x640": (1152, 640), "1280x704": (1280, 704)}
NF = 41
def enc(vae, frames):
"""infer.encode_video 的任意分辨率版(fp32 编码,非 tiled)。"""
x = torch.from_numpy(frames).permute(3, 0, 1, 2).float().div_(127.5).sub_(1.0)
with torch.no_grad(), INF._vae_fp32(vae, True):
z = vae.vae.encode([x], device=vae.device, tiled=False)
return z.to(dtype=torch.bfloat16, device=vae.device)
def dec(vae, z):
with torch.no_grad():
v = vae.vae.decode(z.to(vae.torch_dtype), device=vae.device, tiled=False)
return v[0].float().add_(1.0).mul_(127.5).round_().clamp_(0, 255).to(torch.uint8).permute(1, 2, 3, 0).cpu().numpy()
def to_res(src, wh):
"""源 1280×704 → 目标分辨率:832/960 先按 gc25d 约定裁成目标宽高比再 INTER_AREA;1280×704 原样。"""
W, H = wh
if (W, H) == (1280, 704):
return src
cw = int(round(704 * W / H))
x0 = (1280 - cw) // 2
return np.stack([cv2.resize(f[:, x0:x0 + cw], (W, H), interpolation=cv2.INTER_AREA) for f in src]), x0, cw
def main():
clips = json.load(open(f"{D}/clips.json"))
vae = INF.load_vae("cuda", S.MODEL_DIR)
out = {k: [] for k in RES}
for cid in clips:
ref = np.load(f"{D}/_ref/{cid}.npz")
src, ids = ref["gt_src"][:NF], ref["ids"]
for name, wh in RES.items():
if name == "1280x704":
x, x0, cw = src, 0, 1280
else:
x, x0, cw = to_res(src, wh)
rec = dec(vae, enc(vae, np.ascontiguousarray(x)))
for t in range(8, NF, 8):
m = np.isin(ids[t], GM.PLAYER_PART_IDS)
if m.sum() < 600:
continue
ys, xs = np.nonzero(m)
p = GEO.dst_to_src(np.array([[xs.min() - 10, ys.min() - 10], [xs.max() + 10, ys.max() + 10]], float))
sx0, sy0 = int(max(0, p[0, 0])), int(max(0, p[0, 1]))
sx1, sy1 = int(min(1280, p[1, 0])), int(min(704, p[1, 1]))
gt_c = src[t, sy0:sy1, sx0:sx1]
# 重建帧映回源分辨率坐标再裁同一块(832/960 的重建上采样回源尺寸,比较同一份 GT)
if name == "1280x704":
rc = rec[t, sy0:sy1, sx0:sx1]
else:
up = cv2.resize(rec[t], (cw, 704), interpolation=cv2.INTER_CUBIC)
full = np.zeros_like(src[t]); full[:, x0:x0 + cw] = up
rc = full[sy0:sy1, sx0:sx1]
mse = np.mean((rc.astype(np.float32) - gt_c.astype(np.float32)) ** 2)
g = lambda a: float(np.abs(cv2.Laplacian(cv2.cvtColor(a, cv2.COLOR_RGB2GRAY).astype(np.float32), cv2.CV_32F)).mean())
out[name].append((10 * np.log10(255 ** 2 / max(mse, 1e-6)), g(rc) / max(g(gt_c), 1e-6)))
print(cid, {k: len(v) for k, v in out.items()}, flush=True)
res = {k: {"n": len(v), "player_crop_psnr_vs_native_gt": round(float(np.mean([a for a, _ in v])), 2),
"detail_ratio_vs_native_gt": round(float(np.mean([b for _, b in v])), 3)} for k, v in out.items()}
print(json.dumps(res, indent=1), flush=True)
del vae
torch.cuda.empty_cache()
# (2) DiT 前向耗时(bf16,no_grad,21 cell,双向;只看相对比例)
dit = GM.load_dit(S.MODEL_DIR, "cuda")
GM.install_sem_cond(dit, "parts")
dit = dit.cuda().eval().requires_grad_(False)
dit.freqs = tuple(t.cuda() for t in dit.freqs)
tt = S.TextTable(os.path.join(S.DATA_ROOT, "text_table_gc25d.pt"))
ctx, cids = tt.context([sorted(tt.table)[0]] * 21, "cuda")
timing = {}
for name, (W, H) in RES.items():
lat = torch.randn(1, 48, 21, H // 16, W // 16, dtype=torch.bfloat16, device="cuda")
sem = torch.ones(1, 81, H, W, dtype=torch.uint8, device="cuda")
ts = []
for i in range(4):
torch.cuda.synchronize(); t0 = time.time()
with torch.no_grad():
GM.gc_forward(dit, lat, torch.tensor([500.0], dtype=torch.bfloat16, device="cuda"), ctx, sem,
first_frame_cond=True, context_ids=cids)
torch.cuda.synchronize(); ts.append(time.time() - t0)
timing[name] = {"tokens_per_cell": (H // 32) * (W // 32), "sec_per_forward": round(float(np.median(ts[1:])), 3)}
print(name, timing[name], flush=True)
base = timing["832x480"]["sec_per_forward"]
for k in timing:
timing[k]["ratio_vs_480"] = round(timing[k]["sec_per_forward"] / base, 2)
json.dump({"vae_ceiling": res, "dit_timing": timing}, open(f"{D}/res_test.json", "w"), indent=1)
print(json.dumps(timing, indent=1))
if __name__ == "__main__":
sys.exit(main())