Instructions to use diffusion-gif/any2ego with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use diffusion-gif/any2ego with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("fill-in-base-model", dtype=torch.bfloat16, device_map="cuda") pipe.load_lora_weights("diffusion-gif/any2ego") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- Draw Things
File size: 9,044 Bytes
c28ffff | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | """"""
from pathlib import Path as _Path
import sys as _sys
_bundle_root = _Path(__file__).resolve().parents[1]
_sys.path.insert(0, str(_bundle_root))
from project_paths import project_path as _project_path, env as _env, initialize as _initialize
_initialize(__name__ == '__main__')
from project_paths import seg_mask_root, seg_mask_dir
import os
import sys
import json
import argparse
import numpy as np
import torch
from PIL import Image
from model_resources import wan_base_root, wan_model_paths, wan_tokenizer_path
VACE = wan_base_root()
DS = _project_path('third_party/DiffSynth-Studio')
HERE = os.path.dirname(os.path.abspath(__file__))
EVAL_MANIFEST = _project_path('manifests/eval.json')
MASK_ROOT = seg_mask_root()
CAPTION_JSON = _project_path('data/captions/grpo.json')
PALETTE = np.array([[0, 0, 0], [220, 40, 40], [40, 200, 80]], dtype=np.uint8)
NEG = '色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,鱼眼畸变,鱼眼镜头,广角畸变,桶形畸变,镜头畸变,边缘拉伸变形,画面弯曲,fisheye distortion,wide-angle distortion,lens distortion,barrel distortion,可见的相机,三脚架,GoPro,运动相机'
def build_pipe(device, lora_path, branch_ckpt, caption_dim=4096):
sys.path.insert(0, DS)
sys.path.insert(0, HERE)
from diffsynth.pipelines.wan_video import WanVideoPipeline, ModelConfig
from caption_cross_attn import inject_caption_cross_attn
pipe = WanVideoPipeline.from_pretrained(torch_dtype=torch.bfloat16, device=device, model_configs=[ModelConfig(path=path) for path in wan_model_paths(VACE, strict=True)], tokenizer_config=ModelConfig(path=wan_tokenizer_path(VACE, strict=True)))
if lora_path:
pipe.load_lora(pipe.vace, lora_path, alpha=1.0)
print(f'已加载 Stage1 底座 LoRA: {lora_path}')
dit = pipe.dit
dev = next(dit.parameters()).device
dtype = next(dit.parameters()).dtype
from safetensors.torch import load_file
sd = load_file(branch_ckpt)
ckpt_has_route = any(('spatial_route' in k for k in sd.keys()))
env_route = os.environ.get('USE_SPATIAL_ROUTE')
use_spatial_route = env_route != '0' if env_route is not None else ckpt_has_route
inject_caption_cross_attn(dit, caption_dim=caption_dim, vace_module=pipe.vace, use_spatial_route=use_spatial_route)
for block in dit.blocks:
block.cross_attn._cap_branch.to(device=dev, dtype=dtype)
dit_sd = dit.state_dict()
n_cap = 0
miss = []
for k, v in sd.items():
kk = k
for pre in ('pipe.dit.', 'dit.', 'model.'):
if kk.startswith(pre):
kk = kk[len(pre):]
if kk in dit_sd:
dit_sd[kk].copy_(v.to(dit_sd[kk].dtype).to(dit_sd[kk].device))
if '_cap_branch' in kk:
n_cap += 1
else:
miss.append(k)
print(f'[caption ckpt] 加载 caption {n_cap}/{len(sd)} 个参数 <- {branch_ckpt}')
if miss:
print(f'[caption ckpt] 未匹配 {len(miss)} 个 key, 样本: {miss[:3]}')
for block in dit.blocks:
cb = block.cross_attn._cap_branch
cb.gate_cap.data = cb.gate_cap.data.float()
print(f'[caption ckpt] block0 gate_cap={dit.blocks[0].cross_attn._cap_branch.gate_cap.item():+.4e}')
return pipe
@torch.no_grad()
def encode_caption(pipe, cap):
""""""
if cap is None:
return None
cap = str(cap).strip()
if len(cap) == 0:
return None
pipe.load_models_to_device(('text_encoder',))
ids, mask = pipe.tokenizer(cap, return_mask=True, add_special_tokens=True)
ids = ids.to(pipe.device)
mask = mask.to(pipe.device)
seq_lens = mask.gt(0).sum(dim=1).long()
feat = pipe.text_encoder(ids, mask)
for i, v in enumerate(seq_lens):
feat[:, v:] = 0
return feat.to(device=pipe.device, dtype=pipe.torch_dtype)
def load_mask_video(mask_dir, size):
""""""
fs = sorted((f for f in os.listdir(mask_dir) if f.endswith('.png')))
out = []
for f in fs:
m = np.asarray(Image.open(os.path.join(mask_dir, f)))
if m.ndim == 3:
m = m[..., 0]
col = PALETTE[np.clip(m, 0, 2)]
out.append(Image.fromarray(col).resize(size))
return out
def main():
mode_parser = argparse.ArgumentParser(add_help=False)
modes = ['global', 'spatial']
mode_parser.add_argument('--variant', choices=modes, default='spatial')
mode, _ = mode_parser.parse_known_args()
branch = 'global' if mode.variant == 'global' else 'spatial'
ap = argparse.ArgumentParser()
ap.add_argument('--variant', choices=modes, default=mode.variant)
ap.add_argument('--lora', default=_project_path('checkpoints/generator_hoi/ckpt.safetensors'), help='Stage 1 HOI 底座,与分支训练的 preset_lora_path 一致')
ap.add_argument('--branch_ckpt', default=_project_path(f'checkpoints/generator_{branch}/ckpt.safetensors'))
ap.add_argument('--out', default=_project_path(f'outputs/pred_{mode.variant}'))
ap.add_argument('--manifest', default=EVAL_MANIFEST)
ap.add_argument('--caption_json', default=CAPTION_JSON)
ap.add_argument('--caption_dim', type=int, default=4096)
ap.add_argument('--height', type=int, default=480)
ap.add_argument('--width', type=int, default=480)
ap.add_argument('--steps', type=int, default=50)
ap.add_argument('--cfg_scale', type=float, default=5.0)
ap.add_argument('--vace_scale', type=float, default=1.0)
ap.add_argument('--fps', type=float, default=8.0)
ap.add_argument('--seed', type=int, default=0)
ap.add_argument('--limit', type=int, default=-1)
ap.add_argument('--mask_root', default=MASK_ROOT, help='外部 seg 预测 mask 根目录:<root>/<clip_id>/ego/*.png')
ap.add_argument('--device', default='cuda')
args = ap.parse_args()
os.environ['USE_SPATIAL_ROUTE'] = '0' if args.variant == 'global' else '1'
sys.path.insert(0, HERE)
from caption_cross_attn import set_caption_context
manifest = json.load(open(args.manifest))
if args.limit > 0:
manifest = manifest[:args.limit]
captions = json.load(open(args.caption_json))
out = os.path.abspath(args.out)
os.makedirs(out, exist_ok=True)
pipe = build_pipe(args.device, args.lora, args.branch_ckpt, args.caption_dim)
size = (args.width, args.height)
print(f'开始 caption 旁路推理, {len(manifest)} 条 | cfg={args.cfg_scale}')
n_done = 0
for k, s in enumerate(manifest):
uid = s['take_uid']
if os.path.isfile(os.path.join(out, uid, 'pred.mp4')):
print(f' [skip] 已存在: {uid}')
n_done += 1
continue
mask_dir = os.path.join(args.mask_root, uid, 'ego')
if not os.path.isdir(mask_dir):
print(f' [skip] 无mask: {s['take_name']}')
continue
cap = captions.get(uid)
if cap is None:
print(f' [skip] 无caption: {s['take_name']}')
continue
mask_vid = load_mask_video(mask_dir, size)
nf = len(mask_vid)
ref = Image.open(s['first_frame']).convert('RGB').resize(size)
cap_feat = encode_caption(pipe, cap)
set_caption_context(pipe.dit, cap_feat)
cap_shape = tuple(cap_feat.shape) if cap_feat is not None else 'None(旁路关)'
print(f"[{k + 1}/{len(manifest)}] {s['take_name']} | {nf}帧 | variant={args.variant} | cap{cap_shape} | '{s['prompt'][:40]}'")
video = pipe(prompt=s['prompt'], negative_prompt=NEG, vace_video=mask_vid, vace_reference_image=ref, vace_scale=args.vace_scale, height=args.height, width=args.width, num_frames=nf, num_inference_steps=args.steps, cfg_scale=args.cfg_scale, seed=args.seed, tiled=True)
sd = os.path.join(out, uid)
os.makedirs(sd, exist_ok=True)
for i, fr in enumerate(video):
Image.fromarray(np.asarray(fr)).save(os.path.join(sd, f'{i:03d}.png'))
try:
from diffsynth.utils.data import save_video
save_video(video, os.path.join(sd, 'pred.mp4'), fps=args.fps, quality=5)
except Exception:
pass
n_done += 1
print(f' -> {sd}')
json.dump({'model': 'Wan2.1-VACE-1.3B + HOI-LoRA + caption 旁路', 'lora': args.lora, 'branch_ckpt': args.branch_ckpt, 'caption_json': args.caption_json, 'height': args.height, 'width': args.width, 'rotate': False, 'vace_scale': args.vace_scale, 'steps': args.steps, 'cfg_scale': args.cfg_scale, 'variant': args.variant, 'seed': args.seed, 'num_samples': n_done, 'note': 'ego HOI mask + narration(原生) + ego dense caption(旁路) 引导生成'}, open(os.path.join(out, 'infer_config.json'), 'w'), ensure_ascii=False, indent=2)
print(f'完成 {n_done} 条 ->', out)
if __name__ == '__main__':
main()
|