maliang / scripts /genbench2_run.py
teawhite's picture
Add files using upload-large-folder tool
51ac7cc verified
Raw History Blame Contribute Delete
8.42 kB
#!/usr/bin/env python3
"""480p 双向 SFT 泛化测试第二轮(genbench2):更多、差异更大的没见过的首帧风格。
L2:8 个场景的地图(2 条训练 clip + 6 条 val clip,真 ids / prompts)× 12 种风格(FLUX restyle 原帧 0)。
L3:4 张 procmap 新地图(genbench 第一轮 FLUX map2frame 出的首帧)× 6 种风格(再 restyle)。
风格都不在训练外观里(训练 = photo PBR 各种天气/生物群系 + pixel/comic/sketch/toon/paint/soft/hue/duotone/dither 后处理;
场景 09 本身是赛博朋克霓虹 ⇒ 不用霓虹风)。
usage: genbench2_run.py [--ckpt ...] [--gpus 1,2,3,4,5,6,7] [--flux_gpu 7]"""
import argparse
import json
import os
import subprocess
import sys
from concurrent.futures import ThreadPoolExecutor
M = "/opt/dlami/nvme/zhiyangdeng/maliang"
G1 = f"{M}/outputs/genbench"
G = f"{M}/outputs/genbench2"
PY, FPY = f"{M}/.venv/bin/python", "/opt/dlami/nvme/zhiyangdeng/PCE/envs/pce_f3/bin/python"
ENV = dict(os.environ, DIFFSYNTH_SKIP_DOWNLOAD="True", PYTHONPATH=f"{M}/code/gc25d:{M}/code/arope",
HF_HOME="/opt/dlami/nvme/zhiyangdeng/.cache/huggingface", TMPDIR=f"{M}/.tmp", OMP_NUM_THREADS="8",
GC_RES="832x480")
# tag → clip_id;train_* 的地图训练时见过,val_* 是 held-out run(L1 里有 GT 与原首帧生成结果)
SOURCES = {
"train_v10": "01_Sunlit_Mediterranean_Courtyard__R10_s1501__000672",
"train_v9": "06_Overgrown_Tropical_Water_Temple__P9C_golden_s6942__003648",
"val_v10": "01_Sunlit_Mediterranean_Courtyard__R10_s1512__001152",
"val_v9": "02_Autumn_Woodland_Shrine__P9_golden_s2909__001152",
"val_steampunk": "07_Steampunk_Foundry_District__R10_s7511__001056",
"val_cyber": "09_Dense_Cyberpunk_Neon_District__R10_s9511__001152",
"val_cathedral": "08_Gothic_Cathedral_Quarter__P9C_default_s8946__001152",
"val_underwater": "10_Underwater_Research_Habitat__P9C_snow_s10945__001152",
}
STYLES = {
"ukiyoe": "a Japanese ukiyo-e woodblock print: bold black outlines, flat areas of Prussian blue, vermilion and ochre, "
"visible wood-grain and washi paper texture, no shading gradients",
"stainedglass": "a medieval stained-glass window: every surface built from pieces of glowing translucent coloured glass "
"separated by thick black lead lines, jewel-toned light",
"lego": "a scene built entirely from plastic toy bricks: studs on top of every surface, glossy ABS plastic in bright "
"primary colours, toy photography lighting; characters are brick-built minifigures",
"voxel": "voxel art: everything made of small uniform cubes, blocky stepped terrain, crisp flat-shaded cube faces, "
"simple saturated colours",
"knitted": "a knitted wool and felt craft diorama: visible yarn stitches and fuzzy fibres on every surface, soft pastel "
"textile colours, felt characters",
"porcelain": "Chinese blue-and-white porcelain painting: cobalt-blue brush patterns on glossy white glaze, everything "
"rendered only in shades of blue on white",
"blueprint": "a technical blueprint drawing: thin white line-art on deep blue grid paper, hatching for shading, no "
"colour fills",
"chalkboard": "a chalk drawing on a dark green school blackboard: dusty chalk strokes in white and pastel colours, "
"smudges and eraser marks",
"candy": "a candy and dessert world: ground of pink frosting and biscuit, walls of chocolate and wafer, glossy hard "
"candies, lollipop trees, pastel sugary colours",
"lava": "a volcanic hellscape: black obsidian rock and ash, glowing cracks of molten lava, floating embers, red-orange "
"light from below",
"mosaic": "an ancient Roman floor mosaic: everything made of small square stone and glass tesserae with visible grout "
"lines, earthy terracotta, ochre and teal colours",
"papyrus": "an ancient Egyptian tomb wall painting on papyrus: flat ochre, turquoise and lapis colours, thick dark "
"outlines, cracked aged plaster texture",
}
L3_STYLES = ["ukiyoe", "lego", "porcelain", "candy", "stainedglass", "lava"]
def run(cmd, log, env=ENV):
os.makedirs(os.path.dirname(log), exist_ok=True)
with open(log, "w") as fh:
r = subprocess.run(cmd, stdout=fh, stderr=subprocess.STDOUT, env=env, cwd=f"{M}/.tmp")
if r.returncode:
raise RuntimeError(f"失败({r.returncode}):{' '.join(cmd[:4])}… 见 {log}")
def src_dir(tag):
d = f"{G1}/L2/{tag}/src"
return d if os.path.exists(f"{d}/done") else f"{G}/L2/{tag}/src"
def pick(d, suffix):
return f"{d}/" + [f for f in os.listdir(d) if f.endswith(suffix)][0]
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--ckpt", default=f"{M}/outputs/sft_v10/step-8000.safetensors")
ap.add_argument("--gpus", default="1,2,3,4,5,6,7")
ap.add_argument("--flux_gpu", default="7")
a = ap.parse_args()
gpus = a.gpus.split(",")
# 1) 导出新 val clip 的首帧 / ids / prompts(第一轮已导出的直接复用)
for tag, cid in SOURCES.items():
d = src_dir(tag)
if not os.path.exists(f"{d}/done"):
run([PY, f"{M}/code/gc25d/sample_user.py", "--export_clip", cid, "--out", d],
f"{G}/logs/export_{tag}.log", dict(ENV, CUDA_VISIBLE_DEVICES=gpus[0]))
open(f"{d}/done", "w").write(cid)
print("export ok", flush=True)
# 2) FLUX restyle(一个进程,模型只加载一次)
jobs = []
for tag in SOURCES:
f0 = pick(src_dir(tag), "frame0.png")
for s, text in STYLES.items():
jobs.append({"mode": "restyle", "image": f0, "style": text, "out": f"{G}/L2/{tag}/{s}_f0.png"})
cases = sorted(d for d in os.listdir(f"{G1}/procmap") if os.path.isdir(f"{G1}/procmap/{d}"))
for c in cases:
for s in L3_STYLES:
jobs.append({"mode": "restyle", "image": f"{G1}/L3/{c}/f0.png", "style": STYLES[s],
"out": f"{G}/L3/{c}/{s}_f0.png"})
todo = [j for j in jobs if not os.path.exists(j["out"])]
if todo:
for j in todo:
os.makedirs(os.path.dirname(j["out"]), exist_ok=True)
json.dump(todo, open(f"{G}/flux_jobs.json", "w"), indent=1)
run([FPY, f"{M}/code/gc25d/firstframe_flux.py", "batch", "--jobs", f"{G}/flux_jobs.json"], f"{G}/logs/flux.log",
dict(ENV, CUDA_VISIBLE_DEVICES=a.flux_gpu, PYTHONPATH=f"{M}/code/gc25d"))
print("flux ok", len(jobs), flush=True)
# 3) 生成(50 步,同 L1 / 第一轮)
runs = []
for tag in SOURCES:
d = src_dir(tag)
for s in STYLES:
runs.append(dict(name=f"{tag}_{s}", first=f"{G}/L2/{tag}/{s}_f0.png", ids=pick(d, "ids.npz"),
prompts=pick(d, "prompts.json"), out=f"{G}/L2/{tag}"))
if tag.startswith("train") and not os.path.exists(f"{G1}/L2/{tag}/{tag}_orig.mp4"):
runs.append(dict(name=f"{tag}_orig", first=pick(d, "frame0.png"), ids=pick(d, "ids.npz"),
prompts=pick(d, "prompts.json"), out=f"{G}/L2/{tag}"))
for c in cases:
p = f"{G1}/procmap/{c}"
for s in L3_STYLES:
runs.append(dict(name=f"{c}_{s}", first=f"{G}/L3/{c}/{s}_f0.png", ids=f"{p}/ids.npz",
prompts=f"{p}/prompts.json", out=f"{G}/L3/{c}"))
def gen(i_r):
i, r = i_r
if os.path.exists(f"{r['out']}/{r['name']}.mp4"):
return r["name"]
try:
run([PY, f"{M}/code/gc25d/sample_user.py", "--ckpt", a.ckpt, "--first_frame", r["first"], "--ids", r["ids"],
"--prompts", r["prompts"], "--out", r["out"], "--name", r["name"], "--thumb"],
f"{G}/logs/gen_{r['name']}.log", dict(ENV, CUDA_VISIBLE_DEVICES=gpus[i % len(gpus)]))
print("gen", r["name"], flush=True)
except RuntimeError as e: # 一条失败不拖垮整张卡的队列
print("FAIL", r["name"], e, flush=True)
return r["name"]
queues = [[(i, r) for i, r in enumerate(runs) if i % len(gpus) == g] for g in range(len(gpus))]
with ThreadPoolExecutor(len(gpus)) as ex:
list(ex.map(lambda q: [gen(x) for x in q], queues))
print("ALL DONE", len(runs), flush=True)
if __name__ == "__main__":
sys.exit(main())