This commit is contained in:
songkey-win
2024-11-05 12:42:23 +08:00
parent cf2d195599
commit 258f1e69ce
28 changed files with 4453 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
*.pyc
*.pyd
*.pth
*.pkl
*_fps15.mp4
.idea/
data/results
pretrained_models/
+37
View File
@@ -0,0 +1,37 @@
<h1 align='center'>HelloMeme: Integrating Spatial Knitting Attentions to Embed High-Level and Fidelity-Rich Conditions in Diffusion Models</h1>
<div align='center'>
<a href='https://github.com/songkey' target='_blank'>Shengkai Zhang</a>, <a href='https://github.com/RhythmJnh' target='_blank'>Nianhong Jiao</a>, <a href='https://github.com/Shelton0215' target='_blank'>Tian Li</a>, <a href='https://github.com/chaojie12131243' target='_blank'>Chaojie Yang</a>, <a href='https://github.com/xchgit' target='_blank'>Chenhui Xue</a><sup>*</sup>, <a href='https://github.com/boya34' target='_blank'>Boya Niu</a><sup>*</sup>, <a href='https://github.com/HelloVision/HelloMeme' target='_blank'>Jun Gao</a>
</div>
<div align='center'>
HelloVision | HelloGroup Inc.
</div>
<div align='center'>
<small><sup>*</sup> Intern</small>
</div>
<br>
<div align='center'>
<a href='https://songkey.github.io/hellomeme/'><img src='https://img.shields.io/badge/Project-HomePage-Green'></a>
<a href='https://arxiv.org/pdf/2410.22901'><img src='https://img.shields.io/badge/Paper-Arxiv-red'></a>
<a href='https://huggingface.co/songkey'><img src='https://img.shields.io/badge/%F0%9F%A4%97%20HuggingFace-Model-yellow'></a>
</div>
## Introduction
This repository is the official implementation of the [`HelloMeme`](https://arxiv.org/pdf/2410.22901) ComfyUI interface, featuring both image and video generation functionalities. Example workflow files can be found in the `ComfyUI_HelloMeme/workflows` directory. Test images and videos are saved in the `ComfyUI_HelloMeme/examples` directory. Below are screenshots of the interfaces for image and video generation.
### Image Generation Interface
<p align="center">
<img src="workflows/hellomeme_image_example.jpg" alt="image_generation_interface">
</p>
### Video Generation Interface
<p align="center">
<img src="workflows/hellomeme_video_example.jpg" alt="video_generation_interface">
</p>
+3
View File
@@ -0,0 +1,3 @@
from .meme import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
BIN
View File
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

+12
View File
@@ -0,0 +1,12 @@
# coding: utf-8
"""
@File : __init__.py.py
@Author : Songkey
@Email : songkey@pku.edu.cn
@Date : 8/28/2024
@Desc :
"""
from .piplines import HMVideoPipeline, HMImagePipeline
from .models import HMDenoising3D, HMDenoisingMotion, HMControlNet, HMReferenceAdapter
+14
View File
@@ -0,0 +1,14 @@
# coding: utf-8
"""
@File : __init__.py
@Author : Songkey
@Email : songkey@pku.edu.cn
@Date : 8/14/2024
@Desc :
"""
from .hm_denoising_motion import HMDenoisingMotion
from .hm_control import HMControlNet
from .hm_blocks import HMReferenceAdapter
from .hm_denoising_3d import HMDenoising3D
+330
View File
@@ -0,0 +1,330 @@
# coding: utf-8
"""
@File : models6/hm_blocks.py
@Author : Songkey
@Email : songkey@pku.edu.cn
@Date : 8/14/2024
@Desc :
"""
import copy
import torch
import torch.nn as nn
import torch.utils.checkpoint
import torch.nn.functional as F
from typing import Optional, Tuple, Union
from einops import rearrange
from diffusers.models.modeling_utils import ModelMixin
from diffusers.configuration_utils import ConfigMixin, register_to_config
from diffusers.models.attention import Attention, FeedForward
from torchvision.models.resnet import BasicBlock
from diffusers.models.embeddings import SinusoidalPositionalEmbedding
from diffusers.utils import is_transformers_available, logging
from diffusers.models.unets.unet_2d_condition import UNet2DConditionModel
from diffusers.configuration_utils import FrozenDict
from diffusers.models.attention_processor import (
AttnProcessor,
AttnProcessor2_0,
)
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
class HMReferenceAdapter(ModelMixin, ConfigMixin):
@register_to_config
def __init__(self,
block_out_channels: Tuple[int] = (320, 640, 1280, 1280),
down_block_types: Tuple[str] = (
"CrossAttnDownBlock2D",
"CrossAttnDownBlock2D",
"CrossAttnDownBlock2D",
"DownBlock2D",
),
up_block_types: Tuple[str] = (
"UpBlock2D",
"CrossAttnUpBlock2D",
"CrossAttnUpBlock2D",
"CrossAttnUpBlock2D"),
num_attention_heads: Optional[Union[int, Tuple[int]]] = 8,
):
super().__init__()
self.reference_modules_down = nn.ModuleList([])
if isinstance(num_attention_heads, int):
num_attention_heads = (num_attention_heads,) * len(down_block_types)
for i, down_block_type in enumerate(down_block_types):
output_channel = block_out_channels[i]
self.reference_modules_down.append(
SKReferenceAttention(
in_channels=output_channel,
num_attention_heads=num_attention_heads[i],
)
)
self.reference_modules_mid = SKReferenceAttention(
in_channels=block_out_channels[-1],
num_attention_heads=num_attention_heads[-1],
)
self.reference_modules_up = nn.ModuleList([])
reversed_block_out_channels = list(reversed(block_out_channels))
reversed_num_attention_heads = list(reversed(num_attention_heads))
output_channel = reversed_block_out_channels[0]
for i, up_block_type in enumerate(up_block_types):
prev_output_channel = output_channel
output_channel = reversed_block_out_channels[i]
if i > 0:
self.reference_modules_up.append(
SKReferenceAttention(
in_channels=prev_output_channel,
num_attention_heads=reversed_num_attention_heads[i],
num_positional_embeddings=64 * 2
)
)
class SKReferenceAttention(nn.Module):
def __init__(
self,
in_channels: int,
num_attention_heads: int = 1,
norm_elementwise_affine: bool = True,
norm_eps: float = 1e-5,
num_positional_embeddings: int = 64*2,
):
super().__init__()
self.pos_embed = SinusoidalPositionalEmbedding(in_channels, max_seq_length=num_positional_embeddings)
self.attn1 = Attention(
query_dim=in_channels,
heads=num_attention_heads,
dim_head=in_channels // num_attention_heads,
dropout=0.0,
bias=False,
cross_attention_dim=None,
upcast_attention=False,
out_bias=True,
)
self.attn2 = Attention(
query_dim=in_channels,
heads=num_attention_heads,
dim_head=in_channels // num_attention_heads,
dropout=0.0,
bias=False,
cross_attention_dim=None,
upcast_attention=False,
out_bias=True,
)
# self.ff = FeedForward(
# in_channels,
# dropout=0.0,
# activation_fn="geglu",
# final_dropout=False,
# inner_dim=in_channels*2,
# bias=True,
# )
self.norm = nn.LayerNorm(in_channels, elementwise_affine=norm_elementwise_affine, eps=norm_eps)
self.proj = zero_module(nn.Conv2d(in_channels, in_channels, kernel_size=3, padding=1))
def forward(self, hidden_states, ref_stats, num_frames):
h, w = hidden_states.shape[-2:]
ref_stats = ref_stats.repeat_interleave(num_frames, dim=0)
cat_stats = torch.cat([hidden_states, ref_stats], dim=-1)
cat_stats = rearrange(cat_stats.contiguous(), "b c h w -> (b h) w c")
res1 = self.attn1(self.norm(self.pos_embed(cat_stats)))
res1 = rearrange(res1[:, :w, :], "(b h) w c -> b c h w", h=h)
cat_stats2 = torch.cat([res1, ref_stats], dim=-2)
cat_stats2 = rearrange(cat_stats2.contiguous(), "b c h w -> (b w) h c")
res2 = self.attn2(self.norm(self.pos_embed(cat_stats2)))
res2 = rearrange(res2[:, :h, :], "(b w) h c -> b c h w", w=w)
# res2 = self.ff(res2)
return hidden_states + self.proj(res2)
class SKCrossAttention(nn.Module):
def __init__(
self,
channel_in,
channel_out,
heads: int=8,
cross_attention_dim: int = 320,
norm_elementwise_affine: bool = True,
norm_eps: float = 1e-5,
num_positional_embeddings: int = 64,
num_positional_embeddings_hidden: int = 64,
):
super().__init__()
self.conv = BasicBlock(
inplanes=channel_in,
planes=channel_out,
stride=2,
downsample=nn.Sequential(
nn.Conv2d(channel_in, channel_out, kernel_size=1, stride=2, bias=False),
nn.InstanceNorm2d(channel_out),
nn.SiLU(),
),
norm_layer=nn.InstanceNorm2d,
)
self.pos_embed = SinusoidalPositionalEmbedding(channel_out, max_seq_length=num_positional_embeddings)
self.pos_embed_hidden = SinusoidalPositionalEmbedding(cross_attention_dim, max_seq_length=num_positional_embeddings_hidden)
self.norm1 = nn.LayerNorm(channel_out, elementwise_affine=norm_elementwise_affine, eps=norm_eps)
self.attn1 = Attention(
query_dim=channel_out,
heads=heads,
dim_head=channel_out // heads,
dropout=0.0,
bias=False,
cross_attention_dim=cross_attention_dim,
upcast_attention=False,
out_bias=True,
)
self.norm2 = nn.LayerNorm(channel_out, elementwise_affine=norm_elementwise_affine, eps=norm_eps)
self.attn2 = Attention(
query_dim=channel_out,
heads=heads,
dim_head=channel_out // heads,
dropout=0.0,
bias=False,
cross_attention_dim=cross_attention_dim,
upcast_attention=False,
out_bias=True,
)
self.ff = FeedForward(
channel_out,
dropout=0.0,
activation_fn="geglu",
final_dropout=False,
inner_dim=channel_out*2,
bias=True,
)
self.proj = zero_module(nn.Conv2d(channel_out, channel_out, kernel_size=3, padding=1))
def forward(self, input, hidden_stats):
x = self.conv(input)
h, w = x.shape[-2:]
x = rearrange(x, "b c h w -> (b h) w c")
x = self.attn1(self.norm1(self.pos_embed(x)), self.pos_embed_hidden(hidden_stats.repeat_interleave(h, dim=0).contiguous()))
x = rearrange(x, "(b h) w c -> (b w) h c", h=h)
x = self.ff(self.attn2(self.norm2(self.pos_embed(x)), self.pos_embed_hidden(hidden_stats.repeat_interleave(w, dim=0).contiguous())))
x = rearrange(x, "(b w) h c -> b c h w", w=w)
x = self.proj(x)
return x
# https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/controlnet.py
def zero_module(module):
for p in module.parameters():
nn.init.zeros_(p)
return module
# https://github.com/huggingface/diffusers/blob/82058a5413ca09561cc5cc236c4abc5eeda7b209/src/diffusers/loaders/ip_adapter.py
if is_transformers_available():
from diffusers.models.attention_processor import (
IPAdapterAttnProcessor,
IPAdapterAttnProcessor2_0,
)
class CopyWeights(object):
@classmethod
def from_unet2d(cls, unet: UNet2DConditionModel):
# adapted from :https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/unets/unet_motion_model.py
config = dict(unet.config)
# Need this for backwards compatibility with UNet2DConditionModel checkpoints
if not config.get("num_attention_heads"):
config["num_attention_heads"] = config["attention_head_dim"]
config = FrozenDict(config)
model = cls.from_config(config)
model.conv_in.load_state_dict(unet.conv_in.state_dict())
model.time_proj.load_state_dict(unet.time_proj.state_dict())
model.time_embedding.load_state_dict(unet.time_embedding.state_dict())
if any(
isinstance(proc, (IPAdapterAttnProcessor, IPAdapterAttnProcessor2_0))
for proc in unet.attn_processors.values()
):
attn_procs = {}
for name, processor in unet.attn_processors.items():
if name.endswith("attn1.processor"):
attn_processor_class = (
AttnProcessor2_0 if hasattr(F, "scaled_dot_product_attention") else AttnProcessor
)
attn_procs[name] = attn_processor_class()
else:
attn_processor_class = (
IPAdapterAttnProcessor2_0
if hasattr(F, "scaled_dot_product_attention")
else IPAdapterAttnProcessor
)
attn_procs[name] = attn_processor_class(
hidden_size=processor.hidden_size,
cross_attention_dim=processor.cross_attention_dim,
scale=processor.scale,
num_tokens=processor.num_tokens,
)
for name, processor in model.attn_processors.items():
if name not in attn_procs:
attn_procs[name] = processor.__class__()
model.set_attn_processor(attn_procs)
model.config.encoder_hid_dim_type = "ip_image_proj"
model.encoder_hid_proj = unet.encoder_hid_proj
for i, down_block in enumerate(unet.down_blocks):
model.down_blocks[i].resnets.load_state_dict(down_block.resnets.state_dict())
if hasattr(model.down_blocks[i], "attentions"):
model.down_blocks[i].attentions.load_state_dict(down_block.attentions.state_dict())
if model.down_blocks[i].downsamplers:
model.down_blocks[i].downsamplers.load_state_dict(down_block.downsamplers.state_dict())
for i, up_block in enumerate(unet.up_blocks):
model.up_blocks[i].resnets.load_state_dict(up_block.resnets.state_dict())
if hasattr(model.up_blocks[i], "attentions"):
model.up_blocks[i].attentions.load_state_dict(up_block.attentions.state_dict())
if model.up_blocks[i].upsamplers:
model.up_blocks[i].upsamplers.load_state_dict(up_block.upsamplers.state_dict())
model.mid_block.resnets.load_state_dict(unet.mid_block.resnets.state_dict())
model.mid_block.attentions.load_state_dict(unet.mid_block.attentions.state_dict())
if unet.conv_norm_out is not None:
model.conv_norm_out.load_state_dict(unet.conv_norm_out.state_dict())
if unet.conv_act is not None:
model.conv_act.load_state_dict(unet.conv_act.state_dict())
model.conv_out.load_state_dict(unet.conv_out.state_dict())
# ensure that the Motion UNet is the same dtype as the UNet2DConditionModel
model.to(unet.dtype)
return model
class InsertReferenceAdapter(object):
def __init__(self):
self.reference_modules_down = None
self.reference_modules_mid = None
self.reference_modules_up = None
def insert_reference_adapter(self, adapter: HMReferenceAdapter):
self.reference_modules_down = copy.deepcopy(adapter.reference_modules_down)
self.reference_modules_mid = copy.deepcopy(adapter.reference_modules_mid)
self.reference_modules_up = copy.deepcopy(adapter.reference_modules_up)
+88
View File
@@ -0,0 +1,88 @@
# coding: utf-8
"""
@File : models6/hm_control.py
@Author : Songkey
@Email : songkey@pku.edu.cn
@Date : 8/14/2024
@Desc :
"""
from typing import Tuple
import torch
import torch.nn as nn
import torch.nn.functional as F
from diffusers.models.modeling_utils import ModelMixin
from diffusers.configuration_utils import ConfigMixin, register_to_config
from diffusers.models.embeddings import TimestepEmbedding, Timesteps
from einops import rearrange
from .hm_blocks import SKCrossAttention
class HMControlNet(ModelMixin, ConfigMixin):
@register_to_config
def __init__(
self,
embedding_channels: int,
input_channels: int = 3,
scale_factor: int = 8,
cross_attention_dim: int = 320,
block_out_channels: Tuple[int] = (128, 320, 640, 1280),
):
super().__init__()
self.scale_factor = scale_factor
self.embedding_channels = embedding_channels
self.cross_attention_dim = cross_attention_dim
self.conv_in = nn.Conv2d(input_channels, block_out_channels[0], kernel_size=3, padding=1, bias=False)
self.exp_embedding = Timesteps(cross_attention_dim, True, 0)
self.exp_proj = TimestepEmbedding(cross_attention_dim, cross_attention_dim)
self.face_proj = TimestepEmbedding(1024, cross_attention_dim)
self.blocks_down = nn.ModuleList([])
for i in range(1, len(block_out_channels)):
channel_in = block_out_channels[i-1]
channel_out = block_out_channels[i]
self.blocks_down.append(
SKCrossAttention(
channel_in=channel_in,
channel_out=channel_out,
cross_attention_dim=cross_attention_dim,
num_positional_embeddings=64,
num_positional_embeddings_hidden=64
)
)
def forward(self, condition, drive_coeff, face_parts):
bs, _, video_length, h, w = condition.shape
condition = rearrange(condition, "b c f h w -> (b f) c h w")
conditioning = F.interpolate(condition,
size=(h // self.scale_factor, w // self.scale_factor),
mode='bilinear',
align_corners=False)
embedding = self.conv_in(conditioning)
drive_coeff = rearrange(drive_coeff * 500., "b f c -> (b f c)")
drive_embedding = self.exp_embedding(drive_coeff).to(dtype=embedding.dtype)
drive_embedding = rearrange(drive_embedding, "(b f c) d -> b f c d",
b=bs, f=video_length, d=self.cross_attention_dim)
face_parts = rearrange(face_parts, "b f c d -> (b f) c d")
face_embedding = self.face_proj(face_parts)
face_embedding = rearrange(face_embedding, "(b f) c d -> b f c d", f=video_length)
drive_embedding = torch.cat([face_embedding, drive_embedding], dim=2)
drive_embedding = rearrange(drive_embedding, "b f c d -> (b f) c d")
drive_embedding = self.exp_proj(drive_embedding)
ret_dict = {}
for idx, block in enumerate(self.blocks_down):
embedding = block(embedding, drive_embedding)
ret_dict[f'down_{idx}'] = rearrange(embedding,
"(b f) c h w -> b c f h w",
f=video_length)
return ret_dict
+289
View File
@@ -0,0 +1,289 @@
# coding: utf-8
"""
@File : models6/hm_denoising_3d.py
@Author : Songkey
@Email : songkey@pku.edu.cn
@Date : 8/14/2024
@Desc : 删除实验代码,精简结构
adapted from: https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/unets/unet_2d_condition.py
"""
import torch
import torch.utils.checkpoint
from typing import Any, Dict, Optional, Tuple, Union
from einops import rearrange
from diffusers.utils import USE_PEFT_BACKEND, deprecate, logging, scale_lora_layers, unscale_lora_layers
from diffusers.models.unets.unet_2d_condition import UNet2DConditionModel, UNet2DConditionOutput
from .hm_blocks import CopyWeights, InsertReferenceAdapter
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
class HMDenoising3D(UNet2DConditionModel, CopyWeights, InsertReferenceAdapter):
def forward(
self,
sample: torch.Tensor,
timestep: Union[torch.Tensor, float, int],
encoder_hidden_states: torch.Tensor,
reference_hidden_states: Optional[dict] = None,
control_hidden_states: Optional[torch.Tensor] = None,
class_labels: Optional[torch.Tensor] = None,
timestep_cond: Optional[torch.Tensor] = None,
attention_mask: Optional[torch.Tensor] = None,
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
added_cond_kwargs: Optional[Dict[str, torch.Tensor]] = None,
down_block_additional_residuals: Optional[Tuple[torch.Tensor]] = None,
mid_block_additional_residual: Optional[torch.Tensor] = None,
down_intrablock_additional_residuals: Optional[Tuple[torch.Tensor]] = None,
encoder_attention_mask: Optional[torch.Tensor] = None,
return_dict: bool = True,
) -> Union[UNet2DConditionOutput, Tuple]:
# By default samples have to be AT least a multiple of the overall upsampling factor.
# The overall upsampling factor is equal to 2 ** (# num of upsampling layers).
# However, the upsampling interpolation output size can be forced to fit any upsampling size
# on the fly if necessary.
default_overall_up_factor = 2**self.num_upsamplers
# upsample size should be forwarded when sample is not a multiple of `default_overall_up_factor`
forward_upsample_size = False
upsample_size = None
for dim in sample.shape[-2:]:
if dim % default_overall_up_factor != 0:
# Forward upsample size to force interpolation output size.
forward_upsample_size = True
break
# ensure attention_mask is a bias, and give it a singleton query_tokens dimension
# expects mask of shape:
# [batch, key_tokens]
# adds singleton query_tokens dimension:
# [batch, 1, key_tokens]
# this helps to broadcast it as a bias over attention scores, which will be in one of the following shapes:
# [batch, heads, query_tokens, key_tokens] (e.g. torch sdp attn)
# [batch * heads, query_tokens, key_tokens] (e.g. xformers or classic attn)
if attention_mask is not None:
# assume that mask is expressed as:
# (1 = keep, 0 = discard)
# convert mask into a bias that can be added to attention scores:
# (keep = +0, discard = -10000.0)
attention_mask = (1 - attention_mask.to(sample.dtype)) * -10000.0
attention_mask = attention_mask.unsqueeze(1)
# convert encoder_attention_mask to a bias the same way we do for attention_mask
if encoder_attention_mask is not None:
encoder_attention_mask = (1 - encoder_attention_mask.to(sample.dtype)) * -10000.0
encoder_attention_mask = encoder_attention_mask.unsqueeze(1)
# 0. center input if necessary
if self.config.center_input_sample:
sample = 2 * sample - 1.0
# 1. time
t_emb = self.get_time_embed(sample=sample, timestep=timestep)
emb = self.time_embedding(t_emb, timestep_cond)
aug_emb = None
class_emb = self.get_class_embed(sample=sample, class_labels=class_labels)
if class_emb is not None:
if self.config.class_embeddings_concat:
emb = torch.cat([emb, class_emb], dim=-1)
else:
emb = emb + class_emb
aug_emb = self.get_aug_embed(
emb=emb, encoder_hidden_states=encoder_hidden_states, added_cond_kwargs=added_cond_kwargs
)
if self.config.addition_embed_type == "image_hint":
aug_emb, hint = aug_emb
sample = torch.cat([sample, hint], dim=1)
emb = emb + aug_emb if aug_emb is not None else emb
if self.time_embed_act is not None:
emb = self.time_embed_act(emb)
num_frames = sample.shape[2]
emb = emb.repeat_interleave(repeats=num_frames, dim=0)
if len(encoder_hidden_states.shape) == 3:
encoder_hidden_states = encoder_hidden_states.repeat_interleave(repeats=num_frames, dim=0)
elif len(encoder_hidden_states.shape) == 4:
encoder_hidden_states = rearrange(encoder_hidden_states, "b f l d -> (b f) l d")
if not added_cond_kwargs is None and 'image_embeds' in added_cond_kwargs:
if isinstance(added_cond_kwargs['image_embeds'], torch.Tensor):
added_cond_kwargs['image_embeds'] = added_cond_kwargs['image_embeds'].repeat_interleave(repeats=num_frames, dim=0)
else:
added_cond_kwargs['image_embeds'] = [x.repeat_interleave(repeats=num_frames, dim=0) for x in added_cond_kwargs['image_embeds']]
encoder_hidden_states = self.process_encoder_hidden_states(
encoder_hidden_states=encoder_hidden_states, added_cond_kwargs=added_cond_kwargs
)
# 2. pre-process
sample = rearrange(sample, "b c f h w -> (b f) c h w")
sample = self.conv_in(sample)
# 2.5 GLIGEN position net
if cross_attention_kwargs is not None and cross_attention_kwargs.get("gligen", None) is not None:
cross_attention_kwargs = cross_attention_kwargs.copy()
gligen_args = cross_attention_kwargs.pop("gligen")
cross_attention_kwargs["gligen"] = {"objs": self.position_net(**gligen_args)}
# 3. down
# we're popping the `scale` instead of getting it because otherwise `scale` will be propagated
# to the internal blocks and will raise deprecation warnings. this will be confusing for our users.
if cross_attention_kwargs is not None:
cross_attention_kwargs = cross_attention_kwargs.copy()
lora_scale = cross_attention_kwargs.pop("scale", 1.0)
else:
lora_scale = 1.0
if USE_PEFT_BACKEND:
# weight the lora layers by setting `lora_scale` for each PEFT layer
scale_lora_layers(self, lora_scale)
is_controlnet = mid_block_additional_residual is not None and down_block_additional_residuals is not None
# using new arg down_intrablock_additional_residuals for T2I-Adapters, to distinguish from controlnets
is_adapter = down_intrablock_additional_residuals is not None
# maintain backward compatibility for legacy usage, where
# T2I-Adapter and ControlNet both use down_block_additional_residuals arg
# but can only use one or the other
if not is_adapter and mid_block_additional_residual is None and down_block_additional_residuals is not None:
deprecate(
"T2I should not use down_block_additional_residuals",
"1.3.0",
"Passing intrablock residual connections with `down_block_additional_residuals` is deprecated \
and will be removed in diffusers 1.3.0. `down_block_additional_residuals` should only be used \
for ControlNet. Please make sure use `down_intrablock_additional_residuals` instead. ",
standard_warn=False,
)
down_intrablock_additional_residuals = down_block_additional_residuals
is_adapter = True
res_cache = dict()
down_block_res_samples = (sample,)
for idx, downsample_block in enumerate(self.down_blocks):
if hasattr(downsample_block, "has_cross_attention") and downsample_block.has_cross_attention:
# For t2i-adapter CrossAttnDownBlock2D
additional_residuals = {}
if is_adapter and len(down_intrablock_additional_residuals) > 0:
additional_residuals["additional_residuals"] = down_intrablock_additional_residuals.pop(0)
sample, res_samples = downsample_block(
hidden_states=sample,
temb=emb,
encoder_hidden_states=encoder_hidden_states,
attention_mask=attention_mask,
cross_attention_kwargs=cross_attention_kwargs,
encoder_attention_mask=encoder_attention_mask,
**additional_residuals,
)
res_cache[f"down_{idx}"] = sample.clone()
else:
sample, res_samples = downsample_block(hidden_states=sample, temb=emb)
if is_adapter and len(down_intrablock_additional_residuals) > 0:
sample += down_intrablock_additional_residuals.pop(0)
if not control_hidden_states is None and f'down_{idx}' in control_hidden_states:
sample += rearrange(control_hidden_states[f'down_{idx}'], "b c f h w -> (b f) c h w")
if hasattr(self, 'reference_modules_down') and not reference_hidden_states is None and f'down_{idx}' in reference_hidden_states:
sample = self.reference_modules_down[idx](sample, reference_hidden_states[f'down_{idx}'], num_frames)
down_block_res_samples += res_samples
if is_controlnet:
new_down_block_res_samples = ()
for down_block_res_sample, down_block_additional_residual in zip(
down_block_res_samples, down_block_additional_residuals
):
down_block_res_sample = down_block_res_sample + down_block_additional_residual
new_down_block_res_samples = new_down_block_res_samples + (down_block_res_sample,)
down_block_res_samples = new_down_block_res_samples
# 4. mid
if self.mid_block is not None:
if hasattr(self.mid_block, "has_cross_attention") and self.mid_block.has_cross_attention:
sample = self.mid_block(
sample,
emb,
encoder_hidden_states=encoder_hidden_states,
attention_mask=attention_mask,
cross_attention_kwargs=cross_attention_kwargs,
encoder_attention_mask=encoder_attention_mask,
)
else:
sample = self.mid_block(sample, emb)
if hasattr(self, 'reference_modules_mid') and not reference_hidden_states is None and f'mid' in reference_hidden_states:
sample = self.reference_modules_mid(sample, reference_hidden_states[f'mid'], num_frames)
# To support T2I-Adapter-XL
if (
is_adapter
and len(down_intrablock_additional_residuals) > 0
and sample.shape == down_intrablock_additional_residuals[0].shape
):
sample += down_intrablock_additional_residuals.pop(0)
res_cache[f"mid"] = sample.clone()
if is_controlnet:
sample = sample + mid_block_additional_residual
# 5. up
for i, upsample_block in enumerate(self.up_blocks):
is_final_block = i == len(self.up_blocks) - 1
res_samples = down_block_res_samples[-len(upsample_block.resnets) :]
down_block_res_samples = down_block_res_samples[: -len(upsample_block.resnets)]
# if we have not reached the final block and need to forward the
# upsample size, we do it here
if not is_final_block and forward_upsample_size:
upsample_size = down_block_res_samples[-1].shape[2:]
if hasattr(upsample_block, "has_cross_attention") and upsample_block.has_cross_attention:
res_cache[f"up_{i}"] = sample.clone()
if hasattr(self, "reference_modules_up") and not reference_hidden_states is None and f'up_{i}' in reference_hidden_states:
sample = self.reference_modules_up[i-1](sample, reference_hidden_states[f'up_{i}'], num_frames)
sample = upsample_block(
hidden_states=sample,
temb=emb,
res_hidden_states_tuple=res_samples,
encoder_hidden_states=encoder_hidden_states,
cross_attention_kwargs=cross_attention_kwargs,
upsample_size=upsample_size,
attention_mask=attention_mask,
encoder_attention_mask=encoder_attention_mask,
)
else:
sample = upsample_block(
hidden_states=sample,
temb=emb,
res_hidden_states_tuple=res_samples,
upsample_size=upsample_size,
)
# 6. post-process
if self.conv_norm_out:
sample = self.conv_norm_out(sample)
sample = self.conv_act(sample)
sample = self.conv_out(sample)
if USE_PEFT_BACKEND:
# remove `lora_scale` from each PEFT layer
unscale_lora_layers(self, lora_scale)
# reshape to (batch, channel, framerate, width, height)
sample = rearrange(sample, "(b f) c h w -> b c f h w", f=num_frames)
if not return_dict:
return (sample, res_cache)
return (UNet2DConditionOutput(sample=sample), res_cache)
+241
View File
@@ -0,0 +1,241 @@
# coding: utf-8
"""
@File : models6/hm_denoising_motion.py
@Author : Songkey
@Email : songkey@pku.edu.cn
@Date : 9/9/2024
@Desc :
adapted from: https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/unets/unet_motion_model.py
"""
import torch
import torch.utils.checkpoint
from typing import Any, Dict, Optional, Tuple, Union
from einops import rearrange
from diffusers.utils import logging
from diffusers.models.unets.unet_motion_model import UNetMotionModel, UNetMotionOutput
from .hm_blocks import InsertReferenceAdapter
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
class HMDenoisingMotion(UNetMotionModel, InsertReferenceAdapter):
def forward(
self,
sample: torch.Tensor,
timestep: Union[torch.Tensor, float, int],
encoder_hidden_states: torch.Tensor,
reference_hidden_states: Optional[dict] = None,
control_hidden_states: Optional[torch.Tensor] = None,
timestep_cond: Optional[torch.Tensor] = None,
attention_mask: Optional[torch.Tensor] = None,
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
added_cond_kwargs: Optional[Dict[str, torch.Tensor]] = None,
down_block_additional_residuals: Optional[Tuple[torch.Tensor]] = None,
mid_block_additional_residual: Optional[torch.Tensor] = None,
return_dict: bool = True,
) -> Union[UNetMotionOutput, Tuple[torch.Tensor]]:
# By default samples have to be AT least a multiple of the overall upsampling factor.
# The overall upsampling factor is equal to 2 ** (# num of upsampling layears).
# However, the upsampling interpolation output size can be forced to fit any upsampling size
# on the fly if necessary.
default_overall_up_factor = 2 ** self.num_upsamplers
# upsample size should be forwarded when sample is not a multiple of `default_overall_up_factor`
forward_upsample_size = False
upsample_size = None
if any(s % default_overall_up_factor != 0 for s in sample.shape[-2:]):
logger.info("Forward upsample size to force interpolation output size.")
forward_upsample_size = True
# prepare attention_mask
if attention_mask is not None:
attention_mask = (1 - attention_mask.to(sample.dtype)) * -10000.0
attention_mask = attention_mask.unsqueeze(1)
# 1. time
timesteps = timestep
if not torch.is_tensor(timesteps):
# TODO: this requires sync between CPU and GPU. So try to pass timesteps as tensors if you can
# This would be a good case for the `match` statement (Python 3.10+)
is_mps = sample.device.type == "mps"
if isinstance(timestep, float):
dtype = torch.float32 if is_mps else torch.float64
else:
dtype = torch.int32 if is_mps else torch.int64
timesteps = torch.tensor([timesteps], dtype=dtype, device=sample.device)
elif len(timesteps.shape) == 0:
timesteps = timesteps[None].to(sample.device)
# broadcast to batch dimension in a way that's compatible with ONNX/Core ML
num_frames = sample.shape[2]
timesteps = timesteps.expand(sample.shape[0])
t_emb = self.time_proj(timesteps)
# timesteps does not contain any weights and will always return f32 tensors
# but time_embedding might actually be running in fp16. so we need to cast here.
# there might be better ways to encapsulate this.
t_emb = t_emb.to(dtype=self.dtype)
emb = self.time_embedding(t_emb, timestep_cond)
aug_emb = None
if self.config.addition_embed_type == "text_time":
if "text_embeds" not in added_cond_kwargs:
raise ValueError(
f"{self.__class__} has the config param `addition_embed_type` set to 'text_time' which requires the keyword argument `text_embeds` to be passed in `added_cond_kwargs`"
)
text_embeds = added_cond_kwargs.get("text_embeds")
if "time_ids" not in added_cond_kwargs:
raise ValueError(
f"{self.__class__} has the config param `addition_embed_type` set to 'text_time' which requires the keyword argument `time_ids` to be passed in `added_cond_kwargs`"
)
time_ids = added_cond_kwargs.get("time_ids")
time_embeds = self.add_time_proj(time_ids.flatten())
time_embeds = time_embeds.reshape((text_embeds.shape[0], -1))
add_embeds = torch.concat([text_embeds, time_embeds], dim=-1)
add_embeds = add_embeds.to(emb.dtype)
aug_emb = self.add_embedding(add_embeds)
emb = emb if aug_emb is None else emb + aug_emb
emb = emb.repeat_interleave(repeats=num_frames, dim=0)
if len(encoder_hidden_states.shape) == 3:
encoder_hidden_states = encoder_hidden_states.repeat_interleave(repeats=num_frames, dim=0)
elif len(encoder_hidden_states.shape) == 4:
encoder_hidden_states = rearrange(encoder_hidden_states, "b f l d -> (b f) l d")
if self.encoder_hid_proj is not None and self.config.encoder_hid_dim_type == "ip_image_proj":
if "image_embeds" not in added_cond_kwargs:
raise ValueError(
f"{self.__class__} has the config param `encoder_hid_dim_type` set to 'ip_image_proj' which requires the keyword argument `image_embeds` to be passed in `added_conditions`"
)
image_embeds = added_cond_kwargs.get("image_embeds")
image_embeds = self.encoder_hid_proj(image_embeds)
image_embeds = [image_embed.repeat_interleave(repeats=num_frames, dim=0) for image_embed in image_embeds]
encoder_hidden_states = (encoder_hidden_states, image_embeds)
# 2. pre-process
sample = sample.permute(0, 2, 1, 3, 4).reshape((sample.shape[0] * num_frames, -1) + sample.shape[3:])
sample = self.conv_in(sample)
# 3. down
down_block_res_samples = (sample,)
for idx, downsample_block in enumerate(self.down_blocks):
if hasattr(downsample_block, "has_cross_attention") and downsample_block.has_cross_attention:
sample, res_samples = downsample_block(
hidden_states=sample,
temb=emb,
encoder_hidden_states=encoder_hidden_states,
attention_mask=attention_mask,
num_frames=num_frames,
cross_attention_kwargs=cross_attention_kwargs,
)
else:
sample, res_samples = downsample_block(hidden_states=sample, temb=emb, num_frames=num_frames)
if not control_hidden_states is None and f'down_{idx}' in control_hidden_states:
sample += rearrange(control_hidden_states[f'down_{idx}'], "b c f h w -> (b f) c h w")
if hasattr(self, 'reference_modules_down') and not reference_hidden_states is None and f'down_{idx}' in reference_hidden_states:
sample = self.reference_modules_down[idx](sample, reference_hidden_states[f'down_{idx}'], num_frames)
down_block_res_samples += res_samples
if down_block_additional_residuals is not None:
new_down_block_res_samples = ()
for down_block_res_sample, down_block_additional_residual in zip(
down_block_res_samples, down_block_additional_residuals
):
down_block_res_sample = down_block_res_sample + down_block_additional_residual
new_down_block_res_samples += (down_block_res_sample,)
down_block_res_samples = new_down_block_res_samples
# 4. mid
if self.mid_block is not None:
# To support older versions of motion modules that don't have a mid_block
if hasattr(self.mid_block, "motion_modules"):
sample = self.mid_block(
sample,
emb,
encoder_hidden_states=encoder_hidden_states,
attention_mask=attention_mask,
num_frames=num_frames,
cross_attention_kwargs=cross_attention_kwargs,
)
else:
sample = self.mid_block(
sample,
emb,
encoder_hidden_states=encoder_hidden_states,
attention_mask=attention_mask,
cross_attention_kwargs=cross_attention_kwargs,
)
if hasattr(self, 'reference_modules_mid') and not reference_hidden_states is None and f'mid' in reference_hidden_states:
sample = self.reference_modules_mid(sample, reference_hidden_states[f'mid'], num_frames)
if mid_block_additional_residual is not None:
sample = sample + mid_block_additional_residual
# 5. up
for i, upsample_block in enumerate(self.up_blocks):
is_final_block = i == len(self.up_blocks) - 1
res_samples = down_block_res_samples[-len(upsample_block.resnets):]
down_block_res_samples = down_block_res_samples[: -len(upsample_block.resnets)]
# if we have not reached the final block and need to forward the
# upsample size, we do it here
if not is_final_block and forward_upsample_size:
upsample_size = down_block_res_samples[-1].shape[2:]
if hasattr(upsample_block, "has_cross_attention") and upsample_block.has_cross_attention:
if hasattr(upsample_block, "has_cross_attention") and upsample_block.has_cross_attention:
if hasattr(self,
"reference_modules_up") and not reference_hidden_states is None and f'up_{i}' in reference_hidden_states:
sample = self.reference_modules_up[i - 1](sample, reference_hidden_states[f'up_{i}'],
num_frames)
sample = upsample_block(
hidden_states=sample,
temb=emb,
res_hidden_states_tuple=res_samples,
encoder_hidden_states=encoder_hidden_states,
upsample_size=upsample_size,
attention_mask=attention_mask,
num_frames=num_frames,
cross_attention_kwargs=cross_attention_kwargs,
)
else:
sample = upsample_block(
hidden_states=sample,
temb=emb,
res_hidden_states_tuple=res_samples,
upsample_size=upsample_size,
num_frames=num_frames,
)
# 6. post-process
if self.conv_norm_out:
sample = self.conv_norm_out(sample)
sample = self.conv_act(sample)
sample = self.conv_out(sample)
# reshape to (batch, channel, framerate, width, height)
sample = sample[None, :].reshape((-1, num_frames) + sample.shape[1:]).permute(0, 2, 1, 3, 4)
if not return_dict:
return (sample,)
return UNetMotionOutput(sample=sample)
+12
View File
@@ -0,0 +1,12 @@
# coding: utf-8
"""
@File : __init__.py.py
@Author : Songkey
@Email : songkey@pku.edu.cn
@Date : 8/29/2024
@Desc :
"""
from .pipline_hm_video import HMVideoPipeline
from .pipline_hm_image import HMImagePipeline
+289
View File
@@ -0,0 +1,289 @@
# coding: utf-8
"""
@File : hm_pipline_image.py
@Author : Songkey
@Email : songkey@pku.edu.cn
@Date : 8/29/2024
@Desc :
adapted from: https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion_img2img.py
"""
from typing import Any, Callable, Dict, List, Optional, Union
import torch
from diffusers.callbacks import MultiPipelineCallbacks, PipelineCallback
from diffusers.image_processor import PipelineImageInput
from diffusers.utils import (
deprecate,
)
from diffusers.pipelines.stable_diffusion.pipeline_output import StableDiffusionPipelineOutput
from diffusers import StableDiffusionImg2ImgPipeline
from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img import retrieve_timesteps, retrieve_latents
from ..models import HMDenoising3D, HMControlNet
from ..models import HMReferenceAdapter
class HMImagePipeline(StableDiffusionImg2ImgPipeline):
def caryomitosis(self,):
if not hasattr(self, "unet_ref"):
self.unet_ref = HMDenoising3D.from_unet2d(self.unet)
if not isinstance(self.unet, HMDenoising3D):
unet = HMDenoising3D.from_unet2d(unet=self.unet)
# todo: 不够优雅
del self.unet
self.unet = unet
def insert_hm_modules(self, dtype, device):
if isinstance(self.unet, HMDenoising3D):
hm_adapter = HMReferenceAdapter.from_pretrained('songkey/hm_reference')
self.unet.insert_reference_adapter(hm_adapter)
self.unet_ref = self.unet_ref.to(device=device, dtype=dtype).eval()
self.unet = self.unet.to(device=device, dtype=dtype).eval()
if not hasattr(self, "mp_control"):
self.mp_control = HMControlNet.from_pretrained('songkey/hm_control')
self.mp_control = self.mp_control.to(device=device, dtype=dtype).eval()
@torch.no_grad()
def __call__(
self,
prompt: Union[str, List[str]] = None,
image: PipelineImageInput = None,
drive_params: Dict[str, Any] = None,
strength: float = 0.8,
num_inference_steps: Optional[int] = 50,
timesteps: List[int] = None,
sigmas: List[float] = None,
guidance_scale: Optional[float] = 7.5,
negative_prompt: Optional[Union[str, List[str]]] = None,
eta: Optional[float] = 0.0,
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
prompt_embeds: Optional[torch.Tensor] = None,
negative_prompt_embeds: Optional[torch.Tensor] = None,
ip_adapter_image: Optional[PipelineImageInput] = None,
ip_adapter_image_embeds: Optional[List[torch.Tensor]] = None,
output_type: Optional[str] = "pil",
return_dict: bool = True,
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
clip_skip: int = None,
callback_on_step_end: Optional[
Union[Callable[[int, int, Dict], None], PipelineCallback, MultiPipelineCallbacks]
] = None,
callback_on_step_end_tensor_inputs: List[str] = ["latents"],
**kwargs,
):
callback = kwargs.pop("callback", None)
callback_steps = kwargs.pop("callback_steps", None)
num_images_per_prompt = 1
if callback is not None:
deprecate(
"callback",
"1.0.0",
"Passing `callback` as an input argument to `__call__` is deprecated, consider use `callback_on_step_end`",
)
if callback_steps is not None:
deprecate(
"callback_steps",
"1.0.0",
"Passing `callback_steps` as an input argument to `__call__` is deprecated, consider use `callback_on_step_end`",
)
if isinstance(callback_on_step_end, (PipelineCallback, MultiPipelineCallbacks)):
callback_on_step_end_tensor_inputs = callback_on_step_end.tensor_inputs
# 1. Check inputs. Raise error if not correct
self.check_inputs(
prompt,
strength,
callback_steps,
negative_prompt,
prompt_embeds,
negative_prompt_embeds,
ip_adapter_image,
ip_adapter_image_embeds,
callback_on_step_end_tensor_inputs,
)
self._guidance_scale = guidance_scale
self._clip_skip = clip_skip
self._cross_attention_kwargs = cross_attention_kwargs
self._interrupt = False
# 2. Define call parameters
if prompt is not None and isinstance(prompt, str):
batch_size = 1
elif prompt is not None and isinstance(prompt, list):
batch_size = len(prompt)
else:
batch_size = prompt_embeds.shape[0]
device = self.device
# 3. Encode input prompt
text_encoder_lora_scale = (
self.cross_attention_kwargs.get("scale", None) if self.cross_attention_kwargs is not None else None
)
print('device:', device)
prompt_embeds, negative_prompt_embeds = self.encode_prompt(
prompt,
device,
num_images_per_prompt,
self.do_classifier_free_guidance,
negative_prompt,
prompt_embeds=prompt_embeds,
negative_prompt_embeds=negative_prompt_embeds,
lora_scale=text_encoder_lora_scale,
clip_skip=self.clip_skip,
)
# For classifier free guidance, we need to do two forward passes.
# Here we concatenate the unconditional and text embeddings into a single batch
# to avoid doing two forward passes
if self.do_classifier_free_guidance:
prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds])
if ip_adapter_image is not None or ip_adapter_image_embeds is not None:
image_embeds = self.prepare_ip_adapter_image_embeds(
ip_adapter_image,
ip_adapter_image_embeds,
device,
batch_size * num_images_per_prompt,
self.do_classifier_free_guidance,
)
# 4. Preprocess
image = self.image_processor.preprocess(image).to(device=device, dtype=prompt_embeds.dtype)
# 5. set timesteps
timesteps, num_inference_steps = retrieve_timesteps(
self.scheduler, num_inference_steps, device, timesteps, sigmas
)
timesteps, num_inference_steps = self.get_timesteps(num_inference_steps, strength, device)
latent_timestep = timesteps[:1].repeat(batch_size * num_images_per_prompt)
ref_latents = [
retrieve_latents(self.vae.encode(image[i: i + 1].to(device=device)), generator=generator)
for i in range(batch_size)
]
ref_latents = torch.cat(ref_latents, dim=0)
ref_latents = self.vae.config.scaling_factor * ref_latents
# 6. Prepare latent variables
# latents = self.prepare_latents(
# ref_latents,
# latent_timestep,
# batch_size,
# num_images_per_prompt,
# prompt_embeds.dtype,
# device,
# generator,
# )
# condition, drive_coeff, face_parts
if self.do_classifier_free_guidance:
drive_params['condition'] = torch.cat([torch.ones_like(drive_params['condition']) * -1, drive_params['condition']], dim=0)
drive_params['drive_coeff'] = torch.cat([torch.zeros_like(drive_params['drive_coeff']), drive_params['drive_coeff']], dim=0)
drive_params['face_parts'] = torch.cat([torch.zeros_like(drive_params['face_parts']), drive_params['face_parts']], dim=0)
if hasattr(self, "mp_control"):
control_latents = self.mp_control(**drive_params)
else:
control_latents = None
# 7. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline
extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
# 7.1 Add image embeds for IP-Adapter
added_cond_kwargs = (
{"image_embeds": image_embeds}
if ip_adapter_image is not None or ip_adapter_image_embeds is not None
else None
)
latent_model_input = torch.cat([torch.zeros_like(ref_latents), ref_latents]) if self.do_classifier_free_guidance else ref_latents
cached_res = self.unet_ref(
latent_model_input.unsqueeze(2),
0,
encoder_hidden_states=prompt_embeds,
# added_cond_kwargs=added_cond_kwargs,
return_dict=False,
)[1]
# 7.2 Optionally get Guidance Scale Embedding
timestep_cond = None
if self.unet.config.time_cond_proj_dim is not None:
guidance_scale_tensor = torch.tensor(self.guidance_scale - 1).repeat(batch_size * num_images_per_prompt)
timestep_cond = self.get_guidance_scale_embedding(
guidance_scale_tensor, embedding_dim=self.unet.config.time_cond_proj_dim
).to(device=device, dtype=prompt_embeds.dtype)
latent_timestep = timesteps[:1].repeat(batch_size * num_images_per_prompt)
base_noise = torch.randn_like(ref_latents, device=device, dtype=prompt_embeds.dtype)
latents = self.scheduler.add_noise(ref_latents, base_noise, latent_timestep)
# 8. Denoising loop
num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
self._num_timesteps = len(timesteps)
with self.progress_bar(total=num_inference_steps) as progress_bar:
for i, t in enumerate(timesteps):
if self.interrupt:
continue
# expand the latents if we are doing classifier free guidance
latent_model_input = torch.cat([latents] * 2) if self.do_classifier_free_guidance else latents
latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)
# predict the noise residual
noise_pred = self.unet(
latent_model_input.unsqueeze(2),
t,
encoder_hidden_states=prompt_embeds,
reference_hidden_states=cached_res,
control_hidden_states=control_latents,
timestep_cond=timestep_cond,
cross_attention_kwargs=self.cross_attention_kwargs,
added_cond_kwargs=added_cond_kwargs,
return_dict=False,
)[0][:,:,0,:,:]
# perform guidance
if self.do_classifier_free_guidance:
noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
noise_pred = noise_pred_uncond + self.guidance_scale * (noise_pred_text - noise_pred_uncond)
# compute the previous noisy sample x_t -> x_t-1
latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs, return_dict=False)[0]
if callback_on_step_end is not None:
callback_kwargs = {}
for k in callback_on_step_end_tensor_inputs:
callback_kwargs[k] = locals()[k]
callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
latents = callback_outputs.pop("latents", latents)
prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
negative_prompt_embeds = callback_outputs.pop("negative_prompt_embeds", negative_prompt_embeds)
# call the callback, if provided
if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
progress_bar.update()
if callback is not None and i % callback_steps == 0:
step_idx = i // getattr(self.scheduler, "order", 1)
callback(step_idx, t, latents)
if not output_type == "latent":
image = self.vae.decode(latents / self.vae.config.scaling_factor, return_dict=False, generator=generator)[
0
]
else:
image = latents
do_denormalize = [True] * image.shape[0]
image = self.image_processor.postprocess(image, output_type=output_type, do_denormalize=do_denormalize)
# Offload all models
self.maybe_free_model_hooks()
return StableDiffusionPipelineOutput(images=image, nsfw_content_detected=None)
+376
View File
@@ -0,0 +1,376 @@
# coding: utf-8
"""
@File : hm_pipline_image.py
@Author : Songkey
@Email : songkey@pku.edu.cn
@Date : 8/29/2024
@Desc :
adapted from: https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion_img2img.py
"""
from typing import Any, Callable, Dict, List, Optional, Union
import torch
from diffusers.callbacks import MultiPipelineCallbacks, PipelineCallback
from diffusers.image_processor import PipelineImageInput
from diffusers.utils import deprecate
from diffusers.utils.torch_utils import randn_tensor
from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img import retrieve_timesteps, retrieve_latents
from diffusers import StableDiffusionImg2ImgPipeline
from diffusers import EulerDiscreteScheduler
from diffusers import MotionAdapter
from ..models import HMDenoising3D, HMDenoisingMotion, HMControlNet
from ..models import HMReferenceAdapter
class HMVideoPipeline(StableDiffusionImg2ImgPipeline):
def caryomitosis(self,):
if not hasattr(self, "unet_ref"):
self.unet_ref = HMDenoising3D.from_unet2d(self.unet)
if not isinstance(self.unet, HMDenoisingMotion):
adapter = MotionAdapter.from_pretrained("songkey/hm_animatediff", torch_dtype=torch.float16)
unet = HMDenoisingMotion.from_unet2d(unet=self.unet, motion_adapter=adapter, load_weights=True)
# todo: 不够优雅
del self.unet
self.unet = unet
def insert_hm_modules(self, dtype, device):
if isinstance(self.unet, HMDenoisingMotion):
hm_adapter = HMReferenceAdapter.from_pretrained('songkey/hm_reference')
self.unet.insert_reference_adapter(hm_adapter)
self.unet_ref.insert_reference_adapter(hm_adapter)
self.unet_ref = self.unet_ref.to(device=device, dtype=dtype).eval()
self.unet = self.unet.to(device=device, dtype=dtype).eval()
if not hasattr(self, "mp_control"):
self.mp_control = HMControlNet.from_pretrained('songkey/hm_control')
self.mp_control = self.mp_control.to(device=device, dtype=dtype).eval()
@torch.no_grad()
def __call__(
self,
prompt: Union[str, List[str]] = None,
image: PipelineImageInput = None,
num_frames: int = 16,
chunk_overlap: int = 4,
drive_params: Dict[str, Any] = None,
strength: float = 0.8,
num_inference_steps: Optional[int] = 50,
timesteps: List[int] = None,
sigmas: List[float] = None,
guidance_scale: Optional[float] = 7.5,
negative_prompt: Optional[Union[str, List[str]]] = None,
eta: Optional[float] = 0.0,
generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
prompt_embeds: Optional[torch.Tensor] = None,
negative_prompt_embeds: Optional[torch.Tensor] = None,
ip_adapter_image: Optional[PipelineImageInput] = None,
ip_adapter_image_embeds: Optional[List[torch.Tensor]] = None,
output_type: Optional[str] = "pil",
return_dict: bool = True,
cross_attention_kwargs: Optional[Dict[str, Any]] = None,
clip_skip: int = None,
callback_on_step_end: Optional[
Union[Callable[[int, int, Dict], None], PipelineCallback, MultiPipelineCallbacks]
] = None,
callback_on_step_end_tensor_inputs: List[str] = ["latents"],
**kwargs,
):
callback = kwargs.pop("callback", None)
callback_steps = kwargs.pop("callback_steps", None)
num_images_per_prompt = 1
if callback is not None:
deprecate(
"callback",
"1.0.0",
"Passing `callback` as an input argument to `__call__` is deprecated, consider use `callback_on_step_end`",
)
if callback_steps is not None:
deprecate(
"callback_steps",
"1.0.0",
"Passing `callback_steps` as an input argument to `__call__` is deprecated, consider use `callback_on_step_end`",
)
if isinstance(callback_on_step_end, (PipelineCallback, MultiPipelineCallbacks)):
callback_on_step_end_tensor_inputs = callback_on_step_end.tensor_inputs
# 1. Check inputs. Raise error if not correct
self.check_inputs(
prompt,
strength,
callback_steps,
negative_prompt,
prompt_embeds,
negative_prompt_embeds,
ip_adapter_image,
ip_adapter_image_embeds,
callback_on_step_end_tensor_inputs,
)
self._guidance_scale = guidance_scale
self._clip_skip = clip_skip
self._cross_attention_kwargs = cross_attention_kwargs
self._interrupt = False
# 2. Define call parameters
if prompt is not None and isinstance(prompt, str):
batch_size = 1
elif prompt is not None and isinstance(prompt, list):
batch_size = len(prompt)
else:
batch_size = prompt_embeds.shape[0]
device = self._execution_device
# 3. Encode input prompt
text_encoder_lora_scale = (
self.cross_attention_kwargs.get("scale", None) if self.cross_attention_kwargs is not None else None
)
prompt_embeds, negative_prompt_embeds = self.encode_prompt(
prompt,
device,
num_images_per_prompt,
self.do_classifier_free_guidance,
negative_prompt,
prompt_embeds=prompt_embeds,
negative_prompt_embeds=negative_prompt_embeds,
lora_scale=text_encoder_lora_scale,
clip_skip=self.clip_skip,
)
# For classifier free guidance, we need to do two forward passes.
# Here we concatenate the unconditional and text embeddings into a single batch
# to avoid doing two forward passes
if self.do_classifier_free_guidance:
prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds])
if ip_adapter_image is not None or ip_adapter_image_embeds is not None:
image_embeds = self.prepare_ip_adapter_image_embeds(
ip_adapter_image,
ip_adapter_image_embeds,
device,
batch_size * num_images_per_prompt,
self.do_classifier_free_guidance,
)
# 4. Preprocess image
image = self.image_processor.preprocess(image).to(device=device, dtype=prompt_embeds.dtype)
# 5. set timesteps
timesteps, num_inference_steps = retrieve_timesteps(
self.scheduler, num_inference_steps, device, timesteps, sigmas
)
timesteps, num_inference_steps = self.get_timesteps(num_inference_steps, strength, device)
latent_timestep = timesteps[:1].repeat(batch_size * num_images_per_prompt)
raw_video_len = drive_params['drive_coeff'].size(1)
chunk_size = num_frames
chunk_overlap = min(max(0, chunk_overlap), chunk_size // 2)
chunk_stride = chunk_size - chunk_overlap
drive_idx_list = list(range(raw_video_len))
steped_video_len = len(drive_idx_list)
post_pad_num = chunk_stride - (steped_video_len - chunk_overlap) % chunk_stride
if post_pad_num > 0:
drive_idx_list = drive_idx_list + [drive_idx_list[-1]] * post_pad_num
paded_video_len = len(drive_idx_list)
drive_idx_chunks = [drive_idx_list[i:i + chunk_size] for i in
range(0, paded_video_len - chunk_stride + 1, chunk_stride)]
# 6. Prepare latent variables
ref_latents = [
retrieve_latents(self.vae.encode(image[i: i + 1].to(device=device)), generator=generator)
for i in range(batch_size)
]
ref_latents = torch.cat(ref_latents, dim=0)
ref_latents = self.vae.config.scaling_factor * ref_latents
c, h, w = ref_latents.shape[1:]
# 7. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline
extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta)
# 7.1 Add image embeds for IP-Adapter
added_cond_kwargs = (
{"image_embeds": image_embeds}
if ip_adapter_image is not None or ip_adapter_image_embeds is not None
else None
)
# if hasattr(self, "unet_ref"):
ref_latents = ref_latents.unsqueeze(2)
cached_res = self.unet_ref(
torch.cat([torch.zeros_like(ref_latents), ref_latents], dim=0) if
self.do_classifier_free_guidance else ref_latents,
0,
encoder_hidden_states=prompt_embeds,
# added_cond_kwargs=added_cond_kwargs,
return_dict=False,
)[1]
# else:
# cached_res = None
# 7.2 Optionally get Guidance Scale Embedding
timestep_cond = None
if self.unet.config.time_cond_proj_dim is not None:
guidance_scale_tensor = torch.tensor(self.guidance_scale - 1).repeat(batch_size * num_images_per_prompt)
timestep_cond = self.get_guidance_scale_embedding(
guidance_scale_tensor, embedding_dim=self.unet.config.time_cond_proj_dim
).to(device=device, dtype=prompt_embeds.dtype)
if self.do_classifier_free_guidance:
drive_params['condition'] = torch.cat(
[torch.ones_like(drive_params['condition']) * -1, drive_params['condition']], dim=0)
drive_params['drive_coeff'] = torch.cat(
[torch.zeros_like(drive_params['drive_coeff']), drive_params['drive_coeff']], dim=0)
drive_params['face_parts'] = torch.cat(
[torch.zeros_like(drive_params['face_parts']), drive_params['face_parts']], dim=0)
control_latents_chunks = []
for cidx, chunk in enumerate(drive_idx_chunks):
condition_chunk = drive_params['condition'][:, :, chunk, :, :]
drive_coeff_chunk = drive_params['drive_coeff'][:, chunk, :]
face_parts_chunk = drive_params['face_parts'][:, chunk, :, :]
control_latents_chunk = self.mp_control(condition=condition_chunk.to(device=device),
drive_coeff=drive_coeff_chunk.to(device=device),
face_parts=face_parts_chunk.to(device=device))
control_latents_chunks.append(control_latents_chunk)
drive_idx_chunks[-1] = list(range(drive_idx_chunks[-1][0], paded_video_len))
with self.progress_bar(total=len(drive_idx_chunks)) as progress_bar:
chunk_latents = torch.zeros([batch_size, c, paded_video_len, h, w], dtype=prompt_embeds.dtype).cpu()
chunk_latents_counter = torch.zeros([1, 1, paded_video_len, 1, 1], dtype=prompt_embeds.dtype).cpu()
chunk_noise = randn_tensor([batch_size, c, 1, h, w], dtype=prompt_embeds.dtype, generator=generator).to(
device=device)
chunk_noise = chunk_noise.repeat_interleave(chunk_size, dim=2)
for cidx, chunk in enumerate(drive_idx_chunks):
control_latents_chunk = control_latents_chunks[cidx]
scheduler = EulerDiscreteScheduler(
num_train_timesteps=1000,
beta_start=0.00085,
beta_end=0.012,
beta_schedule="scaled_linear",
)
tmp_timesteps, _ = retrieve_timesteps(
scheduler, 8, device, None, sigmas
)
# chunk_latent = chunk_noise * self.scheduler.init_noise_sigma
chunk_latent = scheduler.add_noise(ref_latents, chunk_noise, tmp_timesteps[:1])
for i, t in enumerate(tmp_timesteps):
latent_model_input = torch.cat([chunk_latent, chunk_latent], dim=0) if \
self.do_classifier_free_guidance else chunk_latent
latent_model_input = scheduler.scale_model_input(latent_model_input, t)
noise_pred_chunk = self.unet_ref(
latent_model_input,
t,
encoder_hidden_states=prompt_embeds,
reference_hidden_states=cached_res,
control_hidden_states=control_latents_chunk,
timestep_cond=timestep_cond,
cross_attention_kwargs=self.cross_attention_kwargs,
added_cond_kwargs=added_cond_kwargs,
return_dict=False,
)[0]
if self.do_classifier_free_guidance:
noise_pred_uncond, noise_pred_text = noise_pred_chunk.chunk(2)
noise_pred_chunk = noise_pred_uncond + self.guidance_scale * (noise_pred_text - noise_pred_uncond)
chunk_latent = scheduler.step(noise_pred_chunk, t, chunk_latent,
**extra_step_kwargs, return_dict=False)[0]
chunk_latents[:, :, chunk] = chunk_latents[:, :, chunk] + chunk_latent.cpu()
chunk_latents_counter[:, :, chunk] += 1
progress_bar.update()
chunk_latents = chunk_latents / chunk_latents_counter
# 5. set timesteps
timesteps, num_inference_steps = retrieve_timesteps(
self.scheduler, num_inference_steps, device, None, sigmas
)
timesteps, num_inference_steps = self.get_timesteps(num_inference_steps, strength, device)
latent_timestep = timesteps[:1].repeat(batch_size * num_images_per_prompt)
# 8. Denoising loop
num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
self._num_timesteps = len(timesteps)
base_noise = randn_tensor([batch_size, c, paded_video_len, h, w], dtype=prompt_embeds.dtype, generator=generator)
# ref_latents = ref_latents.repeat_interleave(paded_video_len, dim=2)
latents = self.scheduler.add_noise(chunk_latents.to(device=device), base_noise.to(device=device), latent_timestep).cpu()
with self.progress_bar(total=num_inference_steps) as progress_bar:
for i, t in enumerate(timesteps):
noise_pred = torch.zeros([batch_size * 2 if self.do_classifier_free_guidance
else batch_size, c, paded_video_len, h, w], dtype=prompt_embeds.dtype).cpu()
noise_pred_counter = torch.zeros([1, 1, paded_video_len, 1, 1], dtype=prompt_embeds.dtype).cpu()
latent_model_input_all = torch.cat([latents.clone(), latents.clone()], dim=0) if \
self.do_classifier_free_guidance else latents.clone()
latent_model_input_all = self.scheduler.scale_model_input(latent_model_input_all, t)
for cidx, chunk in enumerate(drive_idx_chunks):
if self.interrupt:
continue
control_latents = control_latents_chunks[cidx]
latent_model_input = latent_model_input_all[:, :, chunk].to(device=device)
# predict the noise residual
noise_pred_chunk = self.unet(
latent_model_input,
t,
encoder_hidden_states=prompt_embeds,
reference_hidden_states=cached_res,
control_hidden_states=control_latents,
timestep_cond=timestep_cond,
cross_attention_kwargs=self.cross_attention_kwargs,
added_cond_kwargs=added_cond_kwargs,
return_dict=False,
)[0].cpu()
noise_pred[:, :, chunk] = noise_pred[:, :, chunk] + noise_pred_chunk
noise_pred_counter[:, :, chunk] = noise_pred_counter[:, :, chunk] + 1
noise_pred = noise_pred / noise_pred_counter
# perform guidance
if self.do_classifier_free_guidance:
noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
noise_pred = noise_pred_uncond + self.guidance_scale * (noise_pred_text - noise_pred_uncond)
# compute the previous noisy sample x_t -> x_t-1
latents = self.scheduler.step(noise_pred.to(device=device), t, latents.to(device=device),
**extra_step_kwargs, return_dict=False)[0].cpu()
if callback_on_step_end is not None:
callback_kwargs = {}
for k in callback_on_step_end_tensor_inputs:
callback_kwargs[k] = locals()[k]
callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
latents = callback_outputs.pop("latents", latents)
prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
negative_prompt_embeds = callback_outputs.pop("negative_prompt_embeds", negative_prompt_embeds)
# call the callback, if provided
if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
progress_bar.update()
if callback is not None and i % callback_steps == 0:
step_idx = i // getattr(self.scheduler, "order", 1)
callback(step_idx, t, latents)
latents = latents / self.vae.config.scaling_factor
res_frames = [self.vae.decode(latents[:,:,i,:,:].to(device=device), return_dict=False, generator=generator)[0]
for i in range(raw_video_len)]
res_frames = [self.image_processor.postprocess(frame, output_type=output_type) for frame in res_frames]
return res_frames
+13
View File
@@ -0,0 +1,13 @@
# coding: utf-8
# @File : __init__.py
# @Author : Songkey
# @Email : songkey@pku.edu.cn
# @Date : 8/28/2024
# @Desc :
from .hello_arkit import HelloARKitBSPred
from .hello_face_det import HelloFaceDet
from .hello_camera_demo import HelloCameraDemo
from .hello_3dmm import Hello3DMMPred
from .hello_face_alignment import HelloFaceAlignment
+140
View File
@@ -0,0 +1,140 @@
# coding: utf-8
"""
@File : test.py
@Author : Songkey
@Email : songkey@pku.edu.cn
@Date : 11/1/2024
@Desc : Created by Shengjie Wu (wu.shengjie@immomo.com)
这可能是一个很强大的模型
"""
import numpy as np
import cv2
from huggingface_hub import hf_hub_download
from .utils import get_warp_mat_bbox_by_gt_pts_float, create_onnx_session
def crop_transl_to_full_transl(crop_trans, crop_center, scale, full_center, focal_length):
"""
:param crop_trans: (3), float
:param crop_center: (2), float
:param scale: (1), float
:param full_center: (2), float
:param focal_length: (1), float
:return:
"""
crop_c_x, crop_c_y = crop_center
full_c_x, full_c_y = full_center
bs = 2 * focal_length / scale / crop_trans[2]
full_x = crop_trans[0] - 2 * (crop_c_x - full_c_x) / bs
full_y = crop_trans[1] + 2 * (crop_c_y - full_c_y) / bs
full_z = crop_trans[2] * scale
full_trans = np.array([full_x, full_y, full_z], dtype=np.float32)
return full_trans
class Hello3DMMPred(object):
def __init__(self, gpu_id=None):
self.deep3d_pred_net = create_onnx_session(hf_hub_download('songkey/hello_group_facemodel', filename='hello_3dmm.onnx'), gpu_id=gpu_id)
self.deep3d_pred_net_input_name = self.deep3d_pred_net.get_inputs()[0].name
self.deep3d_pred_net_output_name = [output.name for output in self.deep3d_pred_net.get_outputs()]
self.image_size = 224
self.camera_init_z = -0.4
self.camera_init_focal_len = 386.2879122887948
self.used_focal_len = -5.0 / self.camera_init_z * self.camera_init_focal_len
self.id_dims = 526
self.exp_dims = 203
self.tex_dims = 439
def forward_params(self, src_image, src_pt):
align_mat_info = get_warp_mat_bbox_by_gt_pts_float(src_pt, base_angle=0, dst_size=self.image_size, expand_ratio=0.35, return_info=True)
align_mat = align_mat_info["M"]
align_image_rgb_uint8 = cv2.cvtColor(cv2.warpAffine(src_image, align_mat, (self.image_size, self.image_size)), cv2.COLOR_BGR2RGB)
# cv2.imshow('align_image_rgb_uint8', align_image_rgb_uint8)
align_image_rgb_fp32 = align_image_rgb_uint8.astype(np.float32) / 255.0
align_image_rgb_fp32_onnx_input = align_image_rgb_fp32.copy().transpose((2, 0, 1))[np.newaxis, ...]
pred_coeffs = self.deep3d_pred_net.run(self.deep3d_pred_net_output_name,
{self.deep3d_pred_net_input_name: align_image_rgb_fp32_onnx_input})[0]
angles = pred_coeffs[:, self.id_dims + self.exp_dims + self.tex_dims:self.id_dims + self.exp_dims + self.tex_dims + 3]
translations = pred_coeffs[:, self.id_dims + self.exp_dims + self.tex_dims + 3 + 27:]
crop_global_transl = crop_transl_to_full_transl(translations[0],
crop_center=[align_mat_info["center_x"],
align_mat_info["center_y"]],
scale=align_mat_info["scale"],
full_center=[src_image.shape[1] * 0.5, src_image.shape[0] * 0.5],
focal_length=self.used_focal_len)
return angles, crop_global_transl[np.newaxis, :]
def compute_rotation_matrix(self, angles):
n_b = angles.shape[0]
sinx = np.sin(angles[:, 0])
siny = np.sin(angles[:, 1])
sinz = np.sin(angles[:, 2])
cosx = np.cos(angles[:, 0])
cosy = np.cos(angles[:, 1])
cosz = np.cos(angles[:, 2])
rotXYZ = np.eye(3).reshape(1, 3, 3).repeat(n_b*3, 0).reshape(3, n_b, 3, 3)
rotXYZ[0, :, 1, 1] = cosx
rotXYZ[0, :, 1, 2] = -sinx
rotXYZ[0, :, 2, 1] = sinx
rotXYZ[0, :, 2, 2] = cosx
rotXYZ[1, :, 0, 0] = cosy
rotXYZ[1, :, 0, 2] = siny
rotXYZ[1, :, 2, 0] = -siny
rotXYZ[1, :, 2, 2] = cosy
rotXYZ[2, :, 0, 0] = cosz
rotXYZ[2, :, 0, 1] = -sinz
rotXYZ[2, :, 1, 0] = sinz
rotXYZ[2, :, 1, 1] = cosz
rotation = np.matmul(np.matmul(rotXYZ[2], rotXYZ[1]), rotXYZ[0])
return rotation.transpose(0, 2, 1)
def rigid_transform(self, vs, rot, trans):
vs_r = np.matmul(vs, rot)
vs_t = vs_r + trans.reshape(-1, 1, 3)
return vs_t
def perspective_projection_points(self, points, image_w, image_h, used_focal_len):
batch_size = points.shape[0]
K = np.zeros([batch_size, 3, 3])
K[:, 0, 0] = used_focal_len
K[:, 1, 1] = used_focal_len
K[:, 2, 2] = 1.
K[:, 0, 2] = image_w * 0.5
K[:, 1, 2] = image_h * 0.5
reverse_z = np.array([[1, 0, 0], [0, 1, 0], [0, 0, -1]])[np.newaxis, :, :].repeat(batch_size, 0)
# Transform points
aug_projection = np.matmul(points, reverse_z)
aug_projection = np.matmul(aug_projection, K.transpose((0, 2, 1)))
# Apply perspective distortion
projected_points = aug_projection[:, :, :2] / aug_projection[:, :, 2:]
return projected_points
def get_project_points_rect(self, angle, trans, image_w, image_h):
vs = np.array(
[[-1, -1, 0], [-1, 1, 0], [1, 1, 0], [1, -1, 0]],
) * 0.05
vs = vs[np.newaxis, :, :]
rotation = self.compute_rotation_matrix(angle)
translation = trans.copy()
translation[0, 2] *= 0.05
vs_t = self.rigid_transform(vs, rotation, translation)
project_points = self.perspective_projection_points(vs_t, image_w, image_h, self.used_focal_len*0.05)
project_points = np.stack([project_points[:, :, 0], image_h - project_points[:, :, 1]], axis=2)
return project_points[0]
+35
View File
@@ -0,0 +1,35 @@
"""
@File : test.py
@Author : Songkey
@Email : songkey@pku.edu.cn
@Date : 11/1/2024
@Desc : Created by Shengjie Wu (wu.shengjie@immomo.com)
"""
import numpy as np
import cv2
from huggingface_hub import hf_hub_download
from .utils import create_onnx_session, get_warp_mat_bbox_by_gt_pts_float
class HelloARKitBSPred(object):
def __init__(self, gpu_id=0):
self.face_rig_net = create_onnx_session(hf_hub_download('songkey/hello_group_facemodel', filename='hello_arkit_blendshape.onnx'), gpu_id=gpu_id)
self.onnx_input_name = self.face_rig_net.get_inputs()[0].name
self.onnx_output_name = [output.name for output in self.face_rig_net.get_outputs()]
self.image_size = 224
self.expand_ratio = 0.15
def forward(self, src_image, src_pt):
left_eye_corner = src_pt[74]
right_eye_corner = src_pt[96]
radian = np.arctan2(right_eye_corner[1] - left_eye_corner[1], right_eye_corner[0] - left_eye_corner[0] + 0.00000001)
rotate_angle = np.rad2deg(radian)
align_warp_mat = get_warp_mat_bbox_by_gt_pts_float(src_pt, base_angle=rotate_angle, dst_size=self.image_size,
expand_ratio=self.expand_ratio)
face_rig_input = cv2.warpAffine(src_image, align_warp_mat, (self.image_size, self.image_size))
face_rig_onnx_input = face_rig_input.transpose((2, 0, 1)).astype(np.float32)[np.newaxis, :, :, :] / 255.0
face_rig_params = self.face_rig_net.run(self.onnx_output_name,
{self.onnx_input_name: face_rig_onnx_input})
face_rig_params = face_rig_params[0][0]
return face_rig_params
+377
View File
@@ -0,0 +1,377 @@
"""
@File : test.py
@Author : Songkey
@Email : songkey@pku.edu.cn
@Date : 11/1/2024
@Desc : Created by Shengjie Wu (wu.shengjie@immomo.com)
"""
import numpy as np
from .utils import OneEuroFilter
def cult_dis(old_kpts, new_kpts):
dis = np.sqrt(np.square(new_kpts[:, 0] - old_kpts[:, 0]) + np.square(new_kpts[:, 1] - old_kpts[:, 1]))
return dis
class Smoother222(object):
def __init__(self):
# face config
self.face_idx = list(range(0, 33))
self.face_down_idx = list(range(9, 24))
self.filter_face = OneEuroFilter()
# nose config
self.nose_idx = list(range(33, 48))
self.filter_nose = OneEuroFilter()
# eyebrow config
self.eyebrow_idx = list(range(48, 74))
self.filter_eyebrow = OneEuroFilter()
# eye config
self.left_eye_idx = list(range(74, 96))
self.filter_left_eye = OneEuroFilter()
self.right_eye_idx = list(range(96, 118))
self.filter_right_eye = OneEuroFilter()
# mouth config
self.mouth_idx = list(range(118, 182))
self.filter_mouth = OneEuroFilter()
# pupil config
self.left_pupil_idx = list(range(182, 202))
self.filter_left_pupil = OneEuroFilter()
self.right_pupil_idx = list(range(202, 222))
self.filter_right_pupil = OneEuroFilter()
self.prev_points = None
def smooth(self, new_points, face_dis):
if self.prev_points is None:
self.prev_points = new_points.copy()
return new_points
dis = cult_dis(self.prev_points, new_points) / face_dis
smooth_points = new_points.copy()
# smooth face
if np.mean(dis[self.face_down_idx]) < 0.005:
# stable
ratio_tmp = np.mean(dis[self.face_down_idx]) / 0.005
fcmin_tmp = 0.05 * ratio_tmp
beta_tmp = 0.05 * ratio_tmp
smooth_points[self.face_idx] = self.filter_face(new_points[self.face_idx], self.prev_points[self.face_idx],
fcmin=fcmin_tmp, beta=beta_tmp)
elif np.mean(dis[self.face_down_idx]) < 0.02:
# filter
ratio_tmp = (np.mean(dis[self.face_down_idx]) - 0.005) / (0.02 - 0.005)
fcmin_tmp = 0.05 + (0.3 - 0.05) * ratio_tmp
beta_tmp = 0.05 + (0.3 - 0.05) * ratio_tmp
smooth_points[self.face_idx] = self.filter_face(new_points[self.face_idx], self.prev_points[self.face_idx],
fcmin=fcmin_tmp, beta=beta_tmp)
else:
# filter
smooth_points[self.face_idx] = self.filter_face(new_points[self.face_idx], self.prev_points[self.face_idx],
fcmin=0.3, beta=0.3)
# smooth nose
if np.mean(dis[self.nose_idx]) < 0.003:
# stable
ratio_tmp = np.mean(dis[self.nose_idx]) / 0.003
fcmin_tmp = 0.03 * ratio_tmp
beta_tmp = 0.03 * ratio_tmp
smooth_points[self.nose_idx] = self.filter_nose(new_points[self.nose_idx], self.prev_points[self.nose_idx],
fcmin=fcmin_tmp, beta=beta_tmp)
elif np.mean(dis[self.nose_idx]) < 0.02:
ratio_tmp = (np.mean(dis[self.nose_idx]) - 0.003) / (0.02 - 0.003)
fcmin_tmp = 0.03 + (0.7 - 0.03) * ratio_tmp
beta_tmp = 0.03 + (0.7 - 0.03) * ratio_tmp
smooth_points[self.nose_idx] = self.filter_nose(new_points[self.nose_idx], self.prev_points[self.nose_idx],
fcmin=fcmin_tmp, beta=beta_tmp)
else:
# filter
smooth_points[self.nose_idx] = self.filter_nose(new_points[self.nose_idx], self.prev_points[self.nose_idx],
fcmin=0.7, beta=0.7)
# smooth eyebrow
# print(np.mean(dis[self.eyebrow_idx]))
if np.mean(dis[self.eyebrow_idx]) < 0.003:
# stable
ratio_tmp = np.mean(dis[self.eyebrow_idx]) / 0.003
fcmin_tmp = 0.02 * ratio_tmp
beta_tmp = 0.02 * ratio_tmp
smooth_points[self.eyebrow_idx] = self.filter_eyebrow(new_points[self.eyebrow_idx],
self.prev_points[self.eyebrow_idx],
fcmin=fcmin_tmp, beta=beta_tmp)
elif np.mean(dis[self.eyebrow_idx]) < 0.02:
# filter
ratio_tmp = (np.mean(dis[self.eyebrow_idx]) - 0.003) / (0.02 - 0.003)
fcmin_tmp = 0.02 + (0.5 - 0.02) * ratio_tmp
beta_tmp = 0.02 + (0.5 - 0.02) * ratio_tmp
smooth_points[self.eyebrow_idx] = self.filter_eyebrow(new_points[self.eyebrow_idx],
self.prev_points[self.eyebrow_idx],
fcmin=fcmin_tmp, beta=beta_tmp)
else:
# filter
smooth_points[self.eyebrow_idx] = self.filter_eyebrow(new_points[self.eyebrow_idx],
self.prev_points[self.eyebrow_idx],
fcmin=0.5, beta=0.5)
# smooth eye
if np.mean(dis[self.left_eye_idx]) < 0.003:
# stable
ratio_tmp = np.mean(dis[self.left_eye_idx]) / 0.003
fcmin_tmp = 0.03 * ratio_tmp
beta_tmp = 0.03 * ratio_tmp
smooth_points[self.left_eye_idx] = self.filter_left_eye(new_points[self.left_eye_idx],
self.prev_points[self.left_eye_idx],
fcmin=fcmin_tmp, beta=beta_tmp)
elif np.mean(dis[self.left_eye_idx]) < 0.02:
# filter
ratio_tmp = (np.mean(dis[self.left_eye_idx]) - 0.003) / (0.02 - 0.003)
fcmin_tmp = 0.03 + (0.7 - 0.03) * ratio_tmp
beta_tmp = 0.03 + (0.7 - 0.03) * ratio_tmp
smooth_points[self.left_eye_idx] = self.filter_left_eye(new_points[self.left_eye_idx],
self.prev_points[self.left_eye_idx],
fcmin=fcmin_tmp, beta=beta_tmp)
else:
# fast
smooth_points[self.left_eye_idx] = self.filter_left_eye(new_points[self.left_eye_idx],
self.prev_points[self.left_eye_idx],
fcmin=0.7, beta=0.7)
if np.mean(dis[self.right_eye_idx]) < 0.003:
# stable
ratio_tmp = np.mean(dis[self.right_eye_idx]) / 0.003
fcmin_tmp = 0.03 * ratio_tmp
beta_tmp = 0.03 * ratio_tmp
smooth_points[self.right_eye_idx] = self.filter_right_eye(new_points[self.right_eye_idx],
self.prev_points[self.right_eye_idx],
fcmin=fcmin_tmp, beta=beta_tmp)
elif np.mean(dis[self.right_eye_idx]) < 0.02:
# filter
ratio_tmp = (np.mean(dis[self.right_eye_idx]) - 0.003) / (0.02 - 0.003)
fcmin_tmp = 0.03 + (0.7 - 0.03) * ratio_tmp
beta_tmp = 0.03 + (0.7 - 0.03) * ratio_tmp
smooth_points[self.right_eye_idx] = self.filter_right_eye(new_points[self.right_eye_idx],
self.prev_points[self.right_eye_idx],
fcmin=fcmin_tmp, beta=beta_tmp)
else:
# fast
smooth_points[self.right_eye_idx] = self.filter_right_eye(new_points[self.right_eye_idx],
self.prev_points[self.right_eye_idx],
fcmin=0.7, beta=0.7)
# smooth mouth
if np.mean(dis[self.mouth_idx]) < 0.003:
# stable
ratio_tmp = np.mean(dis[self.mouth_idx]) / 0.003
fcmin_tmp = 0.05 * ratio_tmp
beta_tmp = 0.05 * ratio_tmp
smooth_points[self.mouth_idx] = self.filter_mouth(new_points[self.mouth_idx],
self.prev_points[self.mouth_idx],
fcmin=fcmin_tmp, beta=beta_tmp)
elif np.mean(dis[self.mouth_idx]) < 0.02:
# filter
ratio_tmp = (np.mean(dis[self.mouth_idx]) - 0.003) / (0.02 - 0.003)
fcmin_tmp = 0.05 + (0.7 - 0.05) * ratio_tmp
beta_tmp = 0.05 + (0.7 - 0.05) * ratio_tmp
smooth_points[self.mouth_idx] = self.filter_mouth(new_points[self.mouth_idx],
self.prev_points[self.mouth_idx],
fcmin=fcmin_tmp, beta=beta_tmp)
else:
# fast
smooth_points[self.mouth_idx] = self.filter_mouth(new_points[self.mouth_idx],
self.prev_points[self.mouth_idx],
fcmin=0.7, beta=0.7)
# smooth pupil
if np.mean(dis[self.left_pupil_idx]) < 0.003:
# stable
ratio_tmp = np.mean(dis[self.left_pupil_idx]) / 0.003
fcmin_tmp = 0.03 * ratio_tmp
beta_tmp = 0.03 * ratio_tmp
smooth_points[self.left_pupil_idx] = self.filter_left_pupil(new_points[self.left_pupil_idx],
self.prev_points[self.left_pupil_idx],
fcmin=fcmin_tmp, beta=beta_tmp)
elif np.mean(dis[self.left_pupil_idx]) < 0.02:
# filter
ratio_tmp = (np.mean(dis[self.left_pupil_idx]) - 0.003) / (0.02 - 0.003)
fcmin_tmp = 0.03 + (0.7 - 0.03) * ratio_tmp
beta_tmp = 0.03 + (0.7 - 0.03) * ratio_tmp
smooth_points[self.left_pupil_idx] = self.filter_left_pupil(new_points[self.left_pupil_idx],
self.prev_points[self.left_pupil_idx],
fcmin=fcmin_tmp, beta=beta_tmp)
else:
# fast
smooth_points[self.left_pupil_idx] = self.filter_left_pupil(new_points[self.left_pupil_idx],
self.prev_points[self.left_pupil_idx],
fcmin=0.7, beta=0.7)
if np.mean(dis[self.right_pupil_idx]) < 0.003:
# stable
ratio_tmp = np.mean(dis[self.right_pupil_idx]) / 0.003
fcmin_tmp = 0.03 * ratio_tmp
beta_tmp = 0.03 * ratio_tmp
smooth_points[self.right_pupil_idx] = self.filter_right_pupil(new_points[self.right_pupil_idx],
self.prev_points[self.right_pupil_idx],
fcmin=fcmin_tmp, beta=beta_tmp)
elif np.mean(dis[self.right_pupil_idx]) < 0.02:
# filter
ratio_tmp = (np.mean(dis[self.right_pupil_idx]) - 0.003) / (0.02 - 0.003)
fcmin_tmp = 0.03 + (0.7 - 0.03) * ratio_tmp
beta_tmp = 0.03 + (0.7 - 0.03) * ratio_tmp
smooth_points[self.right_pupil_idx] = self.filter_right_pupil(new_points[self.right_pupil_idx],
self.prev_points[self.right_pupil_idx],
fcmin=fcmin_tmp, beta=beta_tmp)
else:
# fast
smooth_points[self.right_pupil_idx] = self.filter_right_pupil(new_points[self.right_pupil_idx],
self.prev_points[self.right_pupil_idx],
fcmin=0.7, beta=0.7)
# update pre points
self.prev_points = smooth_points
return smooth_points
class HelloCameraDemo(object):
def __init__(self, face_alignment_module, reset=False):
self.face_alignment_module = face_alignment_module
self.face_prob_th = 0.01
self.min_face = 96
self.face_image_size = self.face_alignment_module.face_image_size
self.trackingFaces = []
self.reset = reset
def reset_track(self):
self.trackingFaces = []
def forward(self, src_image, reset=False, pre_rect=None):
if self.reset or reset:
self.trackingFaces = []
if len(self.trackingFaces) == 0:
if pre_rect is not None:
detected_faces = [pre_rect]
else:
detected_faces, _, _ = self.face_alignment_module.face_detector.detect(src_image)
for face_rect in detected_faces:
new_tracking_object = {
'face_rect': face_rect,
'rotate_angle': 0.0,
'pre_kpt_222': None,
'face_dis': np.sqrt(
np.square((face_rect[2] - face_rect[0])) + np.square((face_rect[3] - face_rect[1]))),
'smoother_222': Smoother222(),
'prob': 0
}
self.trackingFaces.append(new_tracking_object)
else:
detected_faces, _, _ = self.face_alignment_module.face_detector.detect(src_image)
for face_rect in detected_faces:
new_tracking_object = {
'face_rect': face_rect,
'rotate_angle': 0.0,
'pre_kpt_222': None,
'face_dis': np.sqrt(
np.square((face_rect[2] - face_rect[0])) + np.square((face_rect[3] - face_rect[1]))),
'smoother_222': Smoother222(),
'prob': 0
}
self.trackingFaces.append(new_tracking_object)
delete_idx_list = []
for face_idx, tracking_face in enumerate(self.trackingFaces):
if tracking_face['pre_kpt_222'] is not None:
result_dict = self.face_alignment_module.forward(src_image, pre_pts=tracking_face['pre_kpt_222'],
iterations=3)
else:
result_dict = self.face_alignment_module.forward(src_image, face_box=tracking_face['face_rect'],
iterations=3)
if result_dict['prob'] < self.face_prob_th:
if not face_idx in delete_idx_list:
delete_idx_list.append(face_idx)
continue
landmarks_final = tracking_face['smoother_222'].smooth(result_dict['pt222'], tracking_face['face_dis'])
tracking_face['pre_kpt_222'] = landmarks_final
left_eye_corner = landmarks_final[74]
right_eye_corner = landmarks_final[96]
radian = np.arctan2(right_eye_corner[1] - left_eye_corner[1],
right_eye_corner[0] - left_eye_corner[0] + 0.00000001)
rotate_angle = np.rad2deg(radian)
face_x_min, face_x_max = np.min(landmarks_final[:, 0]), np.max(landmarks_final[:, 0])
face_y_min, face_y_max = np.min(landmarks_final[:, 1]), np.max(landmarks_final[:, 1])
face_bbox = [face_x_min, face_y_min, face_x_max, face_y_max]
face_dis = np.linalg.norm(landmarks_final[0] - landmarks_final[32])
if face_x_max - face_x_min < self.min_face or face_y_max - face_y_min < self.min_face:
if not face_idx in delete_idx_list:
delete_idx_list.append(face_idx)
euler_pred = result_dict["euler_rad"]
pitch = np.rad2deg(euler_pred[0])
yaw = np.rad2deg(euler_pred[1])
roll = np.rad2deg(euler_pred[2])
# print("pitch, yaw, roll", pitch, yaw, roll)
# one filter model
max_euler = abs(pitch) + (abs(yaw)*0.6)
face_dis *= (1.0 + max_euler / 18.0)
# two filter model
tracking_face['face_rect'] = face_bbox
tracking_face['rotate_angle'] = rotate_angle
tracking_face['face_dis'] = face_dis
tracking_face['prob'] = result_dict['prob']
tracking_face['pitch'] = pitch
tracking_face['yaw'] = yaw
tracking_face['roll'] = roll
tracking_face['euler_rad'] = result_dict["euler_rad"]
if len(self.trackingFaces) > 1:
for face_idx, tracking_face_target in enumerate(self.trackingFaces):
if face_idx in delete_idx_list:
continue
for idx, tracking_face in enumerate(self.trackingFaces):
if idx in delete_idx_list:
continue
if face_idx == idx: continue
iou_temp = self.count_iou(tracking_face_target['face_rect'], tracking_face['face_rect'])
# prog 2
if iou_temp > 0.12:
if self.area(tracking_face_target['face_rect']) - self.area(tracking_face['face_rect']) < 0:
if not face_idx in delete_idx_list:
delete_idx_list.append(face_idx)
else:
if not idx in delete_idx_list:
delete_idx_list.append(idx)
idx_offset = 0
for delete_idx in sorted(delete_idx_list):
self.trackingFaces.pop(delete_idx - idx_offset)
idx_offset += 1
return self.trackingFaces
def count_iou(self, boxA, boxB):
# determine the (x, y)-coordinates of the intersection rectangle
xA = max(boxA[0], boxB[0])
yA = max(boxA[1], boxB[1])
xB = min(boxA[2], boxB[2])
yB = min(boxA[3], boxB[3])
# compute the area of intersection rectangle
interArea = abs(max((xB - xA, 0)) * max((yB - yA), 0))
if interArea == 0:
return 0
# compute the area of both the prediction and ground-truth
# rectangles
boxAArea = abs((boxA[2] - boxA[0]) * (boxA[3] - boxA[1]))
boxBArea = abs((boxB[2] - boxB[0]) * (boxB[3] - boxB[1]))
# compute the intersection over union by taking the intersection
# area and dividing it by the sum of prediction + ground-truth
# areas - the interesection area
iou = interArea / float(boxAArea + boxBArea - interArea)
# return the intersection over union value
return iou
def area(self, bbox):
w = bbox[3]-bbox[1]
h = bbox[2]-bbox[0]
return w * h
+93
View File
@@ -0,0 +1,93 @@
"""
@File : test.py
@Author : Songkey
@Email : songkey@pku.edu.cn
@Date : 11/1/2024
@Desc : Created by Shengjie Wu (wu.shengjie@immomo.com)
"""
import cv2
import numpy as np
from .hello_face_det import HelloFaceDet
from .utils import get_warp_mat_bbox, get_warp_mat_bbox_by_gt_pts_float, transform_points
from .utils import create_onnx_session
from huggingface_hub import hf_hub_download
class HelloFaceAlignment(object):
def __init__(self, gpu_id=None):
expand_ratio = 0.15
self.face_alignment_net_222 = (
create_onnx_session(hf_hub_download('songkey/hello_group_facemodel', filename='hello_face_landmark.onnx'), gpu_id=gpu_id))
self.onnx_input_name_222 = self.face_alignment_net_222.get_inputs()[0].name
self.onnx_output_name_222 = [output.name for output in self.face_alignment_net_222.get_outputs()]
self.face_image_size = 128
self.face_detector = HelloFaceDet(hf_hub_download('songkey/hello_group_facemodel', filename='hello_face_det.onnx'), gpu_id=gpu_id)
self.expand_ratio = expand_ratio
def onnx_infer(self, input_uint8):
assert input_uint8.shape[0] == input_uint8.shape[1] == self.face_image_size
onnx_input = input_uint8.transpose((2, 0, 1)).astype(np.float32)[np.newaxis, :, :, :] / 255.0
landmark, euler, prob = self.face_alignment_net_222.run(self.onnx_output_name_222,
{self.onnx_input_name_222: onnx_input})
landmark = np.reshape(landmark[0], (2, -1)).transpose((1, 0)) * self.face_image_size
left_eye_corner = landmark[74]
right_eye_corner = landmark[96]
radian = np.arctan2(right_eye_corner[1] - left_eye_corner[1],
right_eye_corner[0] - left_eye_corner[0] + 0.00000001)
euler_rad = np.array([euler[0, 0], euler[0, 1], radian], dtype=np.float32)
prob = prob[0]
return landmark, euler_rad, prob
def forward(self, src_image, face_box=None, pre_pts=None, iterations=3):
if pre_pts is None:
if face_box is None:
# Detect max size face
bounding_boxes, _, score = self.face_detector.detect(src_image)
print("facedet score", score)
if len(bounding_boxes) == 0:
return None
bbox = np.zeros(4, dtype=np.float32)
if len(bounding_boxes) >= 1:
max_area = 0.0
for each_bbox in bounding_boxes:
area = (each_bbox[2] - each_bbox[0]) * (each_bbox[3] - each_bbox[1])
if area > max_area:
bbox[:4] = each_bbox[:4]
max_area = area
else:
bbox = bounding_boxes[0, :4]
else:
bbox = face_box.copy()
M_Face = get_warp_mat_bbox(bbox, 0, self.face_image_size, expand_ratio=self.expand_ratio)
else:
left_eye_corner = pre_pts[74]
right_eye_corner = pre_pts[96]
radian = np.arctan2(right_eye_corner[1] - left_eye_corner[1],
right_eye_corner[0] - left_eye_corner[0] + 0.00000001)
M_Face = get_warp_mat_bbox_by_gt_pts_float(pre_pts, np.rad2deg(radian), self.face_image_size,
expand_ratio=self.expand_ratio)
face_input = cv2.warpAffine(src_image, M_Face, (self.face_image_size, self.face_image_size))
landmarks, euler, prob = self.onnx_infer(face_input)
landmarks = transform_points(landmarks, M_Face, invert=True)
# Repeat
for i in range(iterations - 1):
M_Face = get_warp_mat_bbox_by_gt_pts_float(landmarks, np.rad2deg(euler[2]), self.face_image_size,
expand_ratio=self.expand_ratio)
face_input = cv2.warpAffine(src_image, M_Face, (self.face_image_size, self.face_image_size))
landmarks, euler, prob = self.onnx_infer(face_input)
landmarks = transform_points(landmarks, M_Face, invert=True)
return_dict = {
"pt222": landmarks,
"euler_rad": euler,
"prob": prob,
"M_Face": M_Face,
"face_input": face_input
}
return return_dict
+326
View File
@@ -0,0 +1,326 @@
"""
@File : test.py
@Author : Songkey
@Email : songkey@pku.edu.cn
@Date : 11/1/2024
@Desc : Created by Zemin An (an.zemin@hellogroup.com)
"""
from abc import ABCMeta, abstractmethod
import cv2
import numpy as np
from scipy.special import softmax
import os.path as osp
from .utils import create_onnx_session
songkey_weights_dir = 'pretrained_models'
_COLORS = (
np.array(
[
0.000,
0.447,
0.741,
]
)
.astype(np.float32)
.reshape(-1, 3)
)
def get_resize_matrix(raw_shape, dst_shape, keep_ratio):
"""
Get resize matrix for resizing raw img to input size
:param raw_shape: (width, height) of raw image
:param dst_shape: (width, height) of input image
:param keep_ratio: whether keep original ratio
:return: 3x3 Matrix
"""
r_w, r_h = raw_shape
d_w, d_h = dst_shape
Rs = np.eye(3)
if keep_ratio:
C = np.eye(3)
C[0, 2] = -r_w / 2
C[1, 2] = -r_h / 2
if r_w / r_h < d_w / d_h:
ratio = d_h / r_h
else:
ratio = d_w / r_w
Rs[0, 0] *= ratio
Rs[1, 1] *= ratio
T = np.eye(3)
T[0, 2] = 0.5 * d_w
T[1, 2] = 0.5 * d_h
return T @ Rs @ C
else:
Rs[0, 0] *= d_w / r_w
Rs[1, 1] *= d_h / r_h
return Rs
def warp_boxes(boxes, M, width, height):
"""Apply transform to boxes
Copy from nanodet/data/transform/warp.py
"""
n = len(boxes)
if n:
# warp points
xy = np.ones((n * 4, 3))
xy[:, :2] = boxes[:, [0, 1, 2, 3, 0, 3, 2, 1]].reshape(
n * 4, 2
) # x1y1, x2y2, x1y2, x2y1
xy = xy @ M.T # transform
xy = (xy[:, :2] / xy[:, 2:3]).reshape(n, 8) # rescale
# create new boxes
x = xy[:, [0, 2, 4, 6]]
y = xy[:, [1, 3, 5, 7]]
xy = np.concatenate((x.min(1), y.min(1), x.max(1), y.max(1))).reshape(4, n).T
# clip boxes
xy[:, [0, 2]] = xy[:, [0, 2]].clip(0, width)
xy[:, [1, 3]] = xy[:, [1, 3]].clip(0, height)
return xy.astype(np.float32)
else:
return boxes
def overlay_bbox_cv(img, all_box, class_names):
"""Draw result boxes
Copy from nanodet/util/visualization.py
"""
# all_box array of [label, x0, y0, x1, y1, score]
all_box.sort(key=lambda v: v[5])
for box in all_box:
label, x0, y0, x1, y1, score = box
# color = self.cmap(i)[:3]
color = (_COLORS[label] * 255).astype(np.uint8).tolist()
text = "{}:{:.1f}%".format(class_names[label], score * 100)
txt_color = (0, 0, 0) if np.mean(_COLORS[label]) > 0.5 else (255, 255, 255)
font = cv2.FONT_HERSHEY_SIMPLEX
txt_size = cv2.getTextSize(text, font, 0.5, 2)[0]
cv2.rectangle(img, (x0, y0), (x1, y1), color, 2)
cv2.rectangle(
img,
(x0, y0 - txt_size[1] - 1),
(x0 + txt_size[0] + txt_size[1], y0 - 1),
color,
-1,
)
cv2.putText(img, text, (x0, y0 - 1), font, 0.5, txt_color, thickness=1)
return img
def hard_nms(box_scores, iou_threshold, top_k=-1, candidate_size=200):
"""
Args:
box_scores (N, 5): boxes in corner-form and probabilities.
iou_threshold: intersection over union threshold.
top_k: keep top_k results. If k <= 0, keep all the results.
candidate_size: only consider the candidates with the highest scores.
Returns:
picked: a list of indexes of the kept boxes
"""
scores = box_scores[:, -1]
boxes = box_scores[:, :-1]
picked = []
# _, indexes = scores.sort(descending=True)
indexes = np.argsort(scores)
# indexes = indexes[:candidate_size]
indexes = indexes[-candidate_size:]
while len(indexes) > 0:
# current = indexes[0]
current = indexes[-1]
picked.append(current)
if 0 < top_k == len(picked) or len(indexes) == 1:
break
current_box = boxes[current, :]
# indexes = indexes[1:]
indexes = indexes[:-1]
rest_boxes = boxes[indexes, :]
iou = iou_of(
rest_boxes,
np.expand_dims(current_box, axis=0),
)
indexes = indexes[iou <= iou_threshold]
return box_scores[picked, :]
def iou_of(boxes0, boxes1, eps=1e-5):
"""Return intersection-over-union (Jaccard index) of boxes.
Args:
boxes0 (N, 4): ground truth boxes.
boxes1 (N or 1, 4): predicted boxes.
eps: a small number to avoid 0 as denominator.
Returns:
iou (N): IoU values.
"""
overlap_left_top = np.maximum(boxes0[..., :2], boxes1[..., :2])
overlap_right_bottom = np.minimum(boxes0[..., 2:], boxes1[..., 2:])
overlap_area = area_of(overlap_left_top, overlap_right_bottom)
area0 = area_of(boxes0[..., :2], boxes0[..., 2:])
area1 = area_of(boxes1[..., :2], boxes1[..., 2:])
return overlap_area / (area0 + area1 - overlap_area + eps)
def area_of(left_top, right_bottom):
"""Compute the areas of rectangles given two corners.
Args:
left_top (N, 2): left top corner.
right_bottom (N, 2): right bottom corner.
Returns:
area (N): return the area.
"""
hw = np.clip(right_bottom - left_top, 0.0, None)
return hw[..., 0] * hw[..., 1]
class NanoDetABC(metaclass=ABCMeta):
def __init__(
self,
input_shape=[272, 160],
reg_max=7,
strides=[8, 16, 32],
prob_threshold=0.4,
iou_threshold=0.3,
num_candidate=1000,
top_k=-1,
class_names=["face"]
):
self.strides = strides
self.input_shape = input_shape
self.reg_max = reg_max
self.prob_threshold = prob_threshold
self.iou_threshold = iou_threshold
self.num_candidate = num_candidate
self.top_k = top_k
self.img_mean = [103.53, 116.28, 123.675]
self.img_std = [57.375, 57.12, 58.395]
self.input_size = (self.input_shape[1], self.input_shape[0])
self.class_names = class_names
self.num_classes = len(self.class_names)
def preprocess(self, img):
# resize image
ResizeM = get_resize_matrix((img.shape[1], img.shape[0]), self.input_size, True)
img_resize = cv2.warpPerspective(img, ResizeM, dsize=self.input_size)
# normalize image
img_input = img_resize.astype(np.float32) / 255
img_mean = np.array(self.img_mean, dtype=np.float32).reshape(1, 1, 3) / 255
img_std = np.array(self.img_std, dtype=np.float32).reshape(1, 1, 3) / 255
img_input = (img_input - img_mean) / img_std
# expand dims
img_input = np.transpose(img_input, [2, 0, 1])
img_input = np.expand_dims(img_input, axis=0)
return img_input, ResizeM
def postprocess(self, scores, raw_boxes, ResizeM, raw_shape):
# generate centers
decode_boxes = []
select_scores = []
for stride, box_distribute, score in zip(self.strides, raw_boxes, scores):
# centers
fm_h = self.input_shape[0] / stride
fm_w = self.input_shape[1] / stride
h_range = np.arange(fm_h)
w_range = np.arange(fm_w)
ww, hh = np.meshgrid(w_range, h_range)
ct_row = hh.flatten() * stride
ct_col = ww.flatten() * stride
center = np.stack((ct_col, ct_row, ct_col, ct_row), axis=1)
# box distribution to distance
reg_range = np.arange(self.reg_max + 1)
box_distance = box_distribute.reshape((-1, self.reg_max + 1))
box_distance = softmax(box_distance, axis=1)
box_distance = box_distance * np.expand_dims(reg_range, axis=0)
box_distance = np.sum(box_distance, axis=1).reshape((-1, 4))
box_distance = box_distance * stride
# top K candidate
topk_idx = np.argsort(score.max(axis=1))[::-1]
topk_idx = topk_idx[: self.num_candidate]
center = center[topk_idx]
score = score[topk_idx]
box_distance = box_distance[topk_idx]
# decode box
decode_box = center + [-1, -1, 1, 1] * box_distance
select_scores.append(score)
decode_boxes.append(decode_box)
# nms
bboxes = np.concatenate(decode_boxes, axis=0)
confidences = np.concatenate(select_scores, axis=0)
picked_box_probs = []
picked_labels = []
for class_index in range(0, confidences.shape[1]):
probs = confidences[:, class_index]
mask = probs > self.prob_threshold
probs = probs[mask]
if probs.shape[0] == 0:
continue
subset_boxes = bboxes[mask, :]
box_probs = np.concatenate([subset_boxes, probs.reshape(-1, 1)], axis=1)
box_probs = hard_nms(
box_probs,
iou_threshold=self.iou_threshold,
top_k=self.top_k,
)
picked_box_probs.append(box_probs)
picked_labels.extend([class_index] * box_probs.shape[0])
if not picked_box_probs:
return np.array([]), np.array([]), np.array([])
picked_box_probs = np.concatenate(picked_box_probs)
# resize output boxes
picked_box_probs[:, :4] = warp_boxes(
picked_box_probs[:, :4], np.linalg.inv(ResizeM), raw_shape[1], raw_shape[0]
)
return (
picked_box_probs[:, :4].astype(np.int32),
np.array(picked_labels),
picked_box_probs[:, 4],
)
@abstractmethod
def infer_image(self, img_input):
pass
def detect(self, img):
raw_shape = img.shape
img_input, ResizeM = self.preprocess(img)
scores, raw_boxes = self.infer_image(img_input)
if scores[0].ndim == 1: # handling num_classes=1 case
scores = [x[:, None] for x in scores]
bbox, label, score = self.postprocess(scores, raw_boxes, ResizeM, raw_shape)
return bbox, label, score
class HelloFaceDet(NanoDetABC):
def __init__(self, model_path=osp.join(songkey_weights_dir, 'face/nanodet_humandet_320-192_220302_model_20220315_test3.onnx'), gpu_id=None, *args, **kwargs):
super(HelloFaceDet, self).__init__(*args, **kwargs)
# print("Using ONNX as inference backend")
# print(f"Using weight: {model_path}")
# load model
self.model_path = model_path
self.ort_session = create_onnx_session(model_path, gpu_id=gpu_id)
self.input_name = self.ort_session.get_inputs()[0].name
def infer_image(self, img_input):
inference_results = self.ort_session.run(None, {self.input_name: img_input})
scores = [np.squeeze(x) for x in inference_results[:3]]
raw_boxes = [np.squeeze(x) for x in inference_results[3:]]
return scores, raw_boxes
+118
View File
@@ -0,0 +1,118 @@
# coding: utf-8
# @File : utils.py
# @Author : Songkey
# @Email : songkey@pku.edu.cn
# @Date : 8/18/2024
# @Desc :
import onnx, onnxruntime
import time
import cv2
import numpy as np
import math
def create_onnx_session(onnx_path, gpu_id=None)->onnxruntime.InferenceSession:
start = time.perf_counter()
onnx_model = onnx.load(onnx_path)
onnx.checker.check_model(onnx_model)
providers = [
('CUDAExecutionProvider', {
'device_id': int(gpu_id),
'arena_extend_strategy': 'kNextPowerOfTwo',
#'cuda_mem_limit': 5 * 1024 * 1024 * 1024,
'cudnn_conv_algo_search': 'EXHAUSTIVE',
'do_copy_in_default_stream': True,
}),
'CPUExecutionProvider',
] if (gpu_id is not None and gpu_id >= 0) else ['CPUExecutionProvider']
sess = onnxruntime.InferenceSession(onnx_path, providers=providers)
print('create onnx session cost: {:.3f}s. {}'.format(time.perf_counter() - start, onnx_path))
return sess
def smoothing_factor(t_e, cutoff):
r = 2 * math.pi * cutoff * t_e
return r / (r + 1)
def exponential_smoothing(a, x, x_prev):
return a * x + (1 - a) * x_prev
class OneEuroFilter:
def __init__(self, dx0=0.0, d_cutoff=1.0):
"""Initialize the one euro filter."""
# self.min_cutoff = float(min_cutoff)
# self.beta = float(beta)
self.d_cutoff = float(d_cutoff)
self.dx_prev = float(dx0)
# self.t_e = fcmin
def __call__(self, x, x_prev, fcmin=1.0, min_cutoff=1.0, beta=0.0):
if x_prev is None:
return x
# t_e = 1
a_d = smoothing_factor(fcmin, self.d_cutoff)
dx = (x - x_prev) / fcmin
dx_hat = exponential_smoothing(a_d, dx, self.dx_prev)
cutoff = min_cutoff + beta * abs(dx_hat)
a = smoothing_factor(fcmin, cutoff)
x_hat = exponential_smoothing(a, x, x_prev)
self.dx_prev = dx_hat
return x_hat
def get_warp_mat_bbox(face_bbox, base_angle, dst_size=128, expand_ratio=0.15, aug_angle=0.0, aug_scale=1.0):
face_x_min, face_y_min, face_x_max, face_y_max = face_bbox
face_x_center = (face_x_min + face_x_max) / 2
face_y_center = (face_y_min + face_y_max) / 2
face_width = face_x_max - face_x_min
face_height = face_y_max - face_y_min
scale = dst_size / max(face_width, face_height) * (1 - expand_ratio) * aug_scale
M = cv2.getRotationMatrix2D((face_x_center, face_y_center), angle=base_angle + aug_angle, scale=scale)
offset = [dst_size / 2 - face_x_center, dst_size / 2 - face_y_center]
M[:, 2] += offset
return M
def transform_points(points, mat, invert=False):
if invert:
mat = cv2.invertAffineTransform(mat)
points = np.expand_dims(points, axis=1)
points = cv2.transform(points, mat, points.shape)
points = np.squeeze(points)
return points
def get_warp_mat_bbox_by_gt_pts_float(gt_pts, base_angle=0.0, dst_size=128, expand_ratio=0.15, return_info=False):
# step 1
face_x_min, face_x_max = np.min(gt_pts[:, 0]), np.max(gt_pts[:, 0])
face_y_min, face_y_max = np.min(gt_pts[:, 1]), np.max(gt_pts[:, 1])
face_x_center = (face_x_min + face_x_max) / 2
face_y_center = (face_y_min + face_y_max) / 2
M_step_1 = cv2.getRotationMatrix2D((face_x_center, face_y_center), angle=base_angle, scale=1.0)
pts_step_1 = transform_points(gt_pts, M_step_1)
face_x_min_step_1, face_x_max_step_1 = np.min(pts_step_1[:, 0]), np.max(pts_step_1[:, 0])
face_y_min_step_1, face_y_max_step_1 = np.min(pts_step_1[:, 1]), np.max(pts_step_1[:, 1])
# step 2
face_width = face_x_max_step_1 - face_x_min_step_1
face_height = face_y_max_step_1 - face_y_min_step_1
scale = dst_size / max(face_width, face_height) * (1 - expand_ratio)
M_step_2 = cv2.getRotationMatrix2D((face_x_center, face_y_center), angle=base_angle, scale=scale)
pts_step_2 = transform_points(gt_pts, M_step_2)
face_x_min_step_2, face_x_max_step_2 = np.min(pts_step_2[:, 0]), np.max(pts_step_2[:, 0])
face_y_min_step_2, face_y_max_step_2 = np.min(pts_step_2[:, 1]), np.max(pts_step_2[:, 1])
face_x_center_step_2 = (face_x_min_step_2 + face_x_max_step_2) / 2
face_y_center_step_2 = (face_y_min_step_2 + face_y_max_step_2) / 2
M = cv2.getRotationMatrix2D((face_x_center, face_y_center), angle=base_angle, scale=scale)
offset = [dst_size / 2 - face_x_center_step_2, dst_size / 2 - face_y_center_step_2]
M[:, 2] += offset
if not return_info:
return M
else:
transform_info = {
"M": M,
"center_x": face_x_center,
"center_y": face_y_center,
"rotate_angle": base_angle,
"scale": scale
}
return transform_info
+379
View File
@@ -0,0 +1,379 @@
# coding: utf-8
"""
@File : utils.py
@Author : Songkey
@Email : songkey@pku.edu.cn
@Date : 8/28/2024
@Desc :
"""
import os
import cv2
import numpy as np
import torch
from PIL import Image
import subprocess
from einops import rearrange
from .tools.utils import transform_points
from diffusers.pipelines.stable_diffusion.convert_from_ckpt import convert_ldm_unet_checkpoint
from safetensors import safe_open
def load_safetensors(model_path):
tensors = {}
with safe_open(model_path, framework="pt", device=0) as f:
for k in f.keys():
tensors[k] = f.get_tensor(k) # loads the full tensor given a key
return tensors
def load_unet_from_safetensors(safetensors_path, unet_config):
original_stats = load_safetensors(safetensors_path)
return convert_ldm_unet_checkpoint(original_stats, unet_config)
def image_preprocess(np_bgr, size, dtype=torch.float32):
img_np = cv2.resize(np_bgr, size)
return np_bgr_to_tensor(img_np, dtype)
def np_bgr_to_tensor(img_np, dtype):
img_rgb = cv2.cvtColor(img_np, cv2.COLOR_BGR2RGB) / 255. * 2 - 1
return torch.tensor(img_rgb).permute(2, 0, 1).to(dtype=dtype)
def tensor_to_np_rgb(img_np):
img_rgb = np.clip((img_np.permute(1, 2, 0).detach().cpu().numpy() + 1) / 2 * 255, 0, 255)
return img_rgb.astype(np.uint8)
def clip_preprocess_from_bgr(image_bgr, dtype=torch.float32):
temp_image = cv2.resize(image_bgr, (224, 224), interpolation=cv2.INTER_CUBIC)
temp_image = cv2.cvtColor(temp_image, cv2.COLOR_BGR2RGB)
temp_image_np = temp_image * 0.00392156862745098
temp_image_np = (temp_image_np - [0.48145466, 0.4578275, 0.40821073]) / [0.26862954, 0.26130258, 0.27577711]
temp_image_np = np.transpose(temp_image_np, (2, 0, 1))
return torch.tensor(temp_image_np, dtype=dtype)
def clip_preprocess_to_bgr(image_tensor):
image_tensor_np = image_tensor.detach().cpu().numpy().astype(float)
image_tensor_np = np.transpose(image_tensor_np, (1, 2, 0))
image_tensor_np = (image_tensor_np * [0.26862954, 0.26130258, 0.27577711]) + [0.48145466, 0.4578275, 0.40821073]
image_tensor_np = np.clip(image_tensor_np / 0.00392156862745098, 0, 255).astype(np.uint8)
return cv2.cvtColor(image_tensor_np, cv2.COLOR_BGR2RGB)
def clip_preprocess_from_pil(image_pil, dtype):
temp_image = image_pil.resize((224, 224), resample=Image.BICUBIC)
temp_image_np = np.asarray(temp_image) * 0.00392156862745098
temp_image_np = (temp_image_np - [0.48145466, 0.4578275, 0.40821073]) / [0.26862954, 0.26130258, 0.27577711]
temp_image_np = np.transpose(temp_image_np, (2, 0, 1))
return torch.tensor(temp_image_np, dtype=dtype)
def draw_skl_by_rect(save_size, rect):
rect = rect.astype(np.int32)
ret_img = np.zeros((save_size, save_size, 3), dtype=np.uint8)
# cv2.rectangle(ret_img, rect[0], rect[2], (255, 255, 255), -1)
cv2.line(ret_img, rect[0], rect[1], (0, 255, 0), 15)
cv2.line(ret_img, rect[1], rect[2], (255, 0, 0), 15)
cv2.line(ret_img, rect[2], rect[3], (0, 0, 255), 15)
cv2.line(ret_img, rect[3], rect[0], (255, 0, 255), 15)
return ret_img
def get_face_part_1frame(image, parts, save_size=256):
ret_list = []
for part_landmarks in parts:
part_tls, part_brs = np.min(part_landmarks, axis=0), np.max(part_landmarks, axis=0)
center = (part_tls + part_brs) * 0.5
wh = max(part_brs - part_tls) * 1.2
scale = save_size / wh
M = cv2.getRotationMatrix2D(center, 0, scale)
M[:, 2] += save_size * 0.5 - center
part = cv2.warpAffine(image, M, (save_size, save_size))
ret_list.append(part)
return np.stack(ret_list, axis=0)
face_parts_regions = [
[48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94],
[61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116],
[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],
]
face_region = [33, 74, 96, 133, 149]
face_temp = np.array([[0.50951539, 0.40128625], [0.29259476, 0.40203411], [0.72196212, 0.40857479], [0.63420714, 0.71678643], [0.38437035, 0.71723224]])
def get_face_parts(images, landmarks, save_size=256):
H, W = images[0].shape[:2]
warped_imgs = []
warped_landmarks = []
for landmark, frame in zip(landmarks, images):
src_landmarks = landmark[face_region, :]
dst_landmarks = face_temp * (W, H)
M = umeyama(src_landmarks, dst_landmarks, True)[0:2]
warped_img = cv2.warpAffine(frame, M, (W, H))
warped_landmark = transform_points(landmark.astype(np.float32), M)[:,:2]
# for x, y in warped_landmark:
# warped_img = cv2.circle(warped_img, (int(x), int(y)), 1, (255, 255, 0), -1)
# for x, y in dst_landmarks:
# warped_img = cv2.circle(warped_img, (int(x), int(y)), 1, (0, 255, 0), -1)
# cv2.imshow('warped_img', warped_img)
# cv2.waitKey()
warped_imgs.append(warped_img)
warped_landmarks.append(warped_landmark)
warped_imgs = np.stack(warped_imgs, axis=0)
warped_landmarks = np.stack(warped_landmarks, axis=0)
ret_list = []
for region in face_parts_regions:
part_landmarks = warped_landmarks[:, region, :]
part_tls, part_brs = np.min(np.min(part_landmarks, axis=1), axis=0), np.max(np.max(part_landmarks, axis=1), axis=0)
center = (part_tls + part_brs) * 0.5
wh = max(part_brs - part_tls) * 1.2
scale = save_size / wh
M = cv2.getRotationMatrix2D(center, 0, scale)
M[:, 2] += save_size * 0.5 - center
tmp_part_list = []
for warped_img in warped_imgs:
part = cv2.warpAffine(warped_img, M, (save_size, save_size))
# cv2.imshow('warped_img', warped_img)
# cv2.imshow('part', part)
# cv2.waitKey()
tmp_part_list.append(part)
ret_list.append(np.stack(tmp_part_list, axis=0))
return np.stack(ret_list, axis=0).transpose(1, 0, 2, 3, 4)
def get_face_params(h3dmm, harkit_bs, frames, landmarks, save_size=512, align=True):
face_parts = []
for tmp_face_parts_list in get_face_parts(frames, landmarks, save_size=256):
tmp_face_parts_list = [cv2.GaussianBlur(x, (9, 9), 0) for x in tmp_face_parts_list]
face_parts.append(tmp_face_parts_list)
if align:
new_frames, new_landmarks = crop_and_resize(np.stack(frames, axis=0), np.stack(landmarks, axis=0),
save_size=save_size, crop=False)
else:
new_frames, new_landmarks = frames, landmarks
rot_list = []
trans_list = []
arkit_bs_list = []
for landmark, frame in zip(new_landmarks, new_frames):
drive_rot, drive_trans = h3dmm.forward_params(frame, landmark)
rot_list.append(drive_rot)
trans_list.append(drive_trans)
arkit_bs_list.append(harkit_bs.forward(frame, landmark))
drive_coeff = torch.from_numpy(np.stack(arkit_bs_list, axis=0))
return face_parts, drive_coeff, rot_list, trans_list
def face_params_to_tensor(clip_encoder, h3dmm, face_parts, drive_rot, drive_trans, ref_trans, save_size=512, trans_ratio=0.0):
face_parts_list = []
for tmp_face_parts_list in face_parts:
tmp_face_parts_list = [clip_preprocess_from_bgr(x).unsqueeze(1) for x in tmp_face_parts_list]
face_parts_list.append(torch.cat(tmp_face_parts_list, dim=1))
face_parts = torch.stack(face_parts_list, dim=2)
face_parts_tensor = rearrange(face_parts, "c f p h w -> (f p) c h w")
face_parts_embedding = clip_encoder(face_parts_tensor.to(device=clip_encoder.device, dtype=clip_encoder.dtype)).image_embeds
face_parts_embedding = rearrange(face_parts_embedding,
"(f p) c -> f p c",
f=face_parts.size(2)).cpu()
control_list = []
control_show_list = []
for rot, trans in zip(drive_rot, drive_trans):
rect = h3dmm.get_project_points_rect(rot, ref_trans + (trans - drive_trans[0]) * trans_ratio, save_size, save_size)
control_heatmap = draw_skl_by_rect(save_size, rect)
control_show_list.append(control_heatmap.copy())
control_heatmap = image_preprocess(control_heatmap, (save_size, save_size))
control_list.append(control_heatmap)
control_heatmaps = torch.stack(control_list, dim=1)
return face_parts_embedding, control_heatmaps
def det_landmarks(face_aligner, frame_list, save_size=(512, 512), reset=False):
rect_list = []
new_frame_list = []
for frame in frame_list:
frame = cv2.resize(frame, save_size)
faces = face_aligner.forward(frame, reset=reset)
if len(faces) > 0:
face = sorted(faces, key=lambda x: (x['face_rect'][2] - x['face_rect'][0]) * (
x['face_rect'][3] - x['face_rect'][1]))[-1]
rect_list.append(face['face_rect'])
new_frame_list.append(frame)
face_aligner.reset_track()
save_frame_list = []
save_landmark_list = []
for frame, rect in zip(new_frame_list, rect_list):
faces = face_aligner.forward(frame, pre_rect=rect, reset=reset)
if len(faces) > 0:
face = sorted(faces, key=lambda x: (x['face_rect'][2] - x['face_rect'][0]) * (
x['face_rect'][3] - x['face_rect'][1]))[-1]
landmarks = face['pre_kpt_222']
save_frame_list.append(frame)
save_landmark_list.append(landmarks)
save_landmark_list = np.stack(save_landmark_list, axis=0).astype(np.float16)
face_aligner.reset_track()
return save_frame_list, save_landmark_list
def get_drive_params(face_aligner, h3dmm, harkit_bs, frame_list, save_size, align):
frame_num = len(frame_list)
frame_list, landmark_list = det_landmarks(face_aligner, frame_list, save_size=(512, 512), reset=False)
assert len(frame_list) == frame_num
return get_face_params(h3dmm, harkit_bs, frame_list, landmark_list, save_size=save_size, align=align)
def load_data_list(data_dir, post_fix='.pickle;.txt'):
post_fixs = post_fix.split(';')
ret_list = []
for root, dirnames, filenames in os.walk(data_dir):
for name in filenames:
if os.path.splitext(name)[1].lower() in post_fixs and not name.startswith('.'):
data_path = os.path.join(root, name)
ret_list.append(data_path)
return ret_list
def crop_and_resize(frames, landmarks, save_size=512, crop=True):
H, W = frames[0].shape[:2]
if crop:
all_tl, all_br = np.min(landmarks, axis=1), np.max(landmarks, axis=1)
mean_wh = np.mean(all_br - all_tl, axis=0)
tl, br = np.min(all_tl, axis=0), np.max(all_br, axis=0)
new_size = min(max(mean_wh) * 2.2, min(H, W) - 1)
fcenter = (tl + br) * 0.5
ftl = fcenter - new_size * 0.5
ftl[1] -= mean_wh[1] * 0.2
fbr = fcenter + new_size * 0.5
fbr[1] -= mean_wh[1] * 0.2
if ftl[0] < 0:
fbr[0] -= ftl[0]
ftl[0] = 0
if ftl[1] < 0:
fbr[1] -= ftl[1]
ftl[1] = 0
if fbr[0] >= W:
ftl[0] -= fbr[0] - W + 1
fbr[0] = W - 1
if fbr[1] >= H:
ftl[1] -= fbr[1] - H + 1
fbr[1] = H - 1
ftl = ftl.astype(int)
fbr = (fbr - ftl)[0].astype(int) + ftl
frames = frames[:, ftl[1]:fbr[1], ftl[0]:fbr[0], :].copy()
landmarks = landmarks - ftl
ratio = save_size / (fbr - ftl)
else:
ratio = (save_size / W, save_size / H)
landmarks = landmarks * ratio
frames = np.stack([cv2.resize(frame, (save_size, save_size), interpolation=cv2.INTER_CUBIC) for frame in frames], axis=0)
return frames, landmarks
def umeyama(src, dst, estimate_scale):
"""Estimate N-D similarity transformation with or without scaling.
Parameters
----------
src : (M, N) array
Source coordinates.
dst : (M, N) array
Destination coordinates.
estimate_scale : bool
Whether to estimate scaling factor.
Returns
-------
T : (N + 1, N + 1)
The homogeneous similarity transformation matrix. The matrix contains
NaN values only if the problem is not well-conditioned.
References
----------
.. [1] "Least-squares estimation of transformation parameters between two
point patterns", Shinji Umeyama, PAMI 1991, DOI: 10.1109/34.88573
"""
num = src.shape[0]
dim = src.shape[1]
# Compute mean of src and dst.
src_mean = src.mean(axis=0)
dst_mean = dst.mean(axis=0)
# Subtract mean from src and dst.
src_demean = src - src_mean
dst_demean = dst - dst_mean
# Eq. (38).
A = np.dot(dst_demean.T, src_demean) / num
# Eq. (39).
d = np.ones((dim,), dtype=np.double)
if np.linalg.det(A) < 0:
d[dim - 1] = -1
T = np.eye(dim + 1, dtype=np.double)
U, S, V = np.linalg.svd(A)
# Eq. (40) and (43).
rank = np.linalg.matrix_rank(A)
if rank == 0:
return np.nan * T
elif rank == dim - 1:
if np.linalg.det(U) * np.linalg.det(V) > 0:
T[:dim, :dim] = np.dot(U, V)
else:
s = d[dim - 1]
d[dim - 1] = -1
T[:dim, :dim] = np.dot(U, np.dot(np.diag(d), V))
d[dim - 1] = s
else:
T[:dim, :dim] = np.dot(U, np.dot(np.diag(d), V.T))
if estimate_scale:
# Eq. (41) and (42).
scale = 1.0 / src_demean.var(axis=0).sum() * np.dot(S, d)
else:
scale = 1.0
T[:dim, dim] = dst_mean - scale * np.dot(T[:dim, :dim], src_mean.T)
T[:dim, :dim] *= scale
return T
def ff_cat_video_and_audio(video_path, audio_path, output_path):
cmd = [
"ffmpeg",
"-y",
"-v", "quiet", "-stats",
"-i", video_path,
"-i", audio_path,
"-c:v", "copy",
"-map", "0:v:0",
"-map", "1:a:0",
"-shortest",
output_path
]
subprocess.run(cmd)
def ff_change_fps(input_video, output_video, fps=15):
cmd = [
"ffmpeg",
"-y",
"-v", "quiet", "-stats",
# "-hwaccel", "cuda",
"-i", input_video,
"-c:v", "libx264",
"-r", f"{fps}",
"-c:a", "copy",
"-crf", "18",
output_video
]
subprocess.run(cmd)
+368
View File
@@ -0,0 +1,368 @@
import os
import os.path as osp
import folder_paths
import torch
import numpy as np
import cv2
import sys
import copy
from PIL import Image
import subprocess
import importlib.metadata
cur_dir = osp.dirname(osp.abspath(__file__))
installed_packages = [package.name for package in importlib.metadata.distributions()]
REQUIRED = {
'diffusers', 'transformers', 'einops', 'opencv-python', 'tqdm', 'pillow', 'onnxruntime', 'onnx', 'safetensors', 'accelerate', 'peft'
}
missing = [name for name in REQUIRED if name not in installed_packages]
print("missing pkgs", missing)
if missing:
python = sys.executable
subprocess.check_call([python, '-m', 'pip', 'install', *missing], stdout=subprocess.DEVNULL)
from .hellomeme.utils import (face_params_to_tensor,
get_drive_params,
get_face_params,
load_data_list,
load_unet_from_safetensors)
from .hellomeme.tools import Hello3DMMPred, HelloARKitBSPred, HelloFaceAlignment, HelloCameraDemo
from .hellomeme import HMImagePipeline, HMVideoPipeline
from transformers import CLIPVisionModelWithProjection
class HMImagePipelineLoader:
@classmethod
def INPUT_TYPES(s):
checkpoint_files = sorted(load_data_list(osp.join(cur_dir, '../../models/checkpoints'), '.pt;.pth;.ckpt;.safetensors'))
lora_files = sorted(load_data_list(osp.join(cur_dir, '../../models/loras'), '.safetensors'))
return {
"optional": {
"checkpoint_path": (['None'] + checkpoint_files, ),
"lora_path": (['None'] + lora_files, ),
"gpu_id": ("INT", {"default": 0}),
}
}
RETURN_TYPES = ("HMIMAGEPIPELINE", )
FUNCTION = "load_pipeline"
CATEGORY = "hellomeme"
def load_pipeline(self, checkpoint_path=None, lora_path=None, gpu_id=0):
dtype = torch.float16
if gpu_id >= 0:
device = torch.device("cuda:{}".format(gpu_id))
else:
device = torch.device("cpu")
pipeline = HMImagePipeline.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5").to(dtype=dtype, device=device)
pipeline.caryomitosis()
### load customized checkpoint or lora here:
## checkpoints
if checkpoint_path and osp.isfile(checkpoint_path):
if checkpoint_path.endswith('.safetensors'):
state_dict = load_unet_from_safetensors(checkpoint_path, pipeline.unet_ref.config)
pipeline.unet.load_state_dict(state_dict, strict=False)
elif osp.splitext(checkpoint_path)[-1] in ['.pt', '.pth', '.ckpt']:
state_dict = torch.load(checkpoint_path)
pipeline.unet.load_state_dict(state_dict, strict=False)
else:
print("Invalid checkpoint path", checkpoint_path)
### lora
if lora_path and osp.isfile(lora_path):
pipeline.load_lora_weights(osp.dirname(lora_path), weight_name=osp.basename(lora_path), adapter_name="lora")
pipeline.insert_hm_modules(dtype=dtype, device=device)
return (pipeline, )
class HMVideoPipelineLoader:
@classmethod
def INPUT_TYPES(s):
checkpoint_files = sorted(load_data_list(osp.join(cur_dir, '../../models/checkpoints'), '.pt;.pth;.ckpt;.safetensors'))
lora_files = sorted(load_data_list(osp.join(cur_dir, '../../models/loras'), '.safetensors'))
return {
"optional": {
"checkpoint_path": (['None'] + checkpoint_files, ),
"lora_path": (['None'] + lora_files, ),
"gpu_id": ("INT", {"default": 0}),
}
}
RETURN_TYPES = ("HMVIDEOPIPELINE",)
FUNCTION = "load_pipeline"
CATEGORY = "hellomeme"
def load_pipeline(self, checkpoint_path=None, lora_path=None, gpu_id=0):
dtype = torch.float16
if gpu_id >= 0:
device = torch.device("cuda:{}".format(gpu_id))
else:
device = torch.device("cpu")
pipeline = HMVideoPipeline.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5").to(dtype=dtype,
device=device)
pipeline.caryomitosis()
### load customized checkpoint or lora here:
## checkpoints
if checkpoint_path and osp.isfile(checkpoint_path):
if checkpoint_path.endswith('.safetensors'):
state_dict = load_unet_from_safetensors(checkpoint_path, pipeline.unet_ref.config)
pipeline.unet.load_state_dict(state_dict, strict=False)
elif osp.splitext(checkpoint_path)[-1] in ['.pt', '.pth', '.ckpt']:
state_dict = torch.load(checkpoint_path)
pipeline.unet.load_state_dict(state_dict, strict=False)
else:
print("Invalid checkpoint path", checkpoint_path)
### lora
if lora_path and osp.isfile(lora_path):
pipeline.load_lora_weights(osp.dirname(lora_path), weight_name=osp.basename(lora_path), adapter_name="lora")
pipeline.insert_hm_modules(dtype=dtype, device=device)
return (pipeline,)
class HMFaceToolkitsLoader:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"gpu_id": ("INT", {"default": 0}),
}
}
RETURN_TYPES = ("FACE_TOOLKITS",)
FUNCTION = "load_face_toolkits"
CATEGORY = "hellomeme"
def load_face_toolkits(self, gpu_id):
dtype = torch.float16
device = torch.device(f'cuda:{gpu_id}')
face_aligner = HelloCameraDemo(face_alignment_module=HelloFaceAlignment(gpu_id=gpu_id), reset=True)
harkit_bs = HelloARKitBSPred(gpu_id=gpu_id)
h3dmm = Hello3DMMPred(gpu_id=gpu_id)
clip_image_encoder = CLIPVisionModelWithProjection.from_pretrained(
'h94/IP-Adapter',subfolder='models/image_encoder').to(dtype=dtype, device=device)
return (dict(face_aligner=face_aligner, harkit_bs=harkit_bs, h3dmm=h3dmm, image_encoder=clip_image_encoder), )
class GetReferenceImageRT:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"face_toolkits": ("FACE_TOOLKITS",),
"image": ("IMAGE",),
}
}
RETURN_TYPES = ("REFRT",)
FUNCTION = "get_reference_image_rt"
CATEGORY = "hellomeme"
def get_reference_image_rt(self, face_toolkits, image):
image_np = cv2.cvtColor((image[0] * 255).cpu().numpy().astype(np.uint8), cv2.COLOR_BGR2RGB)
image_np = cv2.resize(image_np, (512, 512))
print(image_np.shape)
face_toolkits['face_aligner'].reset_track()
faces = face_toolkits['face_aligner'].forward(image_np)
assert len(faces) > 0
face = sorted(faces, key=lambda x: (x['face_rect'][2] - x['face_rect'][0]) * (
x['face_rect'][3] - x['face_rect'][1]))[-1]
ref_landmark = face['pre_kpt_222']
ref_rot, ref_trans = face_toolkits['h3dmm'].forward_params(image_np, ref_landmark)
return (dict(rot=ref_rot, trans=ref_trans), )
class GetImageDriveParams:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"face_toolkits": ("FACE_TOOLKITS",),
"image": ("IMAGE",),
"ref_rt": ("REFRT",),
"gpu_id": ("INT", {"default": 0})
}
}
RETURN_TYPES = ("DRIVE_IMAGE_PARAMS",)
RETURN_NAMES = ("drive_image_params",)
FUNCTION = "get_face_params"
CATEGORY = "hellomeme"
def get_face_params(self, face_toolkits, image, ref_rt, gpu_id):
dtype = torch.float16
device = torch.device(f'cuda:{gpu_id}')
image_np = cv2.cvtColor((image[0] * 255).cpu().numpy().astype(np.uint8), cv2.COLOR_BGR2RGB)
face_toolkits['face_aligner'].reset_track()
faces = face_toolkits['face_aligner'].forward(image_np)
assert len(faces) > 0
face = sorted(faces, key=lambda x: (x['face_rect'][2] - x['face_rect'][0]) * (
x['face_rect'][3] - x['face_rect'][1]))[-1]
drive_landmark = face['pre_kpt_222']
drive_face_parts, drive_coeff, drive_rot, drive_trans = get_face_params(face_toolkits['h3dmm'],
face_toolkits['harkit_bs'],
[image_np],
[drive_landmark],
save_size=(512, 512),
align=False)
face_parts_embedding, control_heatmaps = face_params_to_tensor(face_toolkits['image_encoder'], face_toolkits['h3dmm'],
drive_face_parts,
drive_rot, drive_trans, ref_rt['trans'],
save_size=512, trans_ratio=0.0)
drive_params = dict(
face_parts=face_parts_embedding.unsqueeze(0).to(dtype=dtype, device=device),
drive_coeff=drive_coeff.unsqueeze(0).to(dtype=dtype, device=device),
condition=control_heatmaps.unsqueeze(0).to(dtype=dtype, device=device),
)
return (drive_params, )
class HMPipelineImage:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"pipeline": ("HMIMAGEPIPELINE",),
"image": ("IMAGE",),
"drive_image_params": ("DRIVE_IMAGE_PARAMS",),
"prompt": ("STRING", {"default": '(best quality), highly detailed, ultra-detailed, headshot, person, well-placed five sense organs, looking at the viewer, centered composition, sharp focus, realistic skin texture'}),
"negative_prompt": ("STRING", {"default": ''}),
"steps": ("INT", {"default": 25, "min": 1, "max": 10000}),
"guidance_scale": ("FLOAT", {"default": 2.0, "min": 0.0, "max": 100.0, "step": 0.01}),
}
}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "sample"
CATEGORY = "hellomeme"
def sample(self, pipeline, image, drive_image_params, prompt, negative_prompt, steps=25, guidance_scale=2.0):
image_np = (image[0] * 255).cpu().numpy().astype(np.uint8)
image_np = cv2.resize(image_np, (512, 512))
image_pil = Image.fromarray(image_np)
result_img = pipeline(
prompt=[prompt],
strength=1.0,
image=image_pil,
drive_params=copy.deepcopy(drive_image_params),
num_inference_steps=steps,
negative_prompt=[negative_prompt],
guidance_scale=guidance_scale,
output_type='np'
)
return (torch.from_numpy(np.clip(result_img[0], 0, 1)), )
class GetVideoDriveParams:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"face_toolkits": ("FACE_TOOLKITS",),
"images": ("IMAGE",),
"ref_rt": ("REFRT",),
"trans_ratio": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.01}),
}
}
RETURN_TYPES = ("DRIVE_VIDEO_PARAMS",)
RETURN_NAMES = ("drive_video_params",)
FUNCTION = "get_face_params"
CATEGORY = "hellomeme"
def get_face_params(self, face_toolkits, images, ref_rt, trans_ratio):
dtype = torch.float16
frame_list = [cv2.cvtColor((frame * 255).cpu().numpy().astype(np.uint8), cv2.COLOR_BGR2RGB) for frame in images]
face_toolkits['face_aligner'].reset_track()
(drive_face_parts, drive_coeff, drive_rot, drive_trans) = get_drive_params(face_toolkits['face_aligner'],
face_toolkits['h3dmm'],
face_toolkits['harkit_bs'],
frame_list=frame_list,
save_size=512,
align=True)
face_toolkits['face_aligner'].reset_track()
face_parts_embedding, control_heatmaps = face_params_to_tensor(
face_toolkits['image_encoder'], face_toolkits['h3dmm'],
drive_face_parts,
drive_rot, drive_trans, ref_rt['trans'],
save_size=512, trans_ratio=trans_ratio)
drive_params = dict(
face_parts=face_parts_embedding.unsqueeze(0).to(dtype=dtype),
drive_coeff=drive_coeff.unsqueeze(0).to(dtype=dtype),
condition=control_heatmaps.unsqueeze(0).to(dtype=dtype),
)
return (drive_params, )
class HMPipelineVideo:
@classmethod
def INPUT_TYPES(s):
return {"required":
{
"pipeline": ("HMVIDEOPIPELINE",),
"image": ("IMAGE",),
"drive_video_params": ("DRIVE_VIDEO_PARAMS",),
"prompt": ("STRING", {"default": '(best quality), highly detailed, ultra-detailed, headshot, person, well-placed five sense organs, looking at the viewer, centered composition, sharp focus, realistic skin texture'}),
"negative_prompt": ("STRING", {"default": ''}),
"steps": ("INT", {"default": 25, "min": 1, "max": 10000}),
"guidance_scale": ("FLOAT", {"default": 2.0, "min": 0.0, "max": 100.0, "step": 0.01}),
}
}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "sample"
CATEGORY = "hellomeme"
def sample(self, pipeline, image, drive_video_params, prompt, negative_prompt, steps=25, guidance_scale=2.0):
image_np = (image[0] * 255).cpu().numpy().astype(np.uint8)
image_np = cv2.resize(image_np, (512, 512))
image_pil = Image.fromarray(image_np)
res_frames = pipeline(
prompt=[prompt],
strength=1.0,
image=image_pil,
drive_params=copy.deepcopy(drive_video_params),
num_inference_steps=steps,
negative_prompt=[negative_prompt],
guidance_scale=guidance_scale,
output_type='np'
)
res_frames = [np.clip(x[0], 0, 1) for x in res_frames]
return (torch.from_numpy(np.array(res_frames)), )
NODE_CLASS_MAPPINGS = {
"HMImagePipelineLoader": HMImagePipelineLoader,
"HMVideoPipelineLoader": HMVideoPipelineLoader,
"HMFaceToolkitsLoader": HMFaceToolkitsLoader,
"GetReferenceImageRT": GetReferenceImageRT,
"GetImageDriveParams": GetImageDriveParams,
"GetVideoDriveParams": GetVideoDriveParams,
"HMPipelineImage": HMPipelineImage,
"HMPipelineVideo": HMPipelineVideo,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"HMImagePipelineLoader": "Load HelloMemeImage Pipeline",
"HMVideoPipelineLoader": "Load HelloMemeVideo Pipeline",
"HMFaceToolkitsLoader": "Load Face Toolkits",
"GetReferenceImageT": "Get Reference Image Translation",
"GetImageDriveParams": "Get Drive Image Parameters",
"GetVideoDriveParams": "Get Drive Video Parameters",
"HMPipelineImage": "HelloMeme Image Pipeline",
"HMPipelineVideo": "HelloMeme Video Pipeline",
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 313 KiB

+403
View File
@@ -0,0 +1,403 @@
{
"last_node_id": 14,
"last_link_id": 18,
"nodes": [
{
"id": 11,
"type": "GetReferenceImageRT",
"pos": {
"0": 621,
"1": 262
},
"size": {
"0": 241.79998779296875,
"1": 46
},
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "face_toolkits",
"type": "FACE_TOOLKITS",
"link": 13
},
{
"name": "image",
"type": "IMAGE",
"link": 12
}
],
"outputs": [
{
"name": "REFRT",
"type": "REFRT",
"links": [
17
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "GetReferenceImageRT"
},
"widgets_values": []
},
{
"id": 14,
"type": "GetImageDriveParams",
"pos": {
"0": 987,
"1": 226
},
"size": {
"0": 330,
"1": 98
},
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "face_toolkits",
"type": "FACE_TOOLKITS",
"link": 16
},
{
"name": "image",
"type": "IMAGE",
"link": 18
},
{
"name": "ref_rt",
"type": "REFRT",
"link": 17
}
],
"outputs": [
{
"name": "drive_image_params",
"type": "DRIVE_IMAGE_PARAMS",
"links": [
15
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "GetImageDriveParams"
},
"widgets_values": [
0
]
},
{
"id": 6,
"type": "HMPipelineImage",
"pos": {
"0": 1325,
"1": 654
},
"size": {
"0": 315,
"1": 170
},
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "pipeline",
"type": "HMIMAGEPIPELINE",
"link": 7
},
{
"name": "image",
"type": "IMAGE",
"link": 9
},
{
"name": "drive_image_params",
"type": "DRIVE_IMAGE_PARAMS",
"link": 15
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
11
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "HMPipelineImage"
},
"widgets_values": [
"(best quality), highly detailed, ultra-detailed, headshot, person, well-placed five sense organs, looking at the viewer, centered composition, sharp focus, realistic skin texture",
"",
25,
2
]
},
{
"id": 10,
"type": "PreviewImage",
"pos": {
"0": 1695,
"1": 652
},
"size": {
"0": 210,
"1": 246
},
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 11
}
],
"outputs": [],
"properties": {
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 2,
"type": "HMFaceToolkitsLoader",
"pos": {
"0": 211,
"1": 180
},
"size": {
"0": 315,
"1": 58
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "FACE_TOOLKITS",
"type": "FACE_TOOLKITS",
"links": [
13,
16
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "HMFaceToolkitsLoader"
},
"widgets_values": [
0
]
},
{
"id": 1,
"type": "HMImagePipelineLoader",
"pos": {
"0": 226,
"1": 356
},
"size": {
"0": 315,
"1": 106
},
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "HMIMAGEPIPELINE",
"type": "HMIMAGEPIPELINE",
"links": [
7
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "HMImagePipelineLoader"
},
"widgets_values": [
"None",
"None",
0
]
},
{
"id": 8,
"type": "LoadImage",
"pos": {
"0": 233,
"1": 536
},
"size": {
"0": 315,
"1": 314
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
9,
12
],
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"majicmix2.jpg",
"image"
]
},
{
"id": 9,
"type": "LoadImage",
"pos": {
"0": 227,
"1": 915
},
"size": {
"0": 315,
"1": 314
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
18
],
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"yao.jpg",
"image"
]
}
],
"links": [
[
7,
1,
0,
6,
0,
"HMIMAGEPIPELINE"
],
[
9,
8,
0,
6,
1,
"IMAGE"
],
[
11,
6,
0,
10,
0,
"IMAGE"
],
[
12,
8,
0,
11,
1,
"IMAGE"
],
[
13,
2,
0,
11,
0,
"FACE_TOOLKITS"
],
[
15,
14,
0,
6,
2,
"DRIVE_PARAMS"
],
[
16,
2,
0,
14,
0,
"FACE_TOOLKITS"
],
[
17,
11,
0,
14,
2,
"REFRT"
],
[
18,
9,
0,
14,
1,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
150,
-42.66668701171875
]
}
},
"version": 0.4
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

