Download ComfyUI-WanBerniniS2V_v2/model_patch.py from hanxxing/Bernini-R-S2V: direct link, hf CLI and curl.
- Browser
- Download file 9.74 kB
-
https://huggingface.co/hanxxing/Bernini-R-S2V/resolve/main/ComfyUI-WanBerniniS2V_v2/model_patch.py
- Command line
-
hf download hf://hanxxing/Bernini-R-S2V/ComfyUI-WanBerniniS2V_v2/model_patch.py
-
curl -L -o model_patch.py https://huggingface.co/hanxxing/Bernini-R-S2V/resolve/main/ComfyUI-WanBerniniS2V_v2/model_patch.py
9.74 kB
| import inspect | |
| import logging | |
| import torch | |
| import comfy.conds | |
| import comfy.model_management | |
| from comfy.ldm.wan.model import AudioInjector_WAN, WanModel_S2V | |
| from comfy.model_base import WAN22_S2V | |
| def _append_context_latents(self, x, kwargs): | |
| context_latents = kwargs.get("context_latents", None) | |
| if context_latents is None: | |
| return x | |
| for lat in context_latents: | |
| cl = self.patch_embedding(lat.float().to(x.device)).to(x.dtype).flatten(2).transpose(1, 2) | |
| x = torch.cat([x, cl], dim=1) | |
| return x | |
| def _patch_wan_model_s2v_forward(): | |
| if getattr(WanModel_S2V.forward_orig, "__wan_bernini_s2v_v2_patch__", False): | |
| return | |
| try: | |
| source = inspect.getsource(WanModel_S2V.forward_orig) | |
| except (OSError, TypeError): | |
| source = "" | |
| if "context_latents" in source and getattr(WanModel_S2V.forward_orig, "__wan_bernini_s2v_patch__", False): | |
| WanModel_S2V.forward_orig.__wan_bernini_s2v_v2_patch__ = True | |
| return | |
| original = WanModel_S2V.forward_orig | |
| def forward_orig( | |
| self, | |
| x, | |
| t, | |
| context, | |
| audio_embed=None, | |
| reference_latent=None, | |
| control_video=None, | |
| reference_motion=None, | |
| clip_fea=None, | |
| freqs=None, | |
| transformer_options={}, | |
| **kwargs, | |
| ): | |
| if audio_embed is not None: | |
| num_embeds = x.shape[-3] * 4 | |
| audio_emb_global, audio_emb = self.casual_audio_encoder(audio_embed[:, :, :, :num_embeds]) | |
| else: | |
| audio_emb = None | |
| audio_emb_global = None | |
| bs, _, time, height, width = x.shape | |
| x = self.patch_embedding(x.float()).to(x.dtype) | |
| if control_video is not None: | |
| x = x + self.cond_encoder(control_video) | |
| if t.ndim == 1: | |
| t = t.unsqueeze(1).repeat(1, x.shape[2]) | |
| grid_sizes = x.shape[2:] | |
| x = x.flatten(2).transpose(1, 2) | |
| seq_len = x.size(1) | |
| cond_mask_weight = comfy.model_management.cast_to(self.trainable_cond_mask.weight, dtype=x.dtype, device=x.device).unsqueeze(1).unsqueeze(1) | |
| x = x + cond_mask_weight[0] | |
| x = _append_context_latents(self, x, kwargs) | |
| if reference_latent is not None: | |
| ref = self.patch_embedding(reference_latent.float()).to(x.dtype) | |
| ref = ref.flatten(2).transpose(1, 2) | |
| freqs_ref = self.rope_encode(reference_latent.shape[-3], reference_latent.shape[-2], reference_latent.shape[-1], t_start=max(30, time + 9), device=x.device, dtype=x.dtype) | |
| ref = ref + cond_mask_weight[1] | |
| x = torch.cat([x, ref], dim=1) | |
| freqs = torch.cat([freqs, freqs_ref], dim=1) | |
| t = torch.cat([t, torch.zeros((t.shape[0], reference_latent.shape[-3]), device=t.device, dtype=t.dtype)], dim=1) | |
| if reference_motion is not None: | |
| motion_encoded, freqs_motion = self.frame_packer(reference_motion, self) | |
| motion_encoded = motion_encoded + cond_mask_weight[2] | |
| x = torch.cat([x, motion_encoded], dim=1) | |
| freqs = torch.cat([freqs, freqs_motion], dim=1) | |
| t = torch.repeat_interleave(t, 2, dim=1) | |
| t = torch.cat([t, torch.zeros((t.shape[0], 3), device=t.device, dtype=t.dtype)], dim=1) | |
| from comfy.ldm.wan.model import sinusoidal_embedding_1d | |
| e = self.time_embedding( | |
| sinusoidal_embedding_1d(self.freq_dim, t.flatten()).to(dtype=x[0].dtype)) | |
| e = e.reshape(t.shape[0], -1, e.shape[-1]) | |
| e0 = self.time_projection(e).unflatten(2, (6, self.dim)) | |
| context = self.text_embedding(context) | |
| patches_replace = transformer_options.get("patches_replace", {}) | |
| blocks_replace = patches_replace.get("dit", {}) | |
| transformer_options["total_blocks"] = len(self.blocks) | |
| transformer_options["block_type"] = "double" | |
| for i, block in enumerate(self.blocks): | |
| transformer_options["block_index"] = i | |
| if ("double_block", i) in blocks_replace: | |
| def block_wrap(args): | |
| out = {} | |
| out["img"] = block(args["img"], context=args["txt"], e=args["vec"], freqs=args["pe"], transformer_options=args["transformer_options"]) | |
| return out | |
| out = blocks_replace[("double_block", i)]({"img": x, "txt": context, "vec": e0, "pe": freqs, "transformer_options": transformer_options}, {"original_block": block_wrap}) | |
| x = out["img"] | |
| else: | |
| x = block(x, e=e0, freqs=freqs, context=context, transformer_options=transformer_options) | |
| if audio_emb is not None: | |
| inject_scale = kwargs.get("audio_inject_scale", 1.0) | |
| if isinstance(inject_scale, torch.Tensor): | |
| inject_scale = inject_scale.reshape(-1)[0].item() | |
| x = self.audio_injector( | |
| x, i, audio_emb, audio_emb_global, seq_len, | |
| scale=inject_scale, | |
| token_mask=kwargs.get("audio_inject_mask", None), | |
| ) | |
| x = self.head(x, e) | |
| x = self.unpatchify(x, grid_sizes) | |
| return x | |
| forward_orig.__wan_bernini_s2v_v2_patch__ = True | |
| forward_orig.__wan_bernini_s2v_patch__ = True | |
| forward_orig.__wan_bernini_s2v_original__ = original | |
| WanModel_S2V.forward_orig = forward_orig | |
| def _patch_audio_injector(): | |
| if getattr(AudioInjector_WAN.forward, "__wan_bernini_s2v_v2_masked_patch__", False): | |
| return | |
| original_forward = AudioInjector_WAN.forward | |
| def forward(self, x, block_id, audio_emb, audio_emb_global, seq_len, scale=1.0, token_mask=None): | |
| if token_mask is None: | |
| return original_forward(self, x, block_id, audio_emb, audio_emb_global, seq_len, scale=scale) | |
| audio_attn_id = self.injected_block_id.get(block_id, None) | |
| if audio_attn_id is None: | |
| return x | |
| from einops import rearrange | |
| num_frames = audio_emb.shape[1] | |
| input_hidden_states = rearrange(x[:, :seq_len], "b (t n) c -> (b t) n c", t=num_frames) | |
| if self.enable_adain and self.adain_mode == "attn_norm": | |
| audio_emb_global = rearrange(audio_emb_global, "b t n c -> (b t) n c") | |
| adain_hidden_states = self.injector_adain_layers[audio_attn_id](input_hidden_states, temb=audio_emb_global[:, 0]) | |
| attn_hidden_states = adain_hidden_states | |
| else: | |
| attn_hidden_states = self.injector_pre_norm_feat[audio_attn_id](input_hidden_states) | |
| if audio_emb.dim() == 3: | |
| attn_audio_emb = rearrange(audio_emb, "b t c -> (b t) 1 c", t=num_frames) | |
| else: | |
| attn_audio_emb = rearrange(audio_emb, "b t n c -> (b t) n c", t=num_frames) | |
| residual_out = self.injector[audio_attn_id](x=attn_hidden_states, context=attn_audio_emb) | |
| residual_out = rearrange(residual_out, "(b t) n c -> b (t n) c", t=num_frames) | |
| if token_mask.ndim == 4: | |
| token_mask = token_mask.flatten(1, 2) | |
| if token_mask.shape[1] == residual_out.shape[1]: | |
| residual_out = residual_out * token_mask.to(device=residual_out.device, dtype=residual_out.dtype) | |
| else: | |
| logging.warning( | |
| "ComfyUI-WanBerniniS2V_v2: mask length %s does not match token count %s; using global audio injection", | |
| token_mask.shape[1], | |
| residual_out.shape[1], | |
| ) | |
| x[:, :seq_len] = x[:, :seq_len] + residual_out * scale | |
| return x | |
| forward.__wan_bernini_s2v_v2_masked_patch__ = True | |
| forward.__wan_bernini_s2v_masked_patch__ = True | |
| forward.__wan_bernini_s2v_masked_original__ = original_forward | |
| AudioInjector_WAN.forward = forward | |
| def _patch_wan22_s2v_extra_conds(): | |
| if getattr(WAN22_S2V.extra_conds, "__wan_bernini_s2v_v2_masked_patch__", False): | |
| return | |
| original_extra_conds = WAN22_S2V.extra_conds | |
| def extra_conds(self, **kwargs): | |
| out = original_extra_conds(self, **kwargs) | |
| audio_inject_mask = kwargs.get("audio_inject_mask", None) | |
| if audio_inject_mask is not None: | |
| out["audio_inject_mask"] = comfy.conds.CONDRegular(audio_inject_mask) | |
| audio_inject_scale = kwargs.get("audio_inject_scale", None) | |
| if audio_inject_scale is not None: | |
| out["audio_inject_scale"] = comfy.conds.CONDRegular(torch.FloatTensor([audio_inject_scale])) | |
| return out | |
| extra_conds.__wan_bernini_s2v_v2_masked_patch__ = True | |
| extra_conds.__wan_bernini_s2v_masked_patch__ = True | |
| extra_conds.__wan_bernini_s2v_masked_original__ = original_extra_conds | |
| WAN22_S2V.extra_conds = extra_conds | |
| original_resize = WAN22_S2V.resize_cond_for_context_window | |
| def resize_cond_for_context_window(self, cond_key, cond_value, window, x_in, device, retain_index_list=[]): | |
| if cond_key == "audio_inject_mask": | |
| mask = cond_value.cond | |
| if mask.ndim == 4 and mask.shape[1] == x_in.shape[2]: | |
| return cond_value._copy_with(window.get_tensor(mask, device, dim=1)) | |
| return original_resize(self, cond_key, cond_value, window, x_in, device, retain_index_list=retain_index_list) | |
| resize_cond_for_context_window.__wan_bernini_s2v_v2_masked_patch__ = True | |
| resize_cond_for_context_window.__wan_bernini_s2v_masked_patch__ = True | |
| resize_cond_for_context_window.__wan_bernini_s2v_masked_original__ = original_resize | |
| WAN22_S2V.resize_cond_for_context_window = resize_cond_for_context_window | |
| def apply_model_patches(): | |
| _patch_wan_model_s2v_forward() | |
| _patch_audio_injector() | |
| _patch_wan22_s2v_extra_conds() | |
| logging.info("ComfyUI-WanBerniniS2V_v2: applied Bernini S2V model patches") |