Download scripts/res_test.py from teawhite/maliang: direct link, hf CLI and curl.
- Browser
- Download file 5.32 kB
-
https://huggingface.co/teawhite/maliang/resolve/main/scripts/res_test.py
- Command line
-
hf download hf://teawhite/maliang/scripts/res_test.py
-
curl -L -o res_test.py https://huggingface.co/teawhite/maliang/resolve/main/scripts/res_test.py
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()) | |