+502
View File
@@ -0,0 +1,502 @@
{
"last_node_id": 24,
"last_link_id": 30,
"nodes": [
{
"id": 15,
"type": "HMPipelineVideo",
"pos": {
"0": 1408,
"1": 390
},
"size": {
"0": 315,
"1": 170
},
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "pipeline",
"type": "HMVIDEOPIPELINE",
"link": 16
},
{
"name": "image",
"type": "IMAGE",
"link": 17
},
{
"name": "drive_video_params",
"type": "DRIVE_VIDEO_PARAMS",
"link": 29
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
24
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "HMPipelineVideo"
},
"widgets_values": [
"(best quality), highly detailed, ultra-detailed, headshot, person, well-placed five sense organs, looking at the viewer, centered composition, sharp focus, realistic skin texture",
"",
25,
2
]
},
{
"id": 2,
"type": "HMFaceToolkitsLoader",
"pos": {
"0": 124,
"1": 276
},
"size": {
"0": 315,
"1": 58
},
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "FACE_TOOLKITS",
"type": "FACE_TOOLKITS",
"links": [
13,
27
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "HMFaceToolkitsLoader"
},
"widgets_values": [
0
]
},
{
"id": 11,
"type": "GetReferenceImageRT",
"pos": {
"0": 536,
"1": 516
},
"size": {
"0": 241.79998779296875,
"1": 46
},
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "face_toolkits",
"type": "FACE_TOOLKITS",
"link": 13
},
{
"name": "image",
"type": "IMAGE",
"link": 12
}
],
"outputs": [
{
"name": "REFRT",
"type": "REFRT",
"links": [
26
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "GetReferenceImageRT"
},
"widgets_values": []
},
{
"id": 21,
"type": "VHS_VideoCombine",
"pos": {
"0": 1927,
"1": 604
},
"size": [
214.7587890625,
518.7587890625
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 24
},
{
"name": "audio",
"type": "AUDIO",
"link": 25,
"shape": 7
},
{
"name": "meta_batch",
"type": "VHS_BatchManager",
"link": null,
"shape": 7
},
{
"name": "vae",
"type": "VAE",
"link": null,
"shape": 7
}
],
"outputs": [
{
"name": "Filenames",
"type": "VHS_FILENAMES",
"links": []
}
],
"properties": {
"Node name for S&R": "VHS_VideoCombine"
},
"widgets_values": {
"frame_rate": 15,
"loop_count": 0,
"filename_prefix": "HelloMeme",
"format": "video/h264-mp4",
"pix_fmt": "yuv420p",
"crf": 19,
"save_metadata": true,
"pingpong": false,
"save_output": true,
"videopreview": {
"hidden": false,
"paused": false,
"params": {
"filename": "HelloMeme_00001-audio.mp4",
"subfolder": "",
"type": "output",
"format": "video/h264-mp4",
"frame_rate": 15
},
"muted": false
}
}
},
{
"id": 22,
"type": "GetVideoDriveParams",
"pos": {
"0": 932,
"1": 503
},
"size": {
"0": 330,
"1": 98
},
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "face_toolkits",
"type": "FACE_TOOLKITS",
"link": 27
},
{
"name": "images",
"type": "IMAGE",
"link": 28
},
{
"name": "ref_rt",
"type": "REFRT",
"link": 26
}
],
"outputs": [
{
"name": "drive_video_params",
"type": "DRIVE_VIDEO_PARAMS",
"links": [
29
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "GetVideoDriveParams"
},
"widgets_values": [
0
]
},
{
"id": 14,
"type": "HMVideoPipelineLoader",
"pos": {
"0": 116,
"1": 105
},
"size": {
"0": 352.79998779296875,
"1": 106
},
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "HMVIDEOPIPELINE",
"type": "HMVIDEOPIPELINE",
"links": [
16
],
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "HMVideoPipelineLoader"
},
"widgets_values": [
"None",
"None",
0
]
},
{
"id": 8,
"type": "LoadImage",
"pos": {
"0": 120,
"1": 414
},
"size": {
"0": 315,
"1": 314
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
12,
17
],
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"majicmix2.jpg",
"image"
]
},
{
"id": 20,
"type": "VHS_LoadVideoFFmpeg",
"pos": {
"0": 139,
"1": 793
},
"size": [
303.46875,
535.46875
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "meta_batch",
"type": "VHS_BatchManager",
"link": null,
"shape": 7
},
{
"name": "vae",
"type": "VAE",
"link": null,
"shape": 7
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
28
],
"slot_index": 0
},
{
"name": "mask",
"type": "MASK",
"links": null
},
{
"name": "audio",
"type": "AUDIO",
"links": [
25
],
"slot_index": 2
},
{
"name": "video_info",
"type": "VHS_VIDEOINFO",
"links": null
}
],
"properties": {
"Node name for S&R": "VHS_LoadVideoFFmpeg"
},
"widgets_values": {
"video": "jgz.mp4",
"force_rate": 15,
"force_size": "Disabled",
"custom_width": 512,
"custom_height": 512,
"frame_load_cap": 0,
"start_time": 0,
"choose video to upload": "image",
"videopreview": {
"hidden": false,
"paused": false,
"params": {
"force_rate": 15,
"frame_load_cap": 0,
"start_time": 0,
"filename": "jgz.mp4",
"type": "input",
"format": "video/mp4"
},
"muted": false
}
}
}
],
"links": [
[
12,
8,
0,
11,
1,
"IMAGE"
],
[
13,
2,
0,
11,
0,
"FACE_TOOLKITS"
],
[
16,
14,
0,
15,
0,
"HMVIDEOPIPELINE"
],
[
17,
8,
0,
15,
1,
"IMAGE"
],
[
24,
15,
0,
21,
0,
"IMAGE"
],
[
25,
20,
2,
21,
1,
"AUDIO"
],
[
26,
11,
0,
22,
2,
"REFRT"
],
[
27,
2,
0,
22,
0,
"FACE_TOOLKITS"
],
[
28,
20,
0,
22,
1,
"IMAGE"
],
[
29,
22,
0,
15,
2,
"DRIVE_PARAMS"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1,
"offset": [
0,
0
]
}
},
"version": 0.4
}