maliang / scripts /genbench2_compose.py
teawhite's picture
Add files using upload-large-folder tool
51ac7cc verified
Raw History Blame Contribute Delete
6.86 kB
#!/usr/bin/env python3
"""genbench2 结果整理:
* compare2_<tag>.mp4:每个地图一个 4 列网格(每格 416×240 半分辨率):输入 ids / GT(val)/ 原首帧生成 / 12 种风格;
* compare2_L3.mp4:4 张新地图,每张一行块:ids / 第一轮原首帧生成 / 6 种风格;
* style_sheet.png:每个地图两行(帧 0 = FLUX 首帧;帧 80 = 生成的最后一帧),看风格能不能撑到最后;
* style_stats.json:风格保持 / 回退统计(HSV 直方图交):
keep = sim(生成帧 t, 风格首帧) 越高越好(风格保持)
back = sim(生成帧 t, 原始未风格化帧 0) 相对 keep 越高 ⇒ 越像回退到训练外观
以及 FLUX restyle 的结构保持分(firstframe_flux 的 struct_metrics)。"""
import json
import os
import sys
import cv2
import imageio.v3 as iio
import numpy as np
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import genbench2_run as R # noqa: E402
G, G1 = R.G, R.G1
L1 = f"{R.M}/outputs/sft_v10/eval_step-8000/L1_cond"
W, H = 832, 480
HW, HH = W // 2, H // 2
def panel(path, k):
v = iio.imread(path)
return v[:, -H:, k * W:(k + 1) * W]
def gen_frames(out_dir, name):
p = f"{out_dir}/{name}_gen.mp4"
return iio.imread(p) if os.path.exists(p) else panel(f"{out_dir}/{name}.mp4", 0)
def half(fr):
return cv2.resize(fr, (HW, HH), interpolation=cv2.INTER_AREA)
def label(fr, text, scale=0.5):
out = fr.copy()
cv2.rectangle(out, (0, 0), (out.shape[1], 22), (0, 0, 0), -1)
cv2.putText(out, text, (6, 16), cv2.FONT_HERSHEY_SIMPLEX, scale, (255, 230, 120), 1, cv2.LINE_AA)
return out
def grid_video(tiles, ncol=4):
T = min(len(v) for _, v in tiles)
while len(tiles) % ncol:
tiles.append(("", np.zeros((T, HH, HW, 3), np.uint8)))
frames = []
for t in range(T):
rows = [np.concatenate([label(half(v[t]) if v.shape[1] != HH else v[t], n) for n, v in tiles[r:r + ncol]], 1)
for r in range(0, len(tiles), ncol)]
frames.append(np.concatenate(rows, 0))
return np.stack(frames)
def write(path, frames):
iio.imwrite(path, frames, fps=16, codec="libx264", quality=None, macro_block_size=16,
ffmpeg_params=["-crf", "22", "-pix_fmt", "yuv420p", "-movflags", "+faststart"])
print(path, frames.shape, round(os.path.getsize(path) / 1e6, 1), "MB", flush=True)
def hist(fr):
hsv = cv2.cvtColor(fr, cv2.COLOR_RGB2HSV)
h = cv2.calcHist([hsv], [0, 1, 2], None, [12, 6, 6], [0, 180, 0, 256, 0, 256]).ravel()
return h / max(h.sum(), 1)
def sim(a, b):
return float(np.minimum(hist(a), hist(b)).sum())
def main():
stats, sheet_rows = {}, []
TW, TH = 208, 120
for tag in R.SOURCES:
d, sd = f"{G}/L2/{tag}", R.src_dir(tag)
orig0 = cv2.cvtColor(cv2.imread(R.pick(sd, "frame0.png")), cv2.COLOR_BGR2RGB)
tiles = [("input ids (numeric, parts coloured)", panel(f"{d}/{tag}_{next(iter(R.STYLES))}.mp4", 1))]
if tag.startswith("val"):
cid = R.SOURCES[tag]
tiles.append(("GT (held-out run)", panel(f"{L1}/{cid}.mp4", 0)))
tiles.append(("gen: original first frame", panel(f"{L1}/{cid}.mp4", 1)))
else:
od = f"{G1}/L2/{tag}" if os.path.exists(f"{G1}/L2/{tag}/{tag}_orig.mp4") else d
tiles.append(("gen: original first frame", gen_frames(od, f"{tag}_orig")))
row0, row80 = [cv2.resize(orig0, (TW, TH), interpolation=cv2.INTER_AREA)], [None]
stats[tag] = {}
for s in R.STYLES:
if not os.path.exists(f"{d}/{tag}_{s}.mp4"):
print("缺", tag, s)
continue
v = gen_frames(d, f"{tag}_{s}")
tiles.append((f"UNSEEN style: {s}", v))
f0 = cv2.cvtColor(cv2.imread(f"{d}/{s}_f0.png"), cv2.COLOR_BGR2RGB)
fj = json.load(open(f"{d}/{s}_f0.json"))
stats[tag][s] = {"keep_t40": round(sim(v[40], f0), 3), "keep_t80": round(sim(v[80], f0), 3),
"back_t80": round(sim(v[80], orig0), 3), "flux_struct": fj.get("struct")}
row0.append(cv2.resize(f0, (TW, TH), interpolation=cv2.INTER_AREA))
row80.append(cv2.resize(v[80], (TW, TH), interpolation=cv2.INTER_AREA))
write(f"{G}/compare2_{tag}.mp4", grid_video(tiles))
o = tiles[2 if tag.startswith("val") else 1][1]
row80[0] = cv2.resize(o[80], (TW, TH), interpolation=cv2.INTER_AREA)
stats[tag]["_orig"] = {"keep_t80": round(sim(o[80], orig0), 3)}
sheet_rows.append(label(np.concatenate(row0, 1), f"{tag} frame 0 (FLUX first frames; col 1 = original)", 0.45))
sheet_rows.append(label(np.concatenate(row80, 1), f"{tag} frame 80 (generated)", 0.45))
# L3
blocks = []
for c in sorted(os.listdir(f"{G}/L3")):
d = f"{G}/L3/{c}"
theme = c.split("__")[0]
tiles = [(f"UNSEEN map [{theme}]: ids", panel(f"{G1}/L3/{c}/{c}.mp4", 1)),
("gen: FLUX map2frame first frame", panel(f"{G1}/L3/{c}/{c}.mp4", 0))]
orig0 = cv2.cvtColor(cv2.imread(f"{G1}/L3/{c}/f0.png"), cv2.COLOR_BGR2RGB)
stats[f"L3_{theme}"] = {}
for s in R.L3_STYLES:
if os.path.exists(f"{d}/{c}_{s}.mp4"):
v = gen_frames(d, f"{c}_{s}")
tiles.append((f"UNSEEN style: {s}", v))
f0 = cv2.cvtColor(cv2.imread(f"{d}/{s}_f0.png"), cv2.COLOR_BGR2RGB)
stats[f"L3_{theme}"][s] = {"keep_t80": round(sim(v[80], f0), 3), "back_t80": round(sim(v[80], orig0), 3)}
blocks.append(grid_video(tiles))
if blocks:
T = min(len(b) for b in blocks)
write(f"{G}/compare2_L3.mp4", np.concatenate([b[:T] for b in blocks], 1))
w = max(r.shape[1] for r in sheet_rows)
sheet = np.concatenate([cv2.copyMakeBorder(r, 0, 4 * (i % 2), 0, w - r.shape[1], cv2.BORDER_CONSTANT)
for i, r in enumerate(sheet_rows)], 0)
cv2.imwrite(f"{G}/style_sheet.jpg", sheet[:, :, ::-1], [cv2.IMWRITE_JPEG_QUALITY, 92])
print("sheet", sheet.shape)
# 汇总:每种风格跨地图平均
agg = {}
for tag, d in stats.items():
for s, m in d.items():
if s.startswith("_"):
continue
a = agg.setdefault(s, {"keep_t80": [], "back_t80": []})
a["keep_t80"].append(m["keep_t80"]); a["back_t80"].append(m["back_t80"])
summary = {s: {k: round(float(np.mean(v)), 3) for k, v in a.items()} | {"n": len(a["keep_t80"])} for s, a in agg.items()}
summary["_orig_keep_t80"] = round(float(np.mean([d["_orig"]["keep_t80"] for d in stats.values() if "_orig" in d])), 3)
json.dump({"per_style": summary, "per_run": stats}, open(f"{G}/style_stats.json", "w"), indent=1)
print(json.dumps(summary, indent=1))
if __name__ == "__main__":
sys.exit(main())