first commit

This commit is contained in:
AIFSH
2024-06-17 04:48:02 +00:00
parent 1c76af919b
commit 2fab275b0f
57 changed files with 11700 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
*.pkl
*.pt
*.mov
*.pth
*.mov
*.npz
*.npy
*.boj
*.onnx
*.tar
*.bin
cache*
.DS_Store
*DS_Store
outputs/
**/__pycache__
***/__pycache__
*/__pycache__
checkpoints
+98
View File
@@ -0,0 +1,98 @@
# manual setting
max_frames: 32
resolution: [512, 768] # or resolution: [768, 1216]
# resolution: [768, 1216]
round: 1
ddim_timesteps: 30 # among 25-50
seed: 11 # 7
test_list_path: [
# Format: [frame_interval, reference image, driving pose sequence]
[2, "data/images/WOMEN-Blouses_Shirts-id_00004955-01_4_full.jpg", "data/saved_pose/WOMEN-Blouses_Shirts-id_00004955-01_4_full"],
[2, "data/images/musk.jpg", "data/saved_pose/musk"],
[2, "data/images/WOMEN-Blouses_Shirts-id_00005125-03_4_full.jpg", "data/saved_pose/WOMEN-Blouses_Shirts-id_00005125-03_4_full"],
[2, "data/images/IMG_20240514_104337.jpg", "data/saved_pose/IMG_20240514_104337"]
]
partial_keys: [
['image','local_image', "dwpose"], # reference image as the first frame of the generated video (optional)
['image', 'randomref', "dwpose"],
]
# default settings
TASK_TYPE: inference_unianimate_entrance
use_fp16: True
guide_scale: 2.5
vit_resolution: [224, 224]
use_fp16: True
batch_size: 1
latent_random_ref: True
chunk_size: 2
decoder_bs: 2
scale: 8
use_fps_condition: False
test_model: checkpoints/unianimate_16f_32f_non_ema_223000.pth
embedder: {
'type': 'FrozenOpenCLIPTextVisualEmbedder',
'layer': 'penultimate',
'pretrained': 'checkpoints/open_clip_pytorch_model.bin'
}
auto_encoder: {
'type': 'AutoencoderKL',
'ddconfig': {
'double_z': True,
'z_channels': 4,
'resolution': 256,
'in_channels': 3,
'out_ch': 3,
'ch': 128,
'ch_mult': [1, 2, 4, 4],
'num_res_blocks': 2,
'attn_resolutions': [],
'dropout': 0.0,
'video_kernel_size': [3, 1, 1]
},
'embed_dim': 4,
'pretrained': 'checkpoints/v2-1_512-ema-pruned.ckpt'
}
UNet: {
'type': 'UNetSD_UniAnimate',
'config': None,
'in_dim': 4,
'dim': 320,
'y_dim': 1024,
'context_dim': 1024,
'out_dim': 4,
'dim_mult': [1, 2, 4, 4],
'num_heads': 8,
'head_dim': 64,
'num_res_blocks': 2,
'dropout': 0.1,
'temporal_attention': True,
'num_tokens': 4,
'temporal_attn_times': 1,
'use_checkpoint': True,
'use_fps_condition': False,
'use_sim_mask': False
}
video_compositions: ['image', 'local_image', 'dwpose', 'randomref', 'randomref_pose']
Diffusion: {
'type': 'DiffusionDDIM',
'schedule': 'linear_sd',
'schedule_param': {
'num_timesteps': 1000,
"init_beta": 0.00085,
"last_beta": 0.0120,
'zero_terminal_snr': True,
},
'mean_type': 'v',
'loss_type': 'mse',
'var_type': 'fixed_small', # 'fixed_large',
'rescale_timesteps': False,
'noise_strength': 0.1
}
use_DiffusionDPM: False
CPU_CLIP_VAE: True
@@ -0,0 +1,100 @@
# manual setting
# resolution: [512, 768] # or [768, 1216]
resolution: [768, 1216]
round: 1
ddim_timesteps: 30 # among 25-50
context_size: 32
context_stride: 1
context_overlap: 8
seed: 7
max_frames: "None" # 64, 96, "None" mean the length of original pose sequence
test_list_path: [
# Format: [frame_interval, reference image, driving pose sequence]
[2, "data/images/WOMEN-Blouses_Shirts-id_00004955-01_4_full.jpg", "data/saved_pose/WOMEN-Blouses_Shirts-id_00004955-01_4_full"],
[2, "data/images/musk.jpg", "data/saved_pose/musk"],
[2, "data/images/WOMEN-Blouses_Shirts-id_00005125-03_4_full.jpg", "data/saved_pose/WOMEN-Blouses_Shirts-id_00005125-03_4_full"],
[2, "data/images/IMG_20240514_104337.jpg", "data/saved_pose/IMG_20240514_104337"],
[2, "data/images/IMG_20240514_104337.jpg", "data/saved_pose/IMG_20240514_104337_dance"],
[2, "data/images/WOMEN-Blouses_Shirts-id_00005125-03_4_full.jpg", "data/saved_pose/WOMEN-Blouses_Shirts-id_00005125-03_4_full_dance"]
]
# default settings
TASK_TYPE: inference_unianimate_long_entrance
use_fp16: True
guide_scale: 2.5
vit_resolution: [224, 224]
use_fp16: True
batch_size: 1
latent_random_ref: True
chunk_size: 2
decoder_bs: 2
scale: 8
use_fps_condition: False
test_model: checkpoints/unianimate_16f_32f_non_ema_223000.pth
partial_keys: [
['image', 'randomref', "dwpose"],
]
embedder: {
'type': 'FrozenOpenCLIPTextVisualEmbedder',
'layer': 'penultimate',
'pretrained': 'checkpoints/open_clip_pytorch_model.bin'
}
auto_encoder: {
'type': 'AutoencoderKL',
'ddconfig': {
'double_z': True,
'z_channels': 4,
'resolution': 256,
'in_channels': 3,
'out_ch': 3,
'ch': 128,
'ch_mult': [1, 2, 4, 4],
'num_res_blocks': 2,
'attn_resolutions': [],
'dropout': 0.0,
'video_kernel_size': [3, 1, 1]
},
'embed_dim': 4,
'pretrained': 'checkpoints/v2-1_512-ema-pruned.ckpt'
}
UNet: {
'type': 'UNetSD_UniAnimate',
'config': None,
'in_dim': 4,
'dim': 320,
'y_dim': 1024,
'context_dim': 1024,
'out_dim': 4,
'dim_mult': [1, 2, 4, 4],
'num_heads': 8,
'head_dim': 64,
'num_res_blocks': 2,
'dropout': 0.1,
'temporal_attention': True,
'num_tokens': 4,
'temporal_attn_times': 1,
'use_checkpoint': True,
'use_fps_condition': False,
'use_sim_mask': False
}
video_compositions: ['image', 'local_image', 'dwpose', 'randomref', 'randomref_pose']
Diffusion: {
'type': 'DiffusionDDIMLong',
'schedule': 'linear_sd',
'schedule_param': {
'num_timesteps': 1000,
"init_beta": 0.00085,
"last_beta": 0.0120,
'zero_terminal_snr': True,
},
'mean_type': 'v',
'loss_type': 'mse',
'var_type': 'fixed_small',
'rescale_timesteps': False,
'noise_strength': 0.1
}
CPU_CLIP_VAE: True
View File
+127
View File
@@ -0,0 +1,127 @@
import cv2
import numpy as np
import onnxruntime
def nms(boxes, scores, nms_thr):
"""Single class NMS implemented in Numpy."""
x1 = boxes[:, 0]
y1 = boxes[:, 1]
x2 = boxes[:, 2]
y2 = boxes[:, 3]
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
order = scores.argsort()[::-1]
keep = []
while order.size > 0:
i = order[0]
keep.append(i)
xx1 = np.maximum(x1[i], x1[order[1:]])
yy1 = np.maximum(y1[i], y1[order[1:]])
xx2 = np.minimum(x2[i], x2[order[1:]])
yy2 = np.minimum(y2[i], y2[order[1:]])
w = np.maximum(0.0, xx2 - xx1 + 1)
h = np.maximum(0.0, yy2 - yy1 + 1)
inter = w * h
ovr = inter / (areas[i] + areas[order[1:]] - inter)
inds = np.where(ovr <= nms_thr)[0]
order = order[inds + 1]
return keep
def multiclass_nms(boxes, scores, nms_thr, score_thr):
"""Multiclass NMS implemented in Numpy. Class-aware version."""
final_dets = []
num_classes = scores.shape[1]
for cls_ind in range(num_classes):
cls_scores = scores[:, cls_ind]
valid_score_mask = cls_scores > score_thr
if valid_score_mask.sum() == 0:
continue
else:
valid_scores = cls_scores[valid_score_mask]
valid_boxes = boxes[valid_score_mask]
keep = nms(valid_boxes, valid_scores, nms_thr)
if len(keep) > 0:
cls_inds = np.ones((len(keep), 1)) * cls_ind
dets = np.concatenate(
[valid_boxes[keep], valid_scores[keep, None], cls_inds], 1
)
final_dets.append(dets)
if len(final_dets) == 0:
return None
return np.concatenate(final_dets, 0)
def demo_postprocess(outputs, img_size, p6=False):
grids = []
expanded_strides = []
strides = [8, 16, 32] if not p6 else [8, 16, 32, 64]
hsizes = [img_size[0] // stride for stride in strides]
wsizes = [img_size[1] // stride for stride in strides]
for hsize, wsize, stride in zip(hsizes, wsizes, strides):
xv, yv = np.meshgrid(np.arange(wsize), np.arange(hsize))
grid = np.stack((xv, yv), 2).reshape(1, -1, 2)
grids.append(grid)
shape = grid.shape[:2]
expanded_strides.append(np.full((*shape, 1), stride))
grids = np.concatenate(grids, 1)
expanded_strides = np.concatenate(expanded_strides, 1)
outputs[..., :2] = (outputs[..., :2] + grids) * expanded_strides
outputs[..., 2:4] = np.exp(outputs[..., 2:4]) * expanded_strides
return outputs
def preprocess(img, input_size, swap=(2, 0, 1)):
if len(img.shape) == 3:
padded_img = np.ones((input_size[0], input_size[1], 3), dtype=np.uint8) * 114
else:
padded_img = np.ones(input_size, dtype=np.uint8) * 114
r = min(input_size[0] / img.shape[0], input_size[1] / img.shape[1])
resized_img = cv2.resize(
img,
(int(img.shape[1] * r), int(img.shape[0] * r)),
interpolation=cv2.INTER_LINEAR,
).astype(np.uint8)
padded_img[: int(img.shape[0] * r), : int(img.shape[1] * r)] = resized_img
padded_img = padded_img.transpose(swap)
padded_img = np.ascontiguousarray(padded_img, dtype=np.float32)
return padded_img, r
def inference_detector(session, oriImg):
input_shape = (640,640)
img, ratio = preprocess(oriImg, input_shape)
ort_inputs = {session.get_inputs()[0].name: img[None, :, :, :]}
output = session.run(None, ort_inputs)
predictions = demo_postprocess(output[0], input_shape)[0]
boxes = predictions[:, :4]
scores = predictions[:, 4:5] * predictions[:, 5:]
boxes_xyxy = np.ones_like(boxes)
boxes_xyxy[:, 0] = boxes[:, 0] - boxes[:, 2]/2.
boxes_xyxy[:, 1] = boxes[:, 1] - boxes[:, 3]/2.
boxes_xyxy[:, 2] = boxes[:, 0] + boxes[:, 2]/2.
boxes_xyxy[:, 3] = boxes[:, 1] + boxes[:, 3]/2.
boxes_xyxy /= ratio
dets = multiclass_nms(boxes_xyxy, scores, nms_thr=0.45, score_thr=0.1)
if dets is not None:
final_boxes, final_scores, final_cls_inds = dets[:, :4], dets[:, 4], dets[:, 5]
isscore = final_scores>0.3
iscat = final_cls_inds == 0
isbbox = [ i and j for (i, j) in zip(isscore, iscat)]
final_boxes = final_boxes[isbbox]
else:
final_boxes = np.array([])
return final_boxes
+360
View File
@@ -0,0 +1,360 @@
from typing import List, Tuple
import cv2
import numpy as np
import onnxruntime as ort
def preprocess(
img: np.ndarray, out_bbox, input_size: Tuple[int, int] = (192, 256)
) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
"""Do preprocessing for RTMPose model inference.
Args:
img (np.ndarray): Input image in shape.
input_size (tuple): Input image size in shape (w, h).
Returns:
tuple:
- resized_img (np.ndarray): Preprocessed image.
- center (np.ndarray): Center of image.
- scale (np.ndarray): Scale of image.
"""
# get shape of image
img_shape = img.shape[:2]
out_img, out_center, out_scale = [], [], []
if len(out_bbox) == 0:
out_bbox = [[0, 0, img_shape[1], img_shape[0]]]
for i in range(len(out_bbox)):
x0 = out_bbox[i][0]
y0 = out_bbox[i][1]
x1 = out_bbox[i][2]
y1 = out_bbox[i][3]
bbox = np.array([x0, y0, x1, y1])
# get center and scale
center, scale = bbox_xyxy2cs(bbox, padding=1.25)
# do affine transformation
resized_img, scale = top_down_affine(input_size, scale, center, img)
# normalize image
mean = np.array([123.675, 116.28, 103.53])
std = np.array([58.395, 57.12, 57.375])
resized_img = (resized_img - mean) / std
out_img.append(resized_img)
out_center.append(center)
out_scale.append(scale)
return out_img, out_center, out_scale
def inference(sess: ort.InferenceSession, img: np.ndarray) -> np.ndarray:
"""Inference RTMPose model.
Args:
sess (ort.InferenceSession): ONNXRuntime session.
img (np.ndarray): Input image in shape.
Returns:
outputs (np.ndarray): Output of RTMPose model.
"""
all_out = []
# build input
for i in range(len(img)):
input = [img[i].transpose(2, 0, 1)]
# build output
sess_input = {sess.get_inputs()[0].name: input}
sess_output = []
for out in sess.get_outputs():
sess_output.append(out.name)
# run model
outputs = sess.run(sess_output, sess_input)
all_out.append(outputs)
return all_out
def postprocess(outputs: List[np.ndarray],
model_input_size: Tuple[int, int],
center: Tuple[int, int],
scale: Tuple[int, int],
simcc_split_ratio: float = 2.0
) -> Tuple[np.ndarray, np.ndarray]:
"""Postprocess for RTMPose model output.
Args:
outputs (np.ndarray): Output of RTMPose model.
model_input_size (tuple): RTMPose model Input image size.
center (tuple): Center of bbox in shape (x, y).
scale (tuple): Scale of bbox in shape (w, h).
simcc_split_ratio (float): Split ratio of simcc.
Returns:
tuple:
- keypoints (np.ndarray): Rescaled keypoints.
- scores (np.ndarray): Model predict scores.
"""
all_key = []
all_score = []
for i in range(len(outputs)):
# use simcc to decode
simcc_x, simcc_y = outputs[i]
keypoints, scores = decode(simcc_x, simcc_y, simcc_split_ratio)
# rescale keypoints
keypoints = keypoints / model_input_size * scale[i] + center[i] - scale[i] / 2
all_key.append(keypoints[0])
all_score.append(scores[0])
return np.array(all_key), np.array(all_score)
def bbox_xyxy2cs(bbox: np.ndarray,
padding: float = 1.) -> Tuple[np.ndarray, np.ndarray]:
"""Transform the bbox format from (x,y,w,h) into (center, scale)
Args:
bbox (ndarray): Bounding box(es) in shape (4,) or (n, 4), formatted
as (left, top, right, bottom)
padding (float): BBox padding factor that will be multilied to scale.
Default: 1.0
Returns:
tuple: A tuple containing center and scale.
- np.ndarray[float32]: Center (x, y) of the bbox in shape (2,) or
(n, 2)
- np.ndarray[float32]: Scale (w, h) of the bbox in shape (2,) or
(n, 2)
"""
# convert single bbox from (4, ) to (1, 4)
dim = bbox.ndim
if dim == 1:
bbox = bbox[None, :]
# get bbox center and scale
x1, y1, x2, y2 = np.hsplit(bbox, [1, 2, 3])
center = np.hstack([x1 + x2, y1 + y2]) * 0.5
scale = np.hstack([x2 - x1, y2 - y1]) * padding
if dim == 1:
center = center[0]
scale = scale[0]
return center, scale
def _fix_aspect_ratio(bbox_scale: np.ndarray,
aspect_ratio: float) -> np.ndarray:
"""Extend the scale to match the given aspect ratio.
Args:
scale (np.ndarray): The image scale (w, h) in shape (2, )
aspect_ratio (float): The ratio of ``w/h``
Returns:
np.ndarray: The reshaped image scale in (2, )
"""
w, h = np.hsplit(bbox_scale, [1])
bbox_scale = np.where(w > h * aspect_ratio,
np.hstack([w, w / aspect_ratio]),
np.hstack([h * aspect_ratio, h]))
return bbox_scale
def _rotate_point(pt: np.ndarray, angle_rad: float) -> np.ndarray:
"""Rotate a point by an angle.
Args:
pt (np.ndarray): 2D point coordinates (x, y) in shape (2, )
angle_rad (float): rotation angle in radian
Returns:
np.ndarray: Rotated point in shape (2, )
"""
sn, cs = np.sin(angle_rad), np.cos(angle_rad)
rot_mat = np.array([[cs, -sn], [sn, cs]])
return rot_mat @ pt
def _get_3rd_point(a: np.ndarray, b: np.ndarray) -> np.ndarray:
"""To calculate the affine matrix, three pairs of points are required. This
function is used to get the 3rd point, given 2D points a & b.
The 3rd point is defined by rotating vector `a - b` by 90 degrees
anticlockwise, using b as the rotation center.
Args:
a (np.ndarray): The 1st point (x,y) in shape (2, )
b (np.ndarray): The 2nd point (x,y) in shape (2, )
Returns:
np.ndarray: The 3rd point.
"""
direction = a - b
c = b + np.r_[-direction[1], direction[0]]
return c
def get_warp_matrix(center: np.ndarray,
scale: np.ndarray,
rot: float,
output_size: Tuple[int, int],
shift: Tuple[float, float] = (0., 0.),
inv: bool = False) -> np.ndarray:
"""Calculate the affine transformation matrix that can warp the bbox area
in the input image to the output size.
Args:
center (np.ndarray[2, ]): Center of the bounding box (x, y).
scale (np.ndarray[2, ]): Scale of the bounding box
wrt [width, height].
rot (float): Rotation angle (degree).
output_size (np.ndarray[2, ] | list(2,)): Size of the
destination heatmaps.
shift (0-100%): Shift translation ratio wrt the width/height.
Default (0., 0.).
inv (bool): Option to inverse the affine transform direction.
(inv=False: src->dst or inv=True: dst->src)
Returns:
np.ndarray: A 2x3 transformation matrix
"""
shift = np.array(shift)
src_w = scale[0]
dst_w = output_size[0]
dst_h = output_size[1]
# compute transformation matrix
rot_rad = np.deg2rad(rot)
src_dir = _rotate_point(np.array([0., src_w * -0.5]), rot_rad)
dst_dir = np.array([0., dst_w * -0.5])
# get four corners of the src rectangle in the original image
src = np.zeros((3, 2), dtype=np.float32)
src[0, :] = center + scale * shift
src[1, :] = center + src_dir + scale * shift
src[2, :] = _get_3rd_point(src[0, :], src[1, :])
# get four corners of the dst rectangle in the input image
dst = np.zeros((3, 2), dtype=np.float32)
dst[0, :] = [dst_w * 0.5, dst_h * 0.5]
dst[1, :] = np.array([dst_w * 0.5, dst_h * 0.5]) + dst_dir
dst[2, :] = _get_3rd_point(dst[0, :], dst[1, :])
if inv:
warp_mat = cv2.getAffineTransform(np.float32(dst), np.float32(src))
else:
warp_mat = cv2.getAffineTransform(np.float32(src), np.float32(dst))
return warp_mat
def top_down_affine(input_size: dict, bbox_scale: dict, bbox_center: dict,
img: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""Get the bbox image as the model input by affine transform.
Args:
input_size (dict): The input size of the model.
bbox_scale (dict): The bbox scale of the img.
bbox_center (dict): The bbox center of the img.
img (np.ndarray): The original image.
Returns:
tuple: A tuple containing center and scale.
- np.ndarray[float32]: img after affine transform.
- np.ndarray[float32]: bbox scale after affine transform.
"""
w, h = input_size
warp_size = (int(w), int(h))
# reshape bbox to fixed aspect ratio
bbox_scale = _fix_aspect_ratio(bbox_scale, aspect_ratio=w / h)
# get the affine matrix
center = bbox_center
scale = bbox_scale
rot = 0
warp_mat = get_warp_matrix(center, scale, rot, output_size=(w, h))
# do affine transform
img = cv2.warpAffine(img, warp_mat, warp_size, flags=cv2.INTER_LINEAR)
return img, bbox_scale
def get_simcc_maximum(simcc_x: np.ndarray,
simcc_y: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
"""Get maximum response location and value from simcc representations.
Note:
instance number: N
num_keypoints: K
heatmap height: H
heatmap width: W
Args:
simcc_x (np.ndarray): x-axis SimCC in shape (K, Wx) or (N, K, Wx)
simcc_y (np.ndarray): y-axis SimCC in shape (K, Wy) or (N, K, Wy)
Returns:
tuple:
- locs (np.ndarray): locations of maximum heatmap responses in shape
(K, 2) or (N, K, 2)
- vals (np.ndarray): values of maximum heatmap responses in shape
(K,) or (N, K)
"""
N, K, Wx = simcc_x.shape
simcc_x = simcc_x.reshape(N * K, -1)
simcc_y = simcc_y.reshape(N * K, -1)
# get maximum value locations
x_locs = np.argmax(simcc_x, axis=1)
y_locs = np.argmax(simcc_y, axis=1)
locs = np.stack((x_locs, y_locs), axis=-1).astype(np.float32)
max_val_x = np.amax(simcc_x, axis=1)
max_val_y = np.amax(simcc_y, axis=1)
# get maximum value across x and y axis
mask = max_val_x > max_val_y
max_val_x[mask] = max_val_y[mask]
vals = max_val_x
locs[vals <= 0.] = -1
# reshape
locs = locs.reshape(N, K, 2)
vals = vals.reshape(N, K)
return locs, vals
def decode(simcc_x: np.ndarray, simcc_y: np.ndarray,
simcc_split_ratio) -> Tuple[np.ndarray, np.ndarray]:
"""Modulate simcc distribution with Gaussian.
Args:
simcc_x (np.ndarray[K, Wx]): model predicted simcc in x.
simcc_y (np.ndarray[K, Wy]): model predicted simcc in y.
simcc_split_ratio (int): The split ratio of simcc.
Returns:
tuple: A tuple containing center and scale.
- np.ndarray[float32]: keypoints in shape (K, 2) or (n, K, 2)
- np.ndarray[float32]: scores in shape (K,) or (n, K)
"""
keypoints, scores = get_simcc_maximum(simcc_x, simcc_y)
keypoints /= simcc_split_ratio
return keypoints, scores
def inference_pose(session, out_bbox, oriImg):
h, w = session.get_inputs()[0].shape[2:]
model_input_size = (w, h)
resized_img, center, scale = preprocess(oriImg, out_bbox, model_input_size)
outputs = inference(session, resized_img)
keypoints, scores = postprocess(outputs, model_input_size, center, scale)
return keypoints, scores
+336
View File
@@ -0,0 +1,336 @@
import math
import numpy as np
import matplotlib
import cv2
eps = 0.01
def smart_resize(x, s):
Ht, Wt = s
if x.ndim == 2:
Ho, Wo = x.shape
Co = 1
else:
Ho, Wo, Co = x.shape
if Co == 3 or Co == 1:
k = float(Ht + Wt) / float(Ho + Wo)
return cv2.resize(x, (int(Wt), int(Ht)), interpolation=cv2.INTER_AREA if k < 1 else cv2.INTER_LANCZOS4)
else:
return np.stack([smart_resize(x[:, :, i], s) for i in range(Co)], axis=2)
def smart_resize_k(x, fx, fy):
if x.ndim == 2:
Ho, Wo = x.shape
Co = 1
else:
Ho, Wo, Co = x.shape
Ht, Wt = Ho * fy, Wo * fx
if Co == 3 or Co == 1:
k = float(Ht + Wt) / float(Ho + Wo)
return cv2.resize(x, (int(Wt), int(Ht)), interpolation=cv2.INTER_AREA if k < 1 else cv2.INTER_LANCZOS4)
else:
return np.stack([smart_resize_k(x[:, :, i], fx, fy) for i in range(Co)], axis=2)
def padRightDownCorner(img, stride, padValue):
h = img.shape[0]
w = img.shape[1]
pad = 4 * [None]
pad[0] = 0 # up
pad[1] = 0 # left
pad[2] = 0 if (h % stride == 0) else stride - (h % stride) # down
pad[3] = 0 if (w % stride == 0) else stride - (w % stride) # right
img_padded = img
pad_up = np.tile(img_padded[0:1, :, :]*0 + padValue, (pad[0], 1, 1))
img_padded = np.concatenate((pad_up, img_padded), axis=0)
pad_left = np.tile(img_padded[:, 0:1, :]*0 + padValue, (1, pad[1], 1))
img_padded = np.concatenate((pad_left, img_padded), axis=1)
pad_down = np.tile(img_padded[-2:-1, :, :]*0 + padValue, (pad[2], 1, 1))
img_padded = np.concatenate((img_padded, pad_down), axis=0)
pad_right = np.tile(img_padded[:, -2:-1, :]*0 + padValue, (1, pad[3], 1))
img_padded = np.concatenate((img_padded, pad_right), axis=1)
return img_padded, pad
def transfer(model, model_weights):
transfered_model_weights = {}
for weights_name in model.state_dict().keys():
transfered_model_weights[weights_name] = model_weights['.'.join(weights_name.split('.')[1:])]
return transfered_model_weights
def draw_bodypose(canvas, candidate, subset):
H, W, C = canvas.shape
candidate = np.array(candidate)
subset = np.array(subset)
stickwidth = 4
limbSeq = [[2, 3], [2, 6], [3, 4], [4, 5], [6, 7], [7, 8], [2, 9], [9, 10], \
[10, 11], [2, 12], [12, 13], [13, 14], [2, 1], [1, 15], [15, 17], \
[1, 16], [16, 18], [3, 17], [6, 18]]
colors = [[255, 0, 0], [255, 85, 0], [255, 170, 0], [255, 255, 0], [170, 255, 0], [85, 255, 0], [0, 255, 0], \
[0, 255, 85], [0, 255, 170], [0, 255, 255], [0, 170, 255], [0, 85, 255], [0, 0, 255], [85, 0, 255], \
[170, 0, 255], [255, 0, 255], [255, 0, 170], [255, 0, 85]]
for i in range(17):
for n in range(len(subset)):
index = subset[n][np.array(limbSeq[i]) - 1]
if -1 in index:
continue
Y = candidate[index.astype(int), 0] * float(W)
X = candidate[index.astype(int), 1] * float(H)
mX = np.mean(X)
mY = np.mean(Y)
length = ((X[0] - X[1]) ** 2 + (Y[0] - Y[1]) ** 2) ** 0.5
angle = math.degrees(math.atan2(X[0] - X[1], Y[0] - Y[1]))
polygon = cv2.ellipse2Poly((int(mY), int(mX)), (int(length / 2), stickwidth), int(angle), 0, 360, 1)
cv2.fillConvexPoly(canvas, polygon, colors[i])
canvas = (canvas * 0.6).astype(np.uint8)
for i in range(18):
for n in range(len(subset)):
index = int(subset[n][i])
if index == -1:
continue
x, y = candidate[index][0:2]
x = int(x * W)
y = int(y * H)
cv2.circle(canvas, (int(x), int(y)), 4, colors[i], thickness=-1)
return canvas
def draw_body_and_foot(canvas, candidate, subset):
H, W, C = canvas.shape
candidate = np.array(candidate)
subset = np.array(subset)
stickwidth = 4
limbSeq = [[2, 3], [2, 6], [3, 4], [4, 5], [6, 7], [7, 8], [2, 9], [9, 10], \
[10, 11], [2, 12], [12, 13], [13, 14], [2, 1], [1, 15], [15, 17], \
[1, 16], [16, 18], [14,19], [11, 20]]
colors = [[255, 0, 0], [255, 85, 0], [255, 170, 0], [255, 255, 0], [170, 255, 0], [85, 255, 0], [0, 255, 0], \
[0, 255, 85], [0, 255, 170], [0, 255, 255], [0, 170, 255], [0, 85, 255], [0, 0, 255], [85, 0, 255], \
[170, 0, 255], [255, 0, 255], [255, 0, 170], [255, 0, 85], [170, 255, 255], [255, 255, 0]]
for i in range(19):
for n in range(len(subset)):
index = subset[n][np.array(limbSeq[i]) - 1]
if -1 in index:
continue
Y = candidate[index.astype(int), 0] * float(W)
X = candidate[index.astype(int), 1] * float(H)
mX = np.mean(X)
mY = np.mean(Y)
length = ((X[0] - X[1]) ** 2 + (Y[0] - Y[1]) ** 2) ** 0.5
angle = math.degrees(math.atan2(X[0] - X[1], Y[0] - Y[1]))
polygon = cv2.ellipse2Poly((int(mY), int(mX)), (int(length / 2), stickwidth), int(angle), 0, 360, 1)
cv2.fillConvexPoly(canvas, polygon, colors[i])
canvas = (canvas * 0.6).astype(np.uint8)
for i in range(20):
for n in range(len(subset)):
index = int(subset[n][i])
if index == -1:
continue
x, y = candidate[index][0:2]
x = int(x * W)
y = int(y * H)
cv2.circle(canvas, (int(x), int(y)), 4, colors[i], thickness=-1)
return canvas
def draw_handpose(canvas, all_hand_peaks):
H, W, C = canvas.shape
edges = [[0, 1], [1, 2], [2, 3], [3, 4], [0, 5], [5, 6], [6, 7], [7, 8], [0, 9], [9, 10], \
[10, 11], [11, 12], [0, 13], [13, 14], [14, 15], [15, 16], [0, 17], [17, 18], [18, 19], [19, 20]]
for peaks in all_hand_peaks:
peaks = np.array(peaks)
for ie, e in enumerate(edges):
x1, y1 = peaks[e[0]]
x2, y2 = peaks[e[1]]
x1 = int(x1 * W)
y1 = int(y1 * H)
x2 = int(x2 * W)
y2 = int(y2 * H)
if x1 > eps and y1 > eps and x2 > eps and y2 > eps:
cv2.line(canvas, (x1, y1), (x2, y2), matplotlib.colors.hsv_to_rgb([ie / float(len(edges)), 1.0, 1.0]) * 255, thickness=2)
for i, keyponit in enumerate(peaks):
x, y = keyponit
x = int(x * W)
y = int(y * H)
if x > eps and y > eps:
cv2.circle(canvas, (x, y), 4, (0, 0, 255), thickness=-1)
return canvas
def draw_facepose(canvas, all_lmks):
H, W, C = canvas.shape
for lmks in all_lmks:
lmks = np.array(lmks)
for lmk in lmks:
x, y = lmk
x = int(x * W)
y = int(y * H)
if x > eps and y > eps:
cv2.circle(canvas, (x, y), 3, (255, 255, 255), thickness=-1)
return canvas
# detect hand according to body pose keypoints
# please refer to https://github.com/CMU-Perceptual-Computing-Lab/openpose/blob/master/src/openpose/hand/handDetector.cpp
def handDetect(candidate, subset, oriImg):
# right hand: wrist 4, elbow 3, shoulder 2
# left hand: wrist 7, elbow 6, shoulder 5
ratioWristElbow = 0.33
detect_result = []
image_height, image_width = oriImg.shape[0:2]
for person in subset.astype(int):
# if any of three not detected
has_left = np.sum(person[[5, 6, 7]] == -1) == 0
has_right = np.sum(person[[2, 3, 4]] == -1) == 0
if not (has_left or has_right):
continue
hands = []
#left hand
if has_left:
left_shoulder_index, left_elbow_index, left_wrist_index = person[[5, 6, 7]]
x1, y1 = candidate[left_shoulder_index][:2]
x2, y2 = candidate[left_elbow_index][:2]
x3, y3 = candidate[left_wrist_index][:2]
hands.append([x1, y1, x2, y2, x3, y3, True])
# right hand
if has_right:
right_shoulder_index, right_elbow_index, right_wrist_index = person[[2, 3, 4]]
x1, y1 = candidate[right_shoulder_index][:2]
x2, y2 = candidate[right_elbow_index][:2]
x3, y3 = candidate[right_wrist_index][:2]
hands.append([x1, y1, x2, y2, x3, y3, False])
for x1, y1, x2, y2, x3, y3, is_left in hands:
x = x3 + ratioWristElbow * (x3 - x2)
y = y3 + ratioWristElbow * (y3 - y2)
distanceWristElbow = math.sqrt((x3 - x2) ** 2 + (y3 - y2) ** 2)
distanceElbowShoulder = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
width = 1.5 * max(distanceWristElbow, 0.9 * distanceElbowShoulder)
# x-y refers to the center --> offset to topLeft point
# handRectangle.x -= handRectangle.width / 2.f;
# handRectangle.y -= handRectangle.height / 2.f;
x -= width / 2
y -= width / 2 # width = height
# overflow the image
if x < 0: x = 0
if y < 0: y = 0
width1 = width
width2 = width
if x + width > image_width: width1 = image_width - x
if y + width > image_height: width2 = image_height - y
width = min(width1, width2)
# the max hand box value is 20 pixels
if width >= 20:
detect_result.append([int(x), int(y), int(width), is_left])
'''
return value: [[x, y, w, True if left hand else False]].
width=height since the network require squared input.
x, y is the coordinate of top left
'''
return detect_result
# Written by Lvmin
def faceDetect(candidate, subset, oriImg):
# left right eye ear 14 15 16 17
detect_result = []
image_height, image_width = oriImg.shape[0:2]
for person in subset.astype(int):
has_head = person[0] > -1
if not has_head:
continue
has_left_eye = person[14] > -1
has_right_eye = person[15] > -1
has_left_ear = person[16] > -1
has_right_ear = person[17] > -1
if not (has_left_eye or has_right_eye or has_left_ear or has_right_ear):
continue
head, left_eye, right_eye, left_ear, right_ear = person[[0, 14, 15, 16, 17]]
width = 0.0
x0, y0 = candidate[head][:2]
if has_left_eye:
x1, y1 = candidate[left_eye][:2]
d = max(abs(x0 - x1), abs(y0 - y1))
width = max(width, d * 3.0)
if has_right_eye:
x1, y1 = candidate[right_eye][:2]
d = max(abs(x0 - x1), abs(y0 - y1))
width = max(width, d * 3.0)
if has_left_ear:
x1, y1 = candidate[left_ear][:2]
d = max(abs(x0 - x1), abs(y0 - y1))
width = max(width, d * 1.5)
if has_right_ear:
x1, y1 = candidate[right_ear][:2]
d = max(abs(x0 - x1), abs(y0 - y1))
width = max(width, d * 1.5)
x, y = x0, y0
x -= width
y -= width
if x < 0:
x = 0
if y < 0:
y = 0
width1 = width * 2
width2 = width * 2
if x + width > image_width:
width1 = image_width - x
if y + width > image_height:
width2 = image_height - y
width = min(width1, width2)
if width >= 20:
detect_result.append([int(x), int(y), int(width)])
return detect_result
# get max index of 2d array
def npmax(array):
arrayindex = array.argmax(1)
arrayvalue = array.max(1)
i = arrayvalue.argmax()
j = arrayindex[i]
return i, j
+52
View File
@@ -0,0 +1,52 @@
import os
import cv2
import numpy as np
import onnxruntime as ort
from dwpose.onnxdet import inference_detector
from dwpose.onnxpose import inference_pose
now_dir = os.path.dirname(os.path.abspath(__file__))
uni_dir = os.path.dirname(now_dir)
weights_dir = os.path.join(uni_dir,"checkpoints")
class Wholebody:
def __init__(self):
device = 'cuda' # 'cpu' #
providers = ['CPUExecutionProvider'
] if device == 'cpu' else ['CUDAExecutionProvider']
onnx_det = os.path.join(weights_dir,'yolox_l.onnx')
onnx_pose = os.path.join(weights_dir,'dw-ll_ucoco_384.onnx')
self.session_det = ort.InferenceSession(path_or_bytes=onnx_det, providers=providers)
self.session_pose = ort.InferenceSession(path_or_bytes=onnx_pose, providers=providers)
def __call__(self, oriImg):
det_result = inference_detector(self.session_det, oriImg)
keypoints, scores = inference_pose(self.session_pose, det_result, oriImg)
keypoints_info = np.concatenate(
(keypoints, scores[..., None]), axis=-1)
# compute neck joint
neck = np.mean(keypoints_info[:, [5, 6]], axis=1)
# neck score when visualizing pred
neck[:, 2:4] = np.logical_and(
keypoints_info[:, 5, 2:4] > 0.3,
keypoints_info[:, 6, 2:4] > 0.3).astype(int)
new_keypoints_info = np.insert(
keypoints_info, 17, neck, axis=1)
mmpose_idx = [
17, 6, 8, 10, 7, 9, 12, 14, 16, 13, 15, 2, 1, 4, 3
]
openpose_idx = [
1, 2, 3, 4, 6, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17
]
new_keypoints_info[:, openpose_idx] = \
new_keypoints_info[:, mmpose_idx]
keypoints_info = new_keypoints_info
keypoints, scores = keypoints_info[
..., :2], keypoints_info[..., 2]
return keypoints, scores
+18
View File
@@ -0,0 +1,18 @@
import os
import sys
import copy
import json
import math
import random
import logging
import itertools
import numpy as np
from utils.config import Config
from utils.registry_class import INFER_ENGINE
from tools import *
if __name__ == '__main__':
cfg_update = Config(load=True)
INFER_ENGINE.build(dict(type=cfg_update.TASK_TYPE), cfg_update=cfg_update.cfg_dict)
+712
View File
@@ -0,0 +1,712 @@
# Openpose
# Original from CMU https://github.com/CMU-Perceptual-Computing-Lab/openpose
# 2nd Edited by https://github.com/Hzzone/pytorch-openpose
# 3rd Edited by ControlNet
# 4th Edited by ControlNet (added face and correct hands)
import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
import cv2
import torch
import numpy as np
import json
import copy
import torch
import random
import argparse
import shutil
import tempfile
import subprocess
import numpy as np
import math
import torch.multiprocessing as mp
import torch.distributed as dist
import pickle
import logging
from io import BytesIO
import oss2 as oss
import os.path as osp
import sys
import dwpose.util as util
from dwpose.wholebody import Wholebody
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, t0, x0, dx0=0.0, min_cutoff=1.0, beta=0.0,
d_cutoff=1.0):
"""Initialize the one euro filter."""
# The parameters.
self.min_cutoff = float(min_cutoff)
self.beta = float(beta)
self.d_cutoff = float(d_cutoff)
# Previous values.
self.x_prev = x0
self.dx_prev = float(dx0)
self.t_prev = float(t0)
def __call__(self, t, x):
"""Compute the filtered signal."""
t_e = t - self.t_prev
# The filtered derivative of the signal.
a_d = smoothing_factor(t_e, self.d_cutoff)
dx = (x - self.x_prev) / t_e
dx_hat = exponential_smoothing(a_d, dx, self.dx_prev)
# The filtered signal.
cutoff = self.min_cutoff + self.beta * abs(dx_hat)
a = smoothing_factor(t_e, cutoff)
x_hat = exponential_smoothing(a, x, self.x_prev)
# Memorize the previous values.
self.x_prev = x_hat
self.dx_prev = dx_hat
self.t_prev = t
return x_hat
def get_logger(name="essmc2"):
logger = logging.getLogger(name)
logger.propagate = False
if len(logger.handlers) == 0:
std_handler = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s')
std_handler.setFormatter(formatter)
std_handler.setLevel(logging.INFO)
logger.setLevel(logging.INFO)
logger.addHandler(std_handler)
return logger
class DWposeDetector:
def __init__(self):
self.pose_estimation = Wholebody()
def __call__(self, oriImg):
oriImg = oriImg.copy()
H, W, C = oriImg.shape
with torch.no_grad():
candidate, subset = self.pose_estimation(oriImg)
candidate = candidate[0][np.newaxis, :, :]
subset = subset[0][np.newaxis, :]
nums, keys, locs = candidate.shape
candidate[..., 0] /= float(W)
candidate[..., 1] /= float(H)
body = candidate[:,:18].copy()
body = body.reshape(nums*18, locs)
score = subset[:,:18].copy()
for i in range(len(score)):
for j in range(len(score[i])):
if score[i][j] > 0.3:
score[i][j] = int(18*i+j)
else:
score[i][j] = -1
un_visible = subset<0.3
candidate[un_visible] = -1
bodyfoot_score = subset[:,:24].copy()
for i in range(len(bodyfoot_score)):
for j in range(len(bodyfoot_score[i])):
if bodyfoot_score[i][j] > 0.3:
bodyfoot_score[i][j] = int(18*i+j)
else:
bodyfoot_score[i][j] = -1
if -1 not in bodyfoot_score[:,18] and -1 not in bodyfoot_score[:,19]:
bodyfoot_score[:,18] = np.array([18.])
else:
bodyfoot_score[:,18] = np.array([-1.])
if -1 not in bodyfoot_score[:,21] and -1 not in bodyfoot_score[:,22]:
bodyfoot_score[:,19] = np.array([19.])
else:
bodyfoot_score[:,19] = np.array([-1.])
bodyfoot_score = bodyfoot_score[:, :20]
bodyfoot = candidate[:,:24].copy()
for i in range(nums):
if -1 not in bodyfoot[i][18] and -1 not in bodyfoot[i][19]:
bodyfoot[i][18] = (bodyfoot[i][18]+bodyfoot[i][19])/2
else:
bodyfoot[i][18] = np.array([-1., -1.])
if -1 not in bodyfoot[i][21] and -1 not in bodyfoot[i][22]:
bodyfoot[i][19] = (bodyfoot[i][21]+bodyfoot[i][22])/2
else:
bodyfoot[i][19] = np.array([-1., -1.])
bodyfoot = bodyfoot[:,:20,:]
bodyfoot = bodyfoot.reshape(nums*20, locs)
foot = candidate[:,18:24]
faces = candidate[:,24:92]
hands = candidate[:,92:113]
hands = np.vstack([hands, candidate[:,113:]])
# bodies = dict(candidate=body, subset=score)
bodies = dict(candidate=bodyfoot, subset=bodyfoot_score)
pose = dict(bodies=bodies, hands=hands, faces=faces)
# return draw_pose(pose, H, W)
return pose
def draw_pose(pose, H, W):
bodies = pose['bodies']
faces = pose['faces']
hands = pose['hands']
candidate = bodies['candidate']
subset = bodies['subset']
canvas = np.zeros(shape=(H, W, 3), dtype=np.uint8)
canvas = util.draw_body_and_foot(canvas, candidate, subset)
canvas = util.draw_handpose(canvas, hands)
canvas_without_face = copy.deepcopy(canvas)
canvas = util.draw_facepose(canvas, faces)
return canvas_without_face, canvas
def dw_func(_id, frame, dwpose_model, dwpose_woface_folder='tmp_dwpose_wo_face', dwpose_withface_folder='tmp_dwpose_with_face'):
# frame = cv2.imread(frame_name, cv2.IMREAD_COLOR)
pose = dwpose_model(frame)
return pose
def mp_main(args):
if args.source_video_paths.endswith('mp4'):
video_paths = [args.source_video_paths]
else:
# video list
video_paths = [os.path.join(args.source_video_paths, frame_name) for frame_name in os.listdir(args.source_video_paths)]
logger.info("There are {} videos for extracting poses".format(len(video_paths)))
logger.info('LOAD: DW Pose Model')
dwpose_model = DWposeDetector()
results_vis = []
for i, file_path in enumerate(video_paths):
logger.info(f"{i}/{len(video_paths)}, {file_path}")
videoCapture = cv2.VideoCapture(file_path)
while videoCapture.isOpened():
# get a frame
ret, frame = videoCapture.read()
if ret:
pose = dw_func(i, frame, dwpose_model)
results_vis.append(pose)
else:
break
logger.info(f'all frames in {file_path} have been read.')
videoCapture.release()
# added
# results_vis = results_vis[8:]
print(len(results_vis))
ref_name = args.ref_name
save_motion = args.saved_pose_dir
os.system(f'rm -rf {save_motion}');
os.makedirs(save_motion, exist_ok=True)
save_warp = args.saved_pose_dir
# os.makedirs(save_warp, exist_ok=True)
ref_frame = cv2.imread(ref_name, cv2.IMREAD_COLOR)
pose_ref = dw_func(i, ref_frame, dwpose_model)
bodies = results_vis[0]['bodies']
faces = results_vis[0]['faces']
hands = results_vis[0]['hands']
candidate = bodies['candidate']
ref_bodies = pose_ref['bodies']
ref_faces = pose_ref['faces']
ref_hands = pose_ref['hands']
ref_candidate = ref_bodies['candidate']
ref_2_x = ref_candidate[2][0]
ref_2_y = ref_candidate[2][1]
ref_5_x = ref_candidate[5][0]
ref_5_y = ref_candidate[5][1]
ref_8_x = ref_candidate[8][0]
ref_8_y = ref_candidate[8][1]
ref_11_x = ref_candidate[11][0]
ref_11_y = ref_candidate[11][1]
ref_center1 = 0.5*(ref_candidate[2]+ref_candidate[5])
ref_center2 = 0.5*(ref_candidate[8]+ref_candidate[11])
zero_2_x = candidate[2][0]
zero_2_y = candidate[2][1]
zero_5_x = candidate[5][0]
zero_5_y = candidate[5][1]
zero_8_x = candidate[8][0]
zero_8_y = candidate[8][1]
zero_11_x = candidate[11][0]
zero_11_y = candidate[11][1]
zero_center1 = 0.5*(candidate[2]+candidate[5])
zero_center2 = 0.5*(candidate[8]+candidate[11])
x_ratio = (ref_5_x-ref_2_x)/(zero_5_x-zero_2_x)
y_ratio = (ref_center2[1]-ref_center1[1])/(zero_center2[1]-zero_center1[1])
results_vis[0]['bodies']['candidate'][:,0] *= x_ratio
results_vis[0]['bodies']['candidate'][:,1] *= y_ratio
results_vis[0]['faces'][:,:,0] *= x_ratio
results_vis[0]['faces'][:,:,1] *= y_ratio
results_vis[0]['hands'][:,:,0] *= x_ratio
results_vis[0]['hands'][:,:,1] *= y_ratio
########neck########
l_neck_ref = ((ref_candidate[0][0] - ref_candidate[1][0]) ** 2 + (ref_candidate[0][1] - ref_candidate[1][1]) ** 2) ** 0.5
l_neck_0 = ((candidate[0][0] - candidate[1][0]) ** 2 + (candidate[0][1] - candidate[1][1]) ** 2) ** 0.5
neck_ratio = l_neck_ref / l_neck_0
x_offset_neck = (candidate[1][0]-candidate[0][0])*(1.-neck_ratio)
y_offset_neck = (candidate[1][1]-candidate[0][1])*(1.-neck_ratio)
results_vis[0]['bodies']['candidate'][0,0] += x_offset_neck
results_vis[0]['bodies']['candidate'][0,1] += y_offset_neck
results_vis[0]['bodies']['candidate'][14,0] += x_offset_neck
results_vis[0]['bodies']['candidate'][14,1] += y_offset_neck
results_vis[0]['bodies']['candidate'][15,0] += x_offset_neck
results_vis[0]['bodies']['candidate'][15,1] += y_offset_neck
results_vis[0]['bodies']['candidate'][16,0] += x_offset_neck
results_vis[0]['bodies']['candidate'][16,1] += y_offset_neck
results_vis[0]['bodies']['candidate'][17,0] += x_offset_neck
results_vis[0]['bodies']['candidate'][17,1] += y_offset_neck
########shoulder2########
l_shoulder2_ref = ((ref_candidate[2][0] - ref_candidate[1][0]) ** 2 + (ref_candidate[2][1] - ref_candidate[1][1]) ** 2) ** 0.5
l_shoulder2_0 = ((candidate[2][0] - candidate[1][0]) ** 2 + (candidate[2][1] - candidate[1][1]) ** 2) ** 0.5
shoulder2_ratio = l_shoulder2_ref / l_shoulder2_0
x_offset_shoulder2 = (candidate[1][0]-candidate[2][0])*(1.-shoulder2_ratio)
y_offset_shoulder2 = (candidate[1][1]-candidate[2][1])*(1.-shoulder2_ratio)
results_vis[0]['bodies']['candidate'][2,0] += x_offset_shoulder2
results_vis[0]['bodies']['candidate'][2,1] += y_offset_shoulder2
results_vis[0]['bodies']['candidate'][3,0] += x_offset_shoulder2
results_vis[0]['bodies']['candidate'][3,1] += y_offset_shoulder2
results_vis[0]['bodies']['candidate'][4,0] += x_offset_shoulder2
results_vis[0]['bodies']['candidate'][4,1] += y_offset_shoulder2
results_vis[0]['hands'][1,:,0] += x_offset_shoulder2
results_vis[0]['hands'][1,:,1] += y_offset_shoulder2
########shoulder5########
l_shoulder5_ref = ((ref_candidate[5][0] - ref_candidate[1][0]) ** 2 + (ref_candidate[5][1] - ref_candidate[1][1]) ** 2) ** 0.5
l_shoulder5_0 = ((candidate[5][0] - candidate[1][0]) ** 2 + (candidate[5][1] - candidate[1][1]) ** 2) ** 0.5
shoulder5_ratio = l_shoulder5_ref / l_shoulder5_0
x_offset_shoulder5 = (candidate[1][0]-candidate[5][0])*(1.-shoulder5_ratio)
y_offset_shoulder5 = (candidate[1][1]-candidate[5][1])*(1.-shoulder5_ratio)
results_vis[0]['bodies']['candidate'][5,0] += x_offset_shoulder5
results_vis[0]['bodies']['candidate'][5,1] += y_offset_shoulder5
results_vis[0]['bodies']['candidate'][6,0] += x_offset_shoulder5
results_vis[0]['bodies']['candidate'][6,1] += y_offset_shoulder5
results_vis[0]['bodies']['candidate'][7,0] += x_offset_shoulder5
results_vis[0]['bodies']['candidate'][7,1] += y_offset_shoulder5
results_vis[0]['hands'][0,:,0] += x_offset_shoulder5
results_vis[0]['hands'][0,:,1] += y_offset_shoulder5
########arm3########
l_arm3_ref = ((ref_candidate[3][0] - ref_candidate[2][0]) ** 2 + (ref_candidate[3][1] - ref_candidate[2][1]) ** 2) ** 0.5
l_arm3_0 = ((candidate[3][0] - candidate[2][0]) ** 2 + (candidate[3][1] - candidate[2][1]) ** 2) ** 0.5
arm3_ratio = l_arm3_ref / l_arm3_0
x_offset_arm3 = (candidate[2][0]-candidate[3][0])*(1.-arm3_ratio)
y_offset_arm3 = (candidate[2][1]-candidate[3][1])*(1.-arm3_ratio)
results_vis[0]['bodies']['candidate'][3,0] += x_offset_arm3
results_vis[0]['bodies']['candidate'][3,1] += y_offset_arm3
results_vis[0]['bodies']['candidate'][4,0] += x_offset_arm3
results_vis[0]['bodies']['candidate'][4,1] += y_offset_arm3
results_vis[0]['hands'][1,:,0] += x_offset_arm3
results_vis[0]['hands'][1,:,1] += y_offset_arm3
########arm4########
l_arm4_ref = ((ref_candidate[4][0] - ref_candidate[3][0]) ** 2 + (ref_candidate[4][1] - ref_candidate[3][1]) ** 2) ** 0.5
l_arm4_0 = ((candidate[4][0] - candidate[3][0]) ** 2 + (candidate[4][1] - candidate[3][1]) ** 2) ** 0.5
arm4_ratio = l_arm4_ref / l_arm4_0
x_offset_arm4 = (candidate[3][0]-candidate[4][0])*(1.-arm4_ratio)
y_offset_arm4 = (candidate[3][1]-candidate[4][1])*(1.-arm4_ratio)
results_vis[0]['bodies']['candidate'][4,0] += x_offset_arm4
results_vis[0]['bodies']['candidate'][4,1] += y_offset_arm4
results_vis[0]['hands'][1,:,0] += x_offset_arm4
results_vis[0]['hands'][1,:,1] += y_offset_arm4
########arm6########
l_arm6_ref = ((ref_candidate[6][0] - ref_candidate[5][0]) ** 2 + (ref_candidate[6][1] - ref_candidate[5][1]) ** 2) ** 0.5
l_arm6_0 = ((candidate[6][0] - candidate[5][0]) ** 2 + (candidate[6][1] - candidate[5][1]) ** 2) ** 0.5
arm6_ratio = l_arm6_ref / l_arm6_0
x_offset_arm6 = (candidate[5][0]-candidate[6][0])*(1.-arm6_ratio)
y_offset_arm6 = (candidate[5][1]-candidate[6][1])*(1.-arm6_ratio)
results_vis[0]['bodies']['candidate'][6,0] += x_offset_arm6
results_vis[0]['bodies']['candidate'][6,1] += y_offset_arm6
results_vis[0]['bodies']['candidate'][7,0] += x_offset_arm6
results_vis[0]['bodies']['candidate'][7,1] += y_offset_arm6
results_vis[0]['hands'][0,:,0] += x_offset_arm6
results_vis[0]['hands'][0,:,1] += y_offset_arm6
########arm7########
l_arm7_ref = ((ref_candidate[7][0] - ref_candidate[6][0]) ** 2 + (ref_candidate[7][1] - ref_candidate[6][1]) ** 2) ** 0.5
l_arm7_0 = ((candidate[7][0] - candidate[6][0]) ** 2 + (candidate[7][1] - candidate[6][1]) ** 2) ** 0.5
arm7_ratio = l_arm7_ref / l_arm7_0
x_offset_arm7 = (candidate[6][0]-candidate[7][0])*(1.-arm7_ratio)
y_offset_arm7 = (candidate[6][1]-candidate[7][1])*(1.-arm7_ratio)
results_vis[0]['bodies']['candidate'][7,0] += x_offset_arm7
results_vis[0]['bodies']['candidate'][7,1] += y_offset_arm7
results_vis[0]['hands'][0,:,0] += x_offset_arm7
results_vis[0]['hands'][0,:,1] += y_offset_arm7
########head14########
l_head14_ref = ((ref_candidate[14][0] - ref_candidate[0][0]) ** 2 + (ref_candidate[14][1] - ref_candidate[0][1]) ** 2) ** 0.5
l_head14_0 = ((candidate[14][0] - candidate[0][0]) ** 2 + (candidate[14][1] - candidate[0][1]) ** 2) ** 0.5
head14_ratio = l_head14_ref / l_head14_0
x_offset_head14 = (candidate[0][0]-candidate[14][0])*(1.-head14_ratio)
y_offset_head14 = (candidate[0][1]-candidate[14][1])*(1.-head14_ratio)
results_vis[0]['bodies']['candidate'][14,0] += x_offset_head14
results_vis[0]['bodies']['candidate'][14,1] += y_offset_head14
results_vis[0]['bodies']['candidate'][16,0] += x_offset_head14
results_vis[0]['bodies']['candidate'][16,1] += y_offset_head14
########head15########
l_head15_ref = ((ref_candidate[15][0] - ref_candidate[0][0]) ** 2 + (ref_candidate[15][1] - ref_candidate[0][1]) ** 2) ** 0.5
l_head15_0 = ((candidate[15][0] - candidate[0][0]) ** 2 + (candidate[15][1] - candidate[0][1]) ** 2) ** 0.5
head15_ratio = l_head15_ref / l_head15_0
x_offset_head15 = (candidate[0][0]-candidate[15][0])*(1.-head15_ratio)
y_offset_head15 = (candidate[0][1]-candidate[15][1])*(1.-head15_ratio)
results_vis[0]['bodies']['candidate'][15,0] += x_offset_head15
results_vis[0]['bodies']['candidate'][15,1] += y_offset_head15
results_vis[0]['bodies']['candidate'][17,0] += x_offset_head15
results_vis[0]['bodies']['candidate'][17,1] += y_offset_head15
########head16########
l_head16_ref = ((ref_candidate[16][0] - ref_candidate[14][0]) ** 2 + (ref_candidate[16][1] - ref_candidate[14][1]) ** 2) ** 0.5
l_head16_0 = ((candidate[16][0] - candidate[14][0]) ** 2 + (candidate[16][1] - candidate[14][1]) ** 2) ** 0.5
head16_ratio = l_head16_ref / l_head16_0
x_offset_head16 = (candidate[14][0]-candidate[16][0])*(1.-head16_ratio)
y_offset_head16 = (candidate[14][1]-candidate[16][1])*(1.-head16_ratio)
results_vis[0]['bodies']['candidate'][16,0] += x_offset_head16
results_vis[0]['bodies']['candidate'][16,1] += y_offset_head16
########head17########
l_head17_ref = ((ref_candidate[17][0] - ref_candidate[15][0]) ** 2 + (ref_candidate[17][1] - ref_candidate[15][1]) ** 2) ** 0.5
l_head17_0 = ((candidate[17][0] - candidate[15][0]) ** 2 + (candidate[17][1] - candidate[15][1]) ** 2) ** 0.5
head17_ratio = l_head17_ref / l_head17_0
x_offset_head17 = (candidate[15][0]-candidate[17][0])*(1.-head17_ratio)
y_offset_head17 = (candidate[15][1]-candidate[17][1])*(1.-head17_ratio)
results_vis[0]['bodies']['candidate'][17,0] += x_offset_head17
results_vis[0]['bodies']['candidate'][17,1] += y_offset_head17
########MovingAverage########
########left leg########
l_ll1_ref = ((ref_candidate[8][0] - ref_candidate[9][0]) ** 2 + (ref_candidate[8][1] - ref_candidate[9][1]) ** 2) ** 0.5
l_ll1_0 = ((candidate[8][0] - candidate[9][0]) ** 2 + (candidate[8][1] - candidate[9][1]) ** 2) ** 0.5
ll1_ratio = l_ll1_ref / l_ll1_0
x_offset_ll1 = (candidate[9][0]-candidate[8][0])*(ll1_ratio-1.)
y_offset_ll1 = (candidate[9][1]-candidate[8][1])*(ll1_ratio-1.)
results_vis[0]['bodies']['candidate'][9,0] += x_offset_ll1
results_vis[0]['bodies']['candidate'][9,1] += y_offset_ll1
results_vis[0]['bodies']['candidate'][10,0] += x_offset_ll1
results_vis[0]['bodies']['candidate'][10,1] += y_offset_ll1
results_vis[0]['bodies']['candidate'][19,0] += x_offset_ll1
results_vis[0]['bodies']['candidate'][19,1] += y_offset_ll1
l_ll2_ref = ((ref_candidate[9][0] - ref_candidate[10][0]) ** 2 + (ref_candidate[9][1] - ref_candidate[10][1]) ** 2) ** 0.5
l_ll2_0 = ((candidate[9][0] - candidate[10][0]) ** 2 + (candidate[9][1] - candidate[10][1]) ** 2) ** 0.5
ll2_ratio = l_ll2_ref / l_ll2_0
x_offset_ll2 = (candidate[10][0]-candidate[9][0])*(ll2_ratio-1.)
y_offset_ll2 = (candidate[10][1]-candidate[9][1])*(ll2_ratio-1.)
results_vis[0]['bodies']['candidate'][10,0] += x_offset_ll2
results_vis[0]['bodies']['candidate'][10,1] += y_offset_ll2
results_vis[0]['bodies']['candidate'][19,0] += x_offset_ll2
results_vis[0]['bodies']['candidate'][19,1] += y_offset_ll2
########right leg########
l_rl1_ref = ((ref_candidate[11][0] - ref_candidate[12][0]) ** 2 + (ref_candidate[11][1] - ref_candidate[12][1]) ** 2) ** 0.5
l_rl1_0 = ((candidate[11][0] - candidate[12][0]) ** 2 + (candidate[11][1] - candidate[12][1]) ** 2) ** 0.5
rl1_ratio = l_rl1_ref / l_rl1_0
x_offset_rl1 = (candidate[12][0]-candidate[11][0])*(rl1_ratio-1.)
y_offset_rl1 = (candidate[12][1]-candidate[11][1])*(rl1_ratio-1.)
results_vis[0]['bodies']['candidate'][12,0] += x_offset_rl1
results_vis[0]['bodies']['candidate'][12,1] += y_offset_rl1
results_vis[0]['bodies']['candidate'][13,0] += x_offset_rl1
results_vis[0]['bodies']['candidate'][13,1] += y_offset_rl1
results_vis[0]['bodies']['candidate'][18,0] += x_offset_rl1
results_vis[0]['bodies']['candidate'][18,1] += y_offset_rl1
l_rl2_ref = ((ref_candidate[12][0] - ref_candidate[13][0]) ** 2 + (ref_candidate[12][1] - ref_candidate[13][1]) ** 2) ** 0.5
l_rl2_0 = ((candidate[12][0] - candidate[13][0]) ** 2 + (candidate[12][1] - candidate[13][1]) ** 2) ** 0.5
rl2_ratio = l_rl2_ref / l_rl2_0
x_offset_rl2 = (candidate[13][0]-candidate[12][0])*(rl2_ratio-1.)
y_offset_rl2 = (candidate[13][1]-candidate[12][1])*(rl2_ratio-1.)
results_vis[0]['bodies']['candidate'][13,0] += x_offset_rl2
results_vis[0]['bodies']['candidate'][13,1] += y_offset_rl2
results_vis[0]['bodies']['candidate'][18,0] += x_offset_rl2
results_vis[0]['bodies']['candidate'][18,1] += y_offset_rl2
offset = ref_candidate[1] - results_vis[0]['bodies']['candidate'][1]
results_vis[0]['bodies']['candidate'] += offset[np.newaxis, :]
results_vis[0]['faces'] += offset[np.newaxis, np.newaxis, :]
results_vis[0]['hands'] += offset[np.newaxis, np.newaxis, :]
for i in range(1, len(results_vis)):
results_vis[i]['bodies']['candidate'][:,0] *= x_ratio
results_vis[i]['bodies']['candidate'][:,1] *= y_ratio
results_vis[i]['faces'][:,:,0] *= x_ratio
results_vis[i]['faces'][:,:,1] *= y_ratio
results_vis[i]['hands'][:,:,0] *= x_ratio
results_vis[i]['hands'][:,:,1] *= y_ratio
########neck########
x_offset_neck = (results_vis[i]['bodies']['candidate'][1][0]-results_vis[i]['bodies']['candidate'][0][0])*(1.-neck_ratio)
y_offset_neck = (results_vis[i]['bodies']['candidate'][1][1]-results_vis[i]['bodies']['candidate'][0][1])*(1.-neck_ratio)
results_vis[i]['bodies']['candidate'][0,0] += x_offset_neck
results_vis[i]['bodies']['candidate'][0,1] += y_offset_neck
results_vis[i]['bodies']['candidate'][14,0] += x_offset_neck
results_vis[i]['bodies']['candidate'][14,1] += y_offset_neck
results_vis[i]['bodies']['candidate'][15,0] += x_offset_neck
results_vis[i]['bodies']['candidate'][15,1] += y_offset_neck
results_vis[i]['bodies']['candidate'][16,0] += x_offset_neck
results_vis[i]['bodies']['candidate'][16,1] += y_offset_neck
results_vis[i]['bodies']['candidate'][17,0] += x_offset_neck
results_vis[i]['bodies']['candidate'][17,1] += y_offset_neck
########shoulder2########
x_offset_shoulder2 = (results_vis[i]['bodies']['candidate'][1][0]-results_vis[i]['bodies']['candidate'][2][0])*(1.-shoulder2_ratio)
y_offset_shoulder2 = (results_vis[i]['bodies']['candidate'][1][1]-results_vis[i]['bodies']['candidate'][2][1])*(1.-shoulder2_ratio)
results_vis[i]['bodies']['candidate'][2,0] += x_offset_shoulder2
results_vis[i]['bodies']['candidate'][2,1] += y_offset_shoulder2
results_vis[i]['bodies']['candidate'][3,0] += x_offset_shoulder2
results_vis[i]['bodies']['candidate'][3,1] += y_offset_shoulder2
results_vis[i]['bodies']['candidate'][4,0] += x_offset_shoulder2
results_vis[i]['bodies']['candidate'][4,1] += y_offset_shoulder2
results_vis[i]['hands'][1,:,0] += x_offset_shoulder2
results_vis[i]['hands'][1,:,1] += y_offset_shoulder2
########shoulder5########
x_offset_shoulder5 = (results_vis[i]['bodies']['candidate'][1][0]-results_vis[i]['bodies']['candidate'][5][0])*(1.-shoulder5_ratio)
y_offset_shoulder5 = (results_vis[i]['bodies']['candidate'][1][1]-results_vis[i]['bodies']['candidate'][5][1])*(1.-shoulder5_ratio)
results_vis[i]['bodies']['candidate'][5,0] += x_offset_shoulder5
results_vis[i]['bodies']['candidate'][5,1] += y_offset_shoulder5
results_vis[i]['bodies']['candidate'][6,0] += x_offset_shoulder5
results_vis[i]['bodies']['candidate'][6,1] += y_offset_shoulder5
results_vis[i]['bodies']['candidate'][7,0] += x_offset_shoulder5
results_vis[i]['bodies']['candidate'][7,1] += y_offset_shoulder5
results_vis[i]['hands'][0,:,0] += x_offset_shoulder5
results_vis[i]['hands'][0,:,1] += y_offset_shoulder5
########arm3########
x_offset_arm3 = (results_vis[i]['bodies']['candidate'][2][0]-results_vis[i]['bodies']['candidate'][3][0])*(1.-arm3_ratio)
y_offset_arm3 = (results_vis[i]['bodies']['candidate'][2][1]-results_vis[i]['bodies']['candidate'][3][1])*(1.-arm3_ratio)
results_vis[i]['bodies']['candidate'][3,0] += x_offset_arm3
results_vis[i]['bodies']['candidate'][3,1] += y_offset_arm3
results_vis[i]['bodies']['candidate'][4,0] += x_offset_arm3
results_vis[i]['bodies']['candidate'][4,1] += y_offset_arm3
results_vis[i]['hands'][1,:,0] += x_offset_arm3
results_vis[i]['hands'][1,:,1] += y_offset_arm3
########arm4########
x_offset_arm4 = (results_vis[i]['bodies']['candidate'][3][0]-results_vis[i]['bodies']['candidate'][4][0])*(1.-arm4_ratio)
y_offset_arm4 = (results_vis[i]['bodies']['candidate'][3][1]-results_vis[i]['bodies']['candidate'][4][1])*(1.-arm4_ratio)
results_vis[i]['bodies']['candidate'][4,0] += x_offset_arm4
results_vis[i]['bodies']['candidate'][4,1] += y_offset_arm4
results_vis[i]['hands'][1,:,0] += x_offset_arm4
results_vis[i]['hands'][1,:,1] += y_offset_arm4
########arm6########
x_offset_arm6 = (results_vis[i]['bodies']['candidate'][5][0]-results_vis[i]['bodies']['candidate'][6][0])*(1.-arm6_ratio)
y_offset_arm6 = (results_vis[i]['bodies']['candidate'][5][1]-results_vis[i]['bodies']['candidate'][6][1])*(1.-arm6_ratio)
results_vis[i]['bodies']['candidate'][6,0] += x_offset_arm6
results_vis[i]['bodies']['candidate'][6,1] += y_offset_arm6
results_vis[i]['bodies']['candidate'][7,0] += x_offset_arm6
results_vis[i]['bodies']['candidate'][7,1] += y_offset_arm6
results_vis[i]['hands'][0,:,0] += x_offset_arm6
results_vis[i]['hands'][0,:,1] += y_offset_arm6
########arm7########
x_offset_arm7 = (results_vis[i]['bodies']['candidate'][6][0]-results_vis[i]['bodies']['candidate'][7][0])*(1.-arm7_ratio)
y_offset_arm7 = (results_vis[i]['bodies']['candidate'][6][1]-results_vis[i]['bodies']['candidate'][7][1])*(1.-arm7_ratio)
results_vis[i]['bodies']['candidate'][7,0] += x_offset_arm7
results_vis[i]['bodies']['candidate'][7,1] += y_offset_arm7
results_vis[i]['hands'][0,:,0] += x_offset_arm7
results_vis[i]['hands'][0,:,1] += y_offset_arm7
########head14########
x_offset_head14 = (results_vis[i]['bodies']['candidate'][0][0]-results_vis[i]['bodies']['candidate'][14][0])*(1.-head14_ratio)
y_offset_head14 = (results_vis[i]['bodies']['candidate'][0][1]-results_vis[i]['bodies']['candidate'][14][1])*(1.-head14_ratio)
results_vis[i]['bodies']['candidate'][14,0] += x_offset_head14
results_vis[i]['bodies']['candidate'][14,1] += y_offset_head14
results_vis[i]['bodies']['candidate'][16,0] += x_offset_head14
results_vis[i]['bodies']['candidate'][16,1] += y_offset_head14
########head15########
x_offset_head15 = (results_vis[i]['bodies']['candidate'][0][0]-results_vis[i]['bodies']['candidate'][15][0])*(1.-head15_ratio)
y_offset_head15 = (results_vis[i]['bodies']['candidate'][0][1]-results_vis[i]['bodies']['candidate'][15][1])*(1.-head15_ratio)
results_vis[i]['bodies']['candidate'][15,0] += x_offset_head15
results_vis[i]['bodies']['candidate'][15,1] += y_offset_head15
results_vis[i]['bodies']['candidate'][17,0] += x_offset_head15
results_vis[i]['bodies']['candidate'][17,1] += y_offset_head15
########head16########
x_offset_head16 = (results_vis[i]['bodies']['candidate'][14][0]-results_vis[i]['bodies']['candidate'][16][0])*(1.-head16_ratio)
y_offset_head16 = (results_vis[i]['bodies']['candidate'][14][1]-results_vis[i]['bodies']['candidate'][16][1])*(1.-head16_ratio)
results_vis[i]['bodies']['candidate'][16,0] += x_offset_head16
results_vis[i]['bodies']['candidate'][16,1] += y_offset_head16
########head17########
x_offset_head17 = (results_vis[i]['bodies']['candidate'][15][0]-results_vis[i]['bodies']['candidate'][17][0])*(1.-head17_ratio)
y_offset_head17 = (results_vis[i]['bodies']['candidate'][15][1]-results_vis[i]['bodies']['candidate'][17][1])*(1.-head17_ratio)
results_vis[i]['bodies']['candidate'][17,0] += x_offset_head17
results_vis[i]['bodies']['candidate'][17,1] += y_offset_head17
# ########MovingAverage########
########left leg########
x_offset_ll1 = (results_vis[i]['bodies']['candidate'][9][0]-results_vis[i]['bodies']['candidate'][8][0])*(ll1_ratio-1.)
y_offset_ll1 = (results_vis[i]['bodies']['candidate'][9][1]-results_vis[i]['bodies']['candidate'][8][1])*(ll1_ratio-1.)
results_vis[i]['bodies']['candidate'][9,0] += x_offset_ll1
results_vis[i]['bodies']['candidate'][9,1] += y_offset_ll1
results_vis[i]['bodies']['candidate'][10,0] += x_offset_ll1
results_vis[i]['bodies']['candidate'][10,1] += y_offset_ll1
results_vis[i]['bodies']['candidate'][19,0] += x_offset_ll1
results_vis[i]['bodies']['candidate'][19,1] += y_offset_ll1
x_offset_ll2 = (results_vis[i]['bodies']['candidate'][10][0]-results_vis[i]['bodies']['candidate'][9][0])*(ll2_ratio-1.)
y_offset_ll2 = (results_vis[i]['bodies']['candidate'][10][1]-results_vis[i]['bodies']['candidate'][9][1])*(ll2_ratio-1.)
results_vis[i]['bodies']['candidate'][10,0] += x_offset_ll2
results_vis[i]['bodies']['candidate'][10,1] += y_offset_ll2
results_vis[i]['bodies']['candidate'][19,0] += x_offset_ll2
results_vis[i]['bodies']['candidate'][19,1] += y_offset_ll2
########right leg########
x_offset_rl1 = (results_vis[i]['bodies']['candidate'][12][0]-results_vis[i]['bodies']['candidate'][11][0])*(rl1_ratio-1.)
y_offset_rl1 = (results_vis[i]['bodies']['candidate'][12][1]-results_vis[i]['bodies']['candidate'][11][1])*(rl1_ratio-1.)
results_vis[i]['bodies']['candidate'][12,0] += x_offset_rl1
results_vis[i]['bodies']['candidate'][12,1] += y_offset_rl1
results_vis[i]['bodies']['candidate'][13,0] += x_offset_rl1
results_vis[i]['bodies']['candidate'][13,1] += y_offset_rl1
results_vis[i]['bodies']['candidate'][18,0] += x_offset_rl1
results_vis[i]['bodies']['candidate'][18,1] += y_offset_rl1
x_offset_rl2 = (results_vis[i]['bodies']['candidate'][13][0]-results_vis[i]['bodies']['candidate'][12][0])*(rl2_ratio-1.)
y_offset_rl2 = (results_vis[i]['bodies']['candidate'][13][1]-results_vis[i]['bodies']['candidate'][12][1])*(rl2_ratio-1.)
results_vis[i]['bodies']['candidate'][13,0] += x_offset_rl2
results_vis[i]['bodies']['candidate'][13,1] += y_offset_rl2
results_vis[i]['bodies']['candidate'][18,0] += x_offset_rl2
results_vis[i]['bodies']['candidate'][18,1] += y_offset_rl2
results_vis[i]['bodies']['candidate'] += offset[np.newaxis, :]
results_vis[i]['faces'] += offset[np.newaxis, np.newaxis, :]
results_vis[i]['hands'] += offset[np.newaxis, np.newaxis, :]
for i in range(len(results_vis)):
dwpose_woface, dwpose_wface = draw_pose(results_vis[i], H=768, W=512)
img_path = save_motion+'/' + str(i).zfill(4) + '.jpg'
cv2.imwrite(img_path, dwpose_woface)
dwpose_woface, dwpose_wface = draw_pose(pose_ref, H=768, W=512)
img_path = save_warp+'/' + 'ref_pose.jpg'
cv2.imwrite(img_path, dwpose_woface)
logger = get_logger('dw pose extraction')
if __name__=='__main__':
def parse_args():
parser = argparse.ArgumentParser(description="Simple example of a training script.")
parser.add_argument("--ref_name", type=str, default="data/images/IMG_20240514_104337.jpg",)
parser.add_argument("--source_video_paths", type=str, default="data/videos/source_video.mp4",)
parser.add_argument("--saved_pose_dir", type=str, default="data/saved_pose/IMG_20240514_104337",)
args = parser.parse_args()
return args
args = parse_args()
mp_main(args)
+108
View File
@@ -0,0 +1,108 @@
import os
import sys
import json
import torch
import imageio
import numpy as np
import os.path as osp
sys.path.insert(0, '/'.join(osp.realpath(__file__).split('/')[:-2]))
from thop import profile
from ptflops import get_model_complexity_info
import artist.data as data
from tools.modules.config import cfg
from tools.modules.unet.util import *
from utils.config import Config as pConfig
from utils.registry_class import ENGINE, MODEL
def save_temporal_key():
cfg_update = pConfig(load=True)
for k, v in cfg_update.cfg_dict.items():
if isinstance(v, dict) and k in cfg:
cfg[k].update(v)
else:
cfg[k] = v
model = MODEL.build(cfg.UNet)
temp_name = ''
temp_key_list = []
spth = 'workspace/module_list/UNetSD_I2V_vs_Text_temporal_key_list.json'
for name, module in model.named_modules():
if isinstance(module, (TemporalTransformer, TemporalTransformer_attemask, TemporalAttentionBlock, TemporalAttentionMultiBlock, TemporalConvBlock_v2, TemporalConvBlock)):
temp_name = name
print(f'Model: {name}')
elif isinstance(module, (ResidualBlock, ResBlock, SpatialTransformer, Upsample, Downsample)):
temp_name = ''
if hasattr(module, 'weight'):
if temp_name != '' and (temp_name in name):
temp_key_list.append(name)
print(f'{name}')
# print(name)
save_module_list = []
for k, p in model.named_parameters():
for item in temp_key_list:
if item in k:
print(f'{item} --> {k}')
save_module_list.append(k)
print(int(sum(p.numel() for k, p in model.named_parameters()) / (1024 ** 2)), 'M parameters')
# spth = 'workspace/module_list/{}'
json.dump(save_module_list, open(spth, 'w'))
a = 0
def save_spatial_key():
cfg_update = pConfig(load=True)
for k, v in cfg_update.cfg_dict.items():
if isinstance(v, dict) and k in cfg:
cfg[k].update(v)
else:
cfg[k] = v
model = MODEL.build(cfg.UNet)
temp_name = ''
temp_key_list = []
spth = 'workspace/module_list/UNetSD_I2V_HQ_P_spatial_key_list.json'
for name, module in model.named_modules():
if isinstance(module, (ResidualBlock, ResBlock, SpatialTransformer, Upsample, Downsample)):
temp_name = name
print(f'Model: {name}')
elif isinstance(module, (TemporalTransformer, TemporalTransformer_attemask, TemporalAttentionBlock, TemporalAttentionMultiBlock, TemporalConvBlock_v2, TemporalConvBlock)):
temp_name = ''
if hasattr(module, 'weight'):
if temp_name != '' and (temp_name in name):
temp_key_list.append(name)
print(f'{name}')
# print(name)
save_module_list = []
for k, p in model.named_parameters():
for item in temp_key_list:
if item in k:
print(f'{item} --> {k}')
save_module_list.append(k)
print(int(sum(p.numel() for k, p in model.named_parameters()) / (1024 ** 2)), 'M parameters')
# spth = 'workspace/module_list/{}'
json.dump(save_module_list, open(spth, 'w'))
a = 0
if __name__ == '__main__':
# save_temporal_key()
save_spatial_key()
# print([k for (k, _) in self.input_blocks.named_parameters()])
+95
View File
@@ -0,0 +1,95 @@
import os
import sys
import torch
import imageio
import numpy as np
import os.path as osp
sys.path.insert(0, '/'.join(osp.realpath(__file__).split('/')[:-2]))
from PIL import Image, ImageDraw, ImageFont
from einops import rearrange
from tools import *
import utils.transforms as data
from utils.seed import setup_seed
from tools.modules.config import cfg
from utils.config import Config as pConfig
from utils.registry_class import ENGINE, DATASETS, AUTO_ENCODER
def test_enc_dec(gpu=0):
setup_seed(0)
cfg_update = pConfig(load=True)
for k, v in cfg_update.cfg_dict.items():
if isinstance(v, dict) and k in cfg:
cfg[k].update(v)
else:
cfg[k] = v
save_dir = os.path.join('workspace/test_data/autoencoder', cfg.auto_encoder['type'])
os.system('rm -rf %s' % (save_dir))
os.makedirs(save_dir, exist_ok=True)
train_trans = data.Compose([
data.CenterCropWide(size=cfg.resolution),
data.ToTensor(),
data.Normalize(mean=cfg.mean, std=cfg.std)])
vit_trans = data.Compose([
data.CenterCropWide(size=(cfg.resolution[0], cfg.resolution[0])) if cfg.resolution[0]>cfg.vit_resolution[0] else data.CenterCropWide(size=cfg.vit_resolution),
data.Resize(cfg.vit_resolution),
data.ToTensor(),
data.Normalize(mean=cfg.vit_mean, std=cfg.vit_std)])
video_mean = torch.tensor(cfg.mean).view(1, -1, 1, 1) #n c f h w
video_std = torch.tensor(cfg.std).view(1, -1, 1, 1) #n c f h w
txt_size = cfg.resolution[1]
nc = int(38 * (txt_size / 256))
font = ImageFont.truetype('data/font/DejaVuSans.ttf', size=13)
dataset = DATASETS.build(cfg.vid_dataset, sample_fps=4, transforms=train_trans, vit_transforms=vit_trans)
print('There are %d videos' % (len(dataset)))
autoencoder = AUTO_ENCODER.build(cfg.auto_encoder)
autoencoder.eval() # freeze
for param in autoencoder.parameters():
param.requires_grad = False
autoencoder.to(gpu)
for idx, item in enumerate(dataset):
local_path = os.path.join(save_dir, '%04d.mp4' % idx)
# ref_frame, video_data, caption = item
ref_frame, vit_frame, video_data = item[:3]
video_data = video_data.to(gpu)
image_list = []
video_data_list = torch.chunk(video_data, video_data.shape[0]//cfg.chunk_size,dim=0)
with torch.no_grad():
decode_data = []
for chunk_data in video_data_list:
latent_z = autoencoder.encode_firsr_stage(chunk_data).detach()
# latent_z = get_first_stage_encoding(encoder_posterior).detach()
kwargs = {"timesteps": chunk_data.shape[0]}
recons_data = autoencoder.decode(latent_z, **kwargs)
vis_data = torch.cat([chunk_data, recons_data], dim=2).cpu()
vis_data = vis_data.mul_(video_std).add_(video_mean) # 8x3x16x256x384
vis_data = vis_data.cpu()
vis_data.clamp_(0, 1)
vis_data = vis_data.permute(0, 2, 3, 1)
vis_data = [(image.numpy() * 255).astype('uint8') for image in vis_data]
image_list.extend(vis_data)
num_image = len(image_list)
frame_dir = os.path.join(save_dir, 'temp')
os.makedirs(frame_dir, exist_ok=True)
for idx in range(num_image):
tpth = os.path.join(frame_dir, '%04d.png' % (idx+1))
cv2.imwrite(tpth, image_list[idx][:,:,::-1], [int(cv2.IMWRITE_JPEG_QUALITY), 100])
cmd = f'ffmpeg -y -f image2 -loglevel quiet -framerate 8 -i {frame_dir}/%04d.png -vcodec libx264 -crf 17 -pix_fmt yuv420p {local_path}'
os.system(cmd); os.system(f'rm -rf {frame_dir}')
if __name__ == '__main__':
test_enc_dec()
+152
View File
@@ -0,0 +1,152 @@
import os
import sys
import imageio
import numpy as np
import os.path as osp
sys.path.insert(0, '/'.join(osp.realpath(__file__).split('/')[:-2]))
from PIL import Image, ImageDraw, ImageFont
import torchvision.transforms as T
import utils.transforms as data
from tools.modules.config import cfg
from utils.config import Config as pConfig
from utils.registry_class import ENGINE, DATASETS
from tools import *
def test_video_dataset():
cfg_update = pConfig(load=True)
for k, v in cfg_update.cfg_dict.items():
if isinstance(v, dict) and k in cfg:
cfg[k].update(v)
else:
cfg[k] = v
exp_name = os.path.basename(cfg.cfg_file).split('.')[0]
save_dir = os.path.join('workspace', 'test_data/datasets', cfg.vid_dataset['type'], exp_name)
os.system('rm -rf %s' % (save_dir))
os.makedirs(save_dir, exist_ok=True)
train_trans = data.Compose([
data.CenterCropWide(size=cfg.resolution),
data.ToTensor(),
data.Normalize(mean=cfg.mean, std=cfg.std)])
vit_trans = T.Compose([
data.CenterCropWide(cfg.vit_resolution),
T.ToTensor(),
T.Normalize(mean=cfg.vit_mean, std=cfg.vit_std)])
video_mean = torch.tensor(cfg.mean).view(1, -1, 1, 1) #n c f h w
video_std = torch.tensor(cfg.std).view(1, -1, 1, 1) #n c f h w
img_mean = torch.tensor(cfg.mean).view(-1, 1, 1) # c f h w
img_std = torch.tensor(cfg.std).view(-1, 1, 1) # c f h w
vit_mean = torch.tensor(cfg.vit_mean).view(-1, 1, 1) # c f h w
vit_std = torch.tensor(cfg.vit_std).view(-1, 1, 1) # c f h w
txt_size = cfg.resolution[1]
nc = int(38 * (txt_size / 256))
font = ImageFont.truetype('data/font/DejaVuSans.ttf', size=13)
dataset = DATASETS.build(cfg.vid_dataset, sample_fps=cfg.sample_fps[0], transforms=train_trans, vit_transforms=vit_trans)
print('There are %d videos' % (len(dataset)))
for idx, item in enumerate(dataset):
ref_frame, vit_frame, video_data, caption, video_key = item
video_data = video_data.mul_(video_std).add_(video_mean)
video_data.clamp_(0, 1)
video_data = video_data.permute(0, 2, 3, 1)
video_data = [(image.numpy() * 255).astype('uint8') for image in video_data]
# Single Image
ref_frame = ref_frame.mul_(img_mean).add_(img_std)
ref_frame.clamp_(0, 1)
ref_frame = ref_frame.permute(1, 2, 0)
ref_frame = (ref_frame.numpy() * 255).astype('uint8')
# Text image
txt_img = Image.new("RGB", (txt_size, txt_size), color="white")
draw = ImageDraw.Draw(txt_img)
lines = "\n".join(caption[start:start + nc] for start in range(0, len(caption), nc))
draw.text((0, 0), lines, fill="black", font=font)
txt_img = np.array(txt_img)
video_data = [np.concatenate([ref_frame, u, txt_img], axis=1) for u in video_data]
spath = os.path.join(save_dir, '%04d.gif' % (idx))
imageio.mimwrite(spath, video_data, fps =8)
# if idx > 100: break
def test_vit_image(test_video_flag=True):
cfg_update = pConfig(load=True)
for k, v in cfg_update.cfg_dict.items():
if isinstance(v, dict) and k in cfg:
cfg[k].update(v)
else:
cfg[k] = v
exp_name = os.path.basename(cfg.cfg_file).split('.')[0]
save_dir = os.path.join('workspace', 'test_data/datasets', cfg.img_dataset['type'], exp_name)
os.system('rm -rf %s' % (save_dir))
os.makedirs(save_dir, exist_ok=True)
train_trans = data.Compose([
data.CenterCropWide(size=cfg.resolution),
data.ToTensor(),
data.Normalize(mean=cfg.mean, std=cfg.std)])
vit_trans = data.Compose([
data.CenterCropWide(cfg.resolution),
data.Resize(cfg.vit_resolution),
data.ToTensor(),
data.Normalize(mean=cfg.vit_mean, std=cfg.vit_std)])
img_mean = torch.tensor(cfg.mean).view(-1, 1, 1) # c f h w
img_std = torch.tensor(cfg.std).view(-1, 1, 1) # c f h w
vit_mean = torch.tensor(cfg.vit_mean).view(-1, 1, 1) # c f h w
vit_std = torch.tensor(cfg.vit_std).view(-1, 1, 1) # c f h w
txt_size = cfg.resolution[1]
nc = int(38 * (txt_size / 256))
font = ImageFont.truetype('artist/font/DejaVuSans.ttf', size=13)
dataset = DATASETS.build(cfg.img_dataset, transforms=train_trans, vit_transforms=vit_trans)
print('There are %d videos' % (len(dataset)))
for idx, item in enumerate(dataset):
ref_frame, vit_frame, video_data, caption, video_key = item
video_data = video_data.mul_(img_std).add_(img_mean)
video_data.clamp_(0, 1)
video_data = video_data.permute(0, 2, 3, 1)
video_data = [(image.numpy() * 255).astype('uint8') for image in video_data]
# Single Image
vit_frame = vit_frame.mul_(vit_std).add_(vit_mean)
vit_frame.clamp_(0, 1)
vit_frame = vit_frame.permute(1, 2, 0)
vit_frame = (vit_frame.numpy() * 255).astype('uint8')
zero_frame = np.zeros((cfg.resolution[1], cfg.resolution[1], 3), dtype=np.uint8)
zero_frame[:vit_frame.shape[0], :vit_frame.shape[1], :] = vit_frame
# Text image
txt_img = Image.new("RGB", (txt_size, txt_size), color="white")
draw = ImageDraw.Draw(txt_img)
lines = "\n".join(caption[start:start + nc] for start in range(0, len(caption), nc))
draw.text((0, 0), lines, fill="black", font=font)
txt_img = np.array(txt_img)
video_data = [np.concatenate([zero_frame, u, txt_img], axis=1) for u in video_data]
spath = os.path.join(save_dir, '%04d.gif' % (idx))
imageio.mimwrite(spath, video_data, fps =8)
# if idx > 100: break
if __name__ == '__main__':
# test_video_dataset()
test_vit_image()
+56
View File
@@ -0,0 +1,56 @@
import os
import sys
import torch
import imageio
import numpy as np
import os.path as osp
sys.path.insert(0, '/'.join(osp.realpath(__file__).split('/')[:-2]))
from thop import profile
from ptflops import get_model_complexity_info
import artist.data as data
from tools.modules.config import cfg
from utils.config import Config as pConfig
from utils.registry_class import ENGINE, MODEL
def test_model():
cfg_update = pConfig(load=True)
for k, v in cfg_update.cfg_dict.items():
if isinstance(v, dict) and k in cfg:
cfg[k].update(v)
else:
cfg[k] = v
model = MODEL.build(cfg.UNet)
print(int(sum(p.numel() for k, p in model.named_parameters()) / (1024 ** 2)), 'M parameters')
# state_dict = torch.load('cache/pretrain_model/jiuniu_0600000.pth', map_location='cpu')
# model.load_state_dict(state_dict, strict=False)
model = model.cuda()
x = torch.Tensor(1, 4, 16, 32, 56).cuda()
t = torch.Tensor(1).cuda()
sims = torch.Tensor(1, 32).cuda()
fps = torch.Tensor([8]).cuda()
y = torch.Tensor(1, 1, 1024).cuda()
image = torch.Tensor(1, 3, 256, 448).cuda()
ret = model(x=x, t=t, y=y, ori_img=image, sims=sims, fps=fps)
print('Out shape if {}'.format(ret.shape))
# flops, params = profile(model=model, inputs=(x, t, y, image, sims, fps))
# print('Model: {:.2f} GFLOPs and {:.2f}M parameters'.format(flops/1e9, params/1e6))
def prepare_input(resolution):
return dict(x=[x, t, y, image, sims, fps])
flops, params = get_model_complexity_info(model, (1, 4, 16, 32, 56),
input_constructor = prepare_input,
as_strings=True, print_per_layer_stat=True)
print(' - Flops: ' + flops)
print(' - Params: ' + params)
if __name__ == '__main__':
test_model()
+24
View File
@@ -0,0 +1,24 @@
import numpy as np
import cv2
cap = cv2.VideoCapture('workspace/img_dir/tst.mp4')
fourcc = cv2.VideoWriter_fourcc(*'H264')
ret, frame = cap.read()
vid_size = frame.shape[:2][::-1]
out = cv2.VideoWriter('workspace/img_dir/testwrite.mp4',fourcc, 8, vid_size)
out.write(frame)
while(cap.isOpened()):
ret, frame = cap.read()
if not ret: break
out.write(frame)
cap.release()
out.release()
+3
View File
@@ -0,0 +1,3 @@
from .datasets import *
from .modules import *
from .inferences import *
+2
View File
@@ -0,0 +1,2 @@
from .image_dataset import *
from .video_dataset import *
@@ -0,0 +1,86 @@
import os
import cv2
import torch
import random
import logging
import tempfile
import numpy as np
from copy import copy
from PIL import Image
from io import BytesIO
from torch.utils.data import Dataset
from utils.registry_class import DATASETS
@DATASETS.register_class()
class ImageDataset(Dataset):
def __init__(self,
data_list,
data_dir_list,
max_words=1000,
vit_resolution=[224, 224],
resolution=(384, 256),
max_frames=1,
transforms=None,
vit_transforms=None,
**kwargs):
self.max_frames = max_frames
self.resolution = resolution
self.transforms = transforms
self.vit_resolution = vit_resolution
self.vit_transforms = vit_transforms
image_list = []
for item_path, data_dir in zip(data_list, data_dir_list):
lines = open(item_path, 'r').readlines()
lines = [[data_dir, item.strip()] for item in lines]
image_list.extend(lines)
self.image_list = image_list
def __len__(self):
return len(self.image_list)
def __getitem__(self, index):
data_dir, file_path = self.image_list[index]
img_key = file_path.split('|||')[0]
try:
ref_frame, vit_frame, video_data, caption = self._get_image_data(data_dir, file_path)
except Exception as e:
logging.info('{} get frames failed... with error: {}'.format(img_key, e))
caption = ''
img_key = ''
ref_frame = torch.zeros(3, self.resolution[1], self.resolution[0])
vit_frame = torch.zeros(3, self.vit_resolution[1], self.vit_resolution[0])
video_data = torch.zeros(self.max_frames, 3, self.resolution[1], self.resolution[0])
return ref_frame, vit_frame, video_data, caption, img_key
def _get_image_data(self, data_dir, file_path):
frame_list = []
img_key, caption = file_path.split('|||')
file_path = os.path.join(data_dir, img_key)
for _ in range(5):
try:
image = Image.open(file_path)
if image.mode != 'RGB':
image = image.convert('RGB')
frame_list.append(image)
break
except Exception as e:
logging.info('{} read video frame failed with error: {}'.format(img_key, e))
continue
video_data = torch.zeros(self.max_frames, 3, self.resolution[1], self.resolution[0])
try:
if len(frame_list) > 0:
mid_frame = frame_list[0]
vit_frame = self.vit_transforms(mid_frame)
frame_tensor = self.transforms(frame_list)
video_data[:len(frame_list), ...] = frame_tensor
else:
vit_frame = torch.zeros(3, self.vit_resolution[1], self.vit_resolution[0])
except:
vit_frame = torch.zeros(3, self.vit_resolution[1], self.vit_resolution[0])
ref_frame = copy(video_data[0])
return ref_frame, vit_frame, video_data, caption
+118
View File
@@ -0,0 +1,118 @@
import os
import cv2
import json
import torch
import random
import logging
import tempfile
import numpy as np
from copy import copy
from PIL import Image
from torch.utils.data import Dataset
from utils.registry_class import DATASETS
@DATASETS.register_class()
class VideoDataset(Dataset):
def __init__(self,
data_list,
data_dir_list,
max_words=1000,
resolution=(384, 256),
vit_resolution=(224, 224),
max_frames=16,
sample_fps=8,
transforms=None,
vit_transforms=None,
get_first_frame=False,
**kwargs):
self.max_words = max_words
self.max_frames = max_frames
self.resolution = resolution
self.vit_resolution = vit_resolution
self.sample_fps = sample_fps
self.transforms = transforms
self.vit_transforms = vit_transforms
self.get_first_frame = get_first_frame
image_list = []
for item_path, data_dir in zip(data_list, data_dir_list):
lines = open(item_path, 'r').readlines()
lines = [[data_dir, item] for item in lines]
image_list.extend(lines)
self.image_list = image_list
def __getitem__(self, index):
data_dir, file_path = self.image_list[index]
video_key = file_path.split('|||')[0]
try:
ref_frame, vit_frame, video_data, caption = self._get_video_data(data_dir, file_path)
except Exception as e:
logging.info('{} get frames failed... with error: {}'.format(video_key, e))
caption = ''
video_key = ''
ref_frame = torch.zeros(3, self.resolution[1], self.resolution[0])
vit_frame = torch.zeros(3, self.vit_resolution[1], self.vit_resolution[0])
video_data = torch.zeros(self.max_frames, 3, self.resolution[1], self.resolution[0])
return ref_frame, vit_frame, video_data, caption, video_key
def _get_video_data(self, data_dir, file_path):
video_key, caption = file_path.split('|||')
file_path = os.path.join(data_dir, video_key)
for _ in range(5):
try:
capture = cv2.VideoCapture(file_path)
_fps = capture.get(cv2.CAP_PROP_FPS)
_total_frame_num = capture.get(cv2.CAP_PROP_FRAME_COUNT)
stride = round(_fps / self.sample_fps)
cover_frame_num = (stride * self.max_frames)
if _total_frame_num < cover_frame_num + 5:
start_frame = 0
end_frame = _total_frame_num
else:
start_frame = random.randint(0, _total_frame_num-cover_frame_num-5)
end_frame = start_frame + cover_frame_num
pointer, frame_list = 0, []
while(True):
ret, frame = capture.read()
pointer +=1
if (not ret) or (frame is None): break
if pointer < start_frame: continue
if pointer >= end_frame - 1: break
if (pointer - start_frame) % stride == 0:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = Image.fromarray(frame)
frame_list.append(frame)
break
except Exception as e:
logging.info('{} read video frame failed with error: {}'.format(video_key, e))
continue
video_data = torch.zeros(self.max_frames, 3, self.resolution[1], self.resolution[0])
if self.get_first_frame:
ref_idx = 0
else:
ref_idx = int(len(frame_list)/2)
try:
if len(frame_list)>0:
mid_frame = copy(frame_list[ref_idx])
vit_frame = self.vit_transforms(mid_frame)
frames = self.transforms(frame_list)
video_data[:len(frame_list), ...] = frames
else:
vit_frame = torch.zeros(3, self.vit_resolution[1], self.vit_resolution[0])
except:
vit_frame = torch.zeros(3, self.vit_resolution[1], self.vit_resolution[0])
ref_frame = copy(frames[ref_idx])
return ref_frame, vit_frame, video_data, caption
def __len__(self):
return len(self.image_list)
+2
View File
@@ -0,0 +1,2 @@
from .inference_unianimate_entrance import *
from .inference_unianimate_long_entrance import *
@@ -0,0 +1,496 @@
'''
/*
*Copyright (c) 2021, Alibaba Group;
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/
'''
import os
import re
import os.path as osp
import sys
sys.path.insert(0, '/'.join(osp.realpath(__file__).split('/')[:-4]))
import json
import math
import torch
import pynvml
import logging
import cv2
import numpy as np
from PIL import Image
from tqdm import tqdm
import torch.cuda.amp as amp
from importlib import reload
import torch.distributed as dist
import torch.multiprocessing as mp
import random
from einops import rearrange
import torchvision.transforms as T
import torchvision.transforms.functional as TF
from torch.nn.parallel import DistributedDataParallel
import utils.transforms as data
from ..modules.config import cfg
from utils.seed import setup_seed
from utils.multi_port import find_free_port
from utils.assign_cfg import assign_signle_cfg
from utils.distributed import generalized_all_gather, all_reduce
from utils.video_op import save_i2vgen_video, save_t2vhigen_video_safe, save_video_multiple_conditions_not_gif_horizontal_3col
from tools.modules.autoencoder import get_first_stage_encoding
from utils.registry_class import INFER_ENGINE, MODEL, EMBEDDER, AUTO_ENCODER, DIFFUSION
from copy import copy
import cv2
@INFER_ENGINE.register_function()
def inference_unianimate_entrance(cfg_update, **kwargs):
for k, v in cfg_update.items():
if isinstance(v, dict) and k in cfg:
cfg[k].update(v)
else:
cfg[k] = v
if not 'MASTER_ADDR' in os.environ:
os.environ['MASTER_ADDR']='localhost'
os.environ['MASTER_PORT']= find_free_port()
cfg.pmi_rank = int(os.getenv('RANK', 0))
cfg.pmi_world_size = int(os.getenv('WORLD_SIZE', 1))
if cfg.debug:
cfg.gpus_per_machine = 1
cfg.world_size = 1
else:
cfg.gpus_per_machine = torch.cuda.device_count()
cfg.world_size = cfg.pmi_world_size * cfg.gpus_per_machine
if cfg.world_size == 1:
worker(0, cfg, cfg_update)
else:
mp.spawn(worker, nprocs=cfg.gpus_per_machine, args=(cfg, cfg_update))
return cfg
def make_masked_images(imgs, masks):
masked_imgs = []
for i, mask in enumerate(masks):
# concatenation
masked_imgs.append(torch.cat([imgs[i] * (1 - mask), (1 - mask)], dim=1))
return torch.stack(masked_imgs, dim=0)
def load_video_frames(ref_image_path, pose_file_path, train_trans, vit_transforms, train_trans_pose, max_frames=32, frame_interval = 1, resolution=[512, 768], get_first_frame=True, vit_resolution=[224, 224]):
for _ in range(5):
try:
dwpose_all = {}
frames_all = {}
for ii_index in os.listdir(pose_file_path):
if ii_index != "ref_pose.jpg":
dwpose_all[ii_index] = Image.open(pose_file_path+"/"+ii_index)
frames_all[ii_index] = Image.fromarray(cv2.cvtColor(cv2.imread(ref_image_path),cv2.COLOR_BGR2RGB))
# frames_all[ii_index] = Image.open(ref_image_path)
pose_ref = Image.open(os.path.join(pose_file_path, "ref_pose.jpg"))
first_eq_ref = False
# sample max_frames poses for video generation
stride = frame_interval
_total_frame_num = len(frames_all)
cover_frame_num = (stride * (max_frames-1)+1)
if _total_frame_num < cover_frame_num:
print('_total_frame_num is smaller than cover_frame_num, the sampled frame interval is changed')
start_frame = 0 # we set start_frame = 0 because the pose alignment is performed on the first frame
end_frame = _total_frame_num
stride = max((_total_frame_num-1//(max_frames-1)),1)
end_frame = stride*max_frames
else:
start_frame = 0 # we set start_frame = 0 because the pose alignment is performed on the first frame
end_frame = start_frame + cover_frame_num
frame_list = []
dwpose_list = []
random_ref_frame = frames_all[list(frames_all.keys())[0]]
if random_ref_frame.mode != 'RGB':
random_ref_frame = random_ref_frame.convert('RGB')
random_ref_dwpose = pose_ref
if random_ref_dwpose.mode != 'RGB':
random_ref_dwpose = random_ref_dwpose.convert('RGB')
for i_index in range(start_frame, end_frame, stride):
if i_index == start_frame and first_eq_ref:
i_key = list(frames_all.keys())[i_index]
i_frame = frames_all[i_key]
if i_frame.mode != 'RGB':
i_frame = i_frame.convert('RGB')
i_dwpose = frames_pose_ref
if i_dwpose.mode != 'RGB':
i_dwpose = i_dwpose.convert('RGB')
frame_list.append(i_frame)
dwpose_list.append(i_dwpose)
else:
# added
if first_eq_ref:
i_index = i_index - stride
i_key = list(frames_all.keys())[i_index]
i_frame = frames_all[i_key]
if i_frame.mode != 'RGB':
i_frame = i_frame.convert('RGB')
i_dwpose = dwpose_all[i_key]
if i_dwpose.mode != 'RGB':
i_dwpose = i_dwpose.convert('RGB')
frame_list.append(i_frame)
dwpose_list.append(i_dwpose)
have_frames = len(frame_list)>0
middle_indix = 0
if have_frames:
ref_frame = frame_list[middle_indix]
vit_frame = vit_transforms(ref_frame)
random_ref_frame_tmp = train_trans_pose(random_ref_frame)
random_ref_dwpose_tmp = train_trans_pose(random_ref_dwpose)
misc_data_tmp = torch.stack([train_trans_pose(ss) for ss in frame_list], dim=0)
video_data_tmp = torch.stack([train_trans(ss) for ss in frame_list], dim=0)
dwpose_data_tmp = torch.stack([train_trans_pose(ss) for ss in dwpose_list], dim=0)
video_data = torch.zeros(max_frames, 3, resolution[1], resolution[0])
dwpose_data = torch.zeros(max_frames, 3, resolution[1], resolution[0])
misc_data = torch.zeros(max_frames, 3, resolution[1], resolution[0])
random_ref_frame_data = torch.zeros(max_frames, 3, resolution[1], resolution[0]) # [32, 3, 512, 768]
random_ref_dwpose_data = torch.zeros(max_frames, 3, resolution[1], resolution[0])
if have_frames:
video_data[:len(frame_list), ...] = video_data_tmp
misc_data[:len(frame_list), ...] = misc_data_tmp
dwpose_data[:len(frame_list), ...] = dwpose_data_tmp
random_ref_frame_data[:,...] = random_ref_frame_tmp
random_ref_dwpose_data[:,...] = random_ref_dwpose_tmp
break
except Exception as e:
logging.info('{} read video frame failed with error: {}'.format(pose_file_path, e))
continue
return vit_frame, video_data, misc_data, dwpose_data, random_ref_frame_data, random_ref_dwpose_data
def worker(gpu, cfg, cfg_update):
'''
Inference worker for each gpu
'''
for k, v in cfg_update.items():
if isinstance(v, dict) and k in cfg:
cfg[k].update(v)
else:
cfg[k] = v
cfg.gpu = gpu
cfg.seed = int(cfg.seed)
cfg.rank = cfg.pmi_rank * cfg.gpus_per_machine + gpu
setup_seed(cfg.seed + cfg.rank)
if not cfg.debug:
torch.cuda.set_device(gpu)
torch.backends.cudnn.benchmark = True
dist.init_process_group(backend='nccl', world_size=cfg.world_size, rank=cfg.rank)
# [Log] Save logging and make log dir
log_dir = generalized_all_gather(cfg.log_dir)[0]
inf_name = osp.basename(cfg.cfg_file).split('.')[0]
test_model = osp.basename(cfg.test_model).split('.')[0].split('_')[-1]
cfg.log_dir = osp.join(cfg.log_dir, '%s' % (inf_name))
os.makedirs(cfg.log_dir, exist_ok=True)
log_file = osp.join(cfg.log_dir, 'log_%02d.txt' % (cfg.rank))
cfg.log_file = log_file
reload(logging)
logging.basicConfig(
level=logging.INFO,
format='[%(asctime)s] %(levelname)s: %(message)s',
handlers=[
logging.FileHandler(filename=log_file),
logging.StreamHandler(stream=sys.stdout)])
logging.info(cfg)
logging.info(f"Running UniAnimate inference on gpu {gpu}")
# [Diffusion]
diffusion = DIFFUSION.build(cfg.Diffusion)
# [Data] Data Transform
train_trans = data.Compose([
data.Resize(cfg.resolution),
data.ToTensor(),
data.Normalize(mean=cfg.mean, std=cfg.std)
])
train_trans_pose = data.Compose([
data.Resize(cfg.resolution),
data.ToTensor(),
]
)
vit_transforms = T.Compose([
data.Resize(cfg.vit_resolution),
T.ToTensor(),
T.Normalize(mean=cfg.vit_mean, std=cfg.vit_std)])
# [Model] embedder
clip_encoder = EMBEDDER.build(cfg.embedder)
clip_encoder.model.to(gpu)
with torch.no_grad():
_, _, zero_y = clip_encoder(text="")
# [Model] auotoencoder
autoencoder = AUTO_ENCODER.build(cfg.auto_encoder)
autoencoder.eval() # freeze
for param in autoencoder.parameters():
param.requires_grad = False
autoencoder.cuda()
# [Model] UNet
if "config" in cfg.UNet:
cfg.UNet["config"] = cfg
cfg.UNet["zero_y"] = zero_y
model = MODEL.build(cfg.UNet)
state_dict = torch.load(cfg.test_model, map_location='cpu')
if 'state_dict' in state_dict:
state_dict = state_dict['state_dict']
if 'step' in state_dict:
resume_step = state_dict['step']
else:
resume_step = 0
status = model.load_state_dict(state_dict, strict=True)
logging.info('Load model from {} with status {}'.format(cfg.test_model, status))
model = model.to(gpu)
model.eval()
if hasattr(cfg, "CPU_CLIP_VAE") and cfg.CPU_CLIP_VAE:
model.to(torch.float16)
else:
model = DistributedDataParallel(model, device_ids=[gpu]) if not cfg.debug else model
torch.cuda.empty_cache()
test_list = cfg.test_list_path
num_videos = len(test_list)
logging.info(f'There are {num_videos} videos. with {cfg.round} times')
# test_list = [item for item in test_list for _ in range(cfg.round)]
test_list = [item for _ in range(cfg.round) for item in test_list]
for idx, file_path in enumerate(test_list):
cfg.frame_interval, ref_image_key, pose_seq_key = file_path[0], file_path[1], file_path[2]
manual_seed = int(cfg.seed + cfg.rank + idx//num_videos)
setup_seed(manual_seed)
logging.info(f"[{idx}]/[{len(test_list)}] Begin to sample {ref_image_key}, pose sequence from {pose_seq_key} init seed {manual_seed} ...")
vit_frame, video_data, misc_data, dwpose_data, random_ref_frame_data, random_ref_dwpose_data = load_video_frames(ref_image_key, pose_seq_key, train_trans, vit_transforms, train_trans_pose, max_frames=cfg.max_frames, frame_interval =cfg.frame_interval, resolution=cfg.resolution)
misc_data = misc_data.unsqueeze(0).to(gpu)
vit_frame = vit_frame.unsqueeze(0).to(gpu)
dwpose_data = dwpose_data.unsqueeze(0).to(gpu)
random_ref_frame_data = random_ref_frame_data.unsqueeze(0).to(gpu)
random_ref_dwpose_data = random_ref_dwpose_data.unsqueeze(0).to(gpu)
### save for visualization
misc_backups = copy(misc_data)
frames_num = misc_data.shape[1]
misc_backups = rearrange(misc_backups, 'b f c h w -> b c f h w')
mv_data_video = []
### local image (first frame)
image_local = []
if 'local_image' in cfg.video_compositions:
frames_num = misc_data.shape[1]
bs_vd_local = misc_data.shape[0]
image_local = misc_data[:,:1].clone().repeat(1,frames_num,1,1,1)
image_local_clone = rearrange(image_local, 'b f c h w -> b c f h w', b = bs_vd_local)
image_local = rearrange(image_local, 'b f c h w -> b c f h w', b = bs_vd_local)
if hasattr(cfg, "latent_local_image") and cfg.latent_local_image:
with torch.no_grad():
temporal_length = frames_num
encoder_posterior = autoencoder.encode(video_data[:,0])
local_image_data = get_first_stage_encoding(encoder_posterior).detach()
image_local = local_image_data.unsqueeze(1).repeat(1,temporal_length,1,1,1) # [10, 16, 4, 64, 40]
### encode the video_data
bs_vd = misc_data.shape[0]
misc_data = rearrange(misc_data, 'b f c h w -> (b f) c h w')
misc_data_list = torch.chunk(misc_data, misc_data.shape[0]//cfg.chunk_size,dim=0)
with torch.no_grad():
random_ref_frame = []
if 'randomref' in cfg.video_compositions:
random_ref_frame_clone = rearrange(random_ref_frame_data, 'b f c h w -> b c f h w')
if hasattr(cfg, "latent_random_ref") and cfg.latent_random_ref:
temporal_length = random_ref_frame_data.shape[1]
encoder_posterior = autoencoder.encode(random_ref_frame_data[:,0].sub(0.5).div_(0.5))
random_ref_frame_data = get_first_stage_encoding(encoder_posterior).detach()
random_ref_frame_data = random_ref_frame_data.unsqueeze(1).repeat(1,temporal_length,1,1,1) # [10, 16, 4, 64, 40]
random_ref_frame = rearrange(random_ref_frame_data, 'b f c h w -> b c f h w')
if 'dwpose' in cfg.video_compositions:
bs_vd_local = dwpose_data.shape[0]
dwpose_data_clone = rearrange(dwpose_data.clone(), 'b f c h w -> b c f h w', b = bs_vd_local)
if 'randomref_pose' in cfg.video_compositions:
dwpose_data = torch.cat([random_ref_dwpose_data[:,:1], dwpose_data], dim=1)
dwpose_data = rearrange(dwpose_data, 'b f c h w -> b c f h w', b = bs_vd_local)
y_visual = []
if 'image' in cfg.video_compositions:
with torch.no_grad():
vit_frame = vit_frame.squeeze(1)
y_visual = clip_encoder.encode_image(vit_frame).unsqueeze(1) # [60, 1024]
y_visual0 = y_visual.clone()
with amp.autocast(enabled=True):
pynvml.nvmlInit()
handle=pynvml.nvmlDeviceGetHandleByIndex(0)
meminfo=pynvml.nvmlDeviceGetMemoryInfo(handle)
cur_seed = torch.initial_seed()
logging.info(f"Current seed {cur_seed} ...")
noise = torch.randn([1, 4, cfg.max_frames, int(cfg.resolution[1]/cfg.scale), int(cfg.resolution[0]/cfg.scale)])
noise = noise.to(gpu)
if hasattr(cfg.Diffusion, "noise_strength"):
b, c, f, _, _= noise.shape
offset_noise = torch.randn(b, c, f, 1, 1, device=noise.device)
noise = noise + cfg.Diffusion.noise_strength * offset_noise
# construct model inputs (CFG)
full_model_kwargs=[{
'y': None,
"local_image": None if len(image_local) == 0 else image_local[:],
'image': None if len(y_visual) == 0 else y_visual0[:],
'dwpose': None if len(dwpose_data) == 0 else dwpose_data[:],
'randomref': None if len(random_ref_frame) == 0 else random_ref_frame[:],
},
{
'y': None,
"local_image": None,
'image': None,
'randomref': None,
'dwpose': None,
}]
# for visualization
full_model_kwargs_vis =[{
'y': None,
"local_image": None if len(image_local) == 0 else image_local_clone[:],
'image': None,
'dwpose': None if len(dwpose_data_clone) == 0 else dwpose_data_clone[:],
'randomref': None if len(random_ref_frame) == 0 else random_ref_frame_clone[:, :3],
},
{
'y': None,
"local_image": None,
'image': None,
'randomref': None,
'dwpose': None,
}]
partial_keys = [
['image', 'randomref', "dwpose"],
]
if hasattr(cfg, "partial_keys") and cfg.partial_keys:
partial_keys = cfg.partial_keys
for partial_keys_one in partial_keys:
model_kwargs_one = prepare_model_kwargs(partial_keys = partial_keys_one,
full_model_kwargs = full_model_kwargs,
use_fps_condition = cfg.use_fps_condition)
model_kwargs_one_vis = prepare_model_kwargs(partial_keys = partial_keys_one,
full_model_kwargs = full_model_kwargs_vis,
use_fps_condition = cfg.use_fps_condition)
noise_one = noise
if hasattr(cfg, "CPU_CLIP_VAE") and cfg.CPU_CLIP_VAE:
clip_encoder.cpu() # add this line
autoencoder.cpu() # add this line
torch.cuda.empty_cache() # add this line
video_data = diffusion.ddim_sample_loop(
noise=noise_one,
model=model.eval(),
model_kwargs=model_kwargs_one,
guide_scale=cfg.guide_scale,
ddim_timesteps=cfg.ddim_timesteps,
eta=0.0)
if hasattr(cfg, "CPU_CLIP_VAE") and cfg.CPU_CLIP_VAE:
# if run forward of autoencoder or clip_encoder second times, load them again
clip_encoder.cuda()
autoencoder.cuda()
video_data = 1. / cfg.scale_factor * video_data
video_data = rearrange(video_data, 'b c f h w -> (b f) c h w')
chunk_size = min(cfg.decoder_bs, video_data.shape[0])
video_data_list = torch.chunk(video_data, video_data.shape[0]//chunk_size, dim=0)
decode_data = []
for vd_data in video_data_list:
gen_frames = autoencoder.decode(vd_data)
decode_data.append(gen_frames)
video_data = torch.cat(decode_data, dim=0)
video_data = rearrange(video_data, '(b f) c h w -> b c f h w', b = cfg.batch_size).float()
text_size = cfg.resolution[-1]
cap_name = re.sub(r'[^\w\s]', '', ref_image_key.split("/")[-1].split('.')[0]) # .replace(' ', '_')
name = f'seed_{cur_seed}'
for ii in partial_keys_one:
name = name + "_" + ii
file_name = f'rank_{cfg.world_size:02d}_{cfg.rank:02d}_{idx:02d}_{name}_{cap_name}_{cfg.resolution[1]}x{cfg.resolution[0]}.mp4'
local_path = os.path.join(cfg.log_dir, f'{file_name}')
os.makedirs(os.path.dirname(local_path), exist_ok=True)
captions = "human"
del model_kwargs_one_vis[0][list(model_kwargs_one_vis[0].keys())[0]]
del model_kwargs_one_vis[1][list(model_kwargs_one_vis[1].keys())[0]]
save_video_multiple_conditions_not_gif_horizontal_3col(local_path, video_data.cpu(), model_kwargs_one_vis, misc_backups,
cfg.mean, cfg.std, nrow=1, save_fps=cfg.save_fps)
# try:
# save_t2vhigen_video_safe(local_path, video_data.cpu(), captions, cfg.mean, cfg.std, text_size)
# logging.info('Save video to dir %s:' % (local_path))
# except Exception as e:
# logging.info(f'Step: save text or video error with {e}')
logging.info('Congratulations! The inference is completed!')
# synchronize to finish some processes
if not cfg.debug:
torch.cuda.synchronize()
dist.barrier()
def prepare_model_kwargs(partial_keys, full_model_kwargs, use_fps_condition=False):
if use_fps_condition is True:
partial_keys.append('fps')
partial_model_kwargs = [{}, {}]
for partial_key in partial_keys:
partial_model_kwargs[0][partial_key] = full_model_kwargs[0][partial_key]
partial_model_kwargs[1][partial_key] = full_model_kwargs[1][partial_key]
return partial_model_kwargs
@@ -0,0 +1,501 @@
'''
/*
*Copyright (c) 2021, Alibaba Group;
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/
'''
import os
import re
import os.path as osp
import sys
sys.path.insert(0, '/'.join(osp.realpath(__file__).split('/')[:-4]))
import json
import math
import torch
import pynvml
import logging
import cv2
import numpy as np
from PIL import Image
from tqdm import tqdm
import torch.cuda.amp as amp
from importlib import reload
import torch.distributed as dist
import torch.multiprocessing as mp
import random
from einops import rearrange
import torchvision.transforms as T
import torchvision.transforms.functional as TF
from torch.nn.parallel import DistributedDataParallel
import utils.transforms as data
from ..modules.config import cfg
from utils.seed import setup_seed
from utils.multi_port import find_free_port
from utils.assign_cfg import assign_signle_cfg
from utils.distributed import generalized_all_gather, all_reduce
from utils.video_op import save_i2vgen_video, save_t2vhigen_video_safe, save_video_multiple_conditions_not_gif_horizontal_3col
from tools.modules.autoencoder import get_first_stage_encoding
from utils.registry_class import INFER_ENGINE, MODEL, EMBEDDER, AUTO_ENCODER, DIFFUSION
from copy import copy
import cv2
@INFER_ENGINE.register_function()
def inference_unianimate_long_entrance(cfg_update, **kwargs):
for k, v in cfg_update.items():
if isinstance(v, dict) and k in cfg:
cfg[k].update(v)
else:
cfg[k] = v
if not 'MASTER_ADDR' in os.environ:
os.environ['MASTER_ADDR']='localhost'
os.environ['MASTER_PORT']= find_free_port()
cfg.pmi_rank = int(os.getenv('RANK', 0))
cfg.pmi_world_size = int(os.getenv('WORLD_SIZE', 1))
if cfg.debug:
cfg.gpus_per_machine = 1
cfg.world_size = 1
else:
cfg.gpus_per_machine = torch.cuda.device_count()
cfg.world_size = cfg.pmi_world_size * cfg.gpus_per_machine
if cfg.world_size == 1:
worker(0, cfg, cfg_update)
else:
mp.spawn(worker, nprocs=cfg.gpus_per_machine, args=(cfg, cfg_update))
return cfg
def make_masked_images(imgs, masks):
masked_imgs = []
for i, mask in enumerate(masks):
# concatenation
masked_imgs.append(torch.cat([imgs[i] * (1 - mask), (1 - mask)], dim=1))
return torch.stack(masked_imgs, dim=0)
def load_video_frames(ref_image_path, pose_file_path, train_trans, vit_transforms, train_trans_pose, max_frames=32, frame_interval = 1, resolution=[512, 768], get_first_frame=True, vit_resolution=[224, 224]):
for _ in range(5):
try:
dwpose_all = {}
frames_all = {}
for ii_index in os.listdir(pose_file_path):
if ii_index != "ref_pose.jpg":
dwpose_all[ii_index] = Image.open(pose_file_path+"/"+ii_index)
frames_all[ii_index] = Image.fromarray(cv2.cvtColor(cv2.imread(ref_image_path),cv2.COLOR_BGR2RGB))
# frames_all[ii_index] = Image.open(ref_image_path)
pose_ref = Image.open(os.path.join(pose_file_path, "ref_pose.jpg"))
first_eq_ref = False
# sample max_frames poses for video generation
stride = frame_interval
_total_frame_num = len(frames_all)
if max_frames == "None":
max_frames = (_total_frame_num-1)//frame_interval + 1
cover_frame_num = (stride * (max_frames-1)+1)
if _total_frame_num < cover_frame_num:
print('_total_frame_num is smaller than cover_frame_num, the sampled frame interval is changed')
start_frame = 0 # we set start_frame = 0 because the pose alignment is performed on the first frame
end_frame = _total_frame_num
stride = max((_total_frame_num-1//(max_frames-1)),1)
end_frame = stride*max_frames
else:
start_frame = 0 # we set start_frame = 0 because the pose alignment is performed on the first frame
end_frame = start_frame + cover_frame_num
frame_list = []
dwpose_list = []
random_ref_frame = frames_all[list(frames_all.keys())[0]]
if random_ref_frame.mode != 'RGB':
random_ref_frame = random_ref_frame.convert('RGB')
random_ref_dwpose = pose_ref
if random_ref_dwpose.mode != 'RGB':
random_ref_dwpose = random_ref_dwpose.convert('RGB')
for i_index in range(start_frame, end_frame, stride):
if i_index == start_frame and first_eq_ref:
i_key = list(frames_all.keys())[i_index]
i_frame = frames_all[i_key]
if i_frame.mode != 'RGB':
i_frame = i_frame.convert('RGB')
i_dwpose = frames_pose_ref
if i_dwpose.mode != 'RGB':
i_dwpose = i_dwpose.convert('RGB')
frame_list.append(i_frame)
dwpose_list.append(i_dwpose)
else:
# added
if first_eq_ref:
i_index = i_index - stride
i_key = list(frames_all.keys())[i_index]
i_frame = frames_all[i_key]
if i_frame.mode != 'RGB':
i_frame = i_frame.convert('RGB')
i_dwpose = dwpose_all[i_key]
if i_dwpose.mode != 'RGB':
i_dwpose = i_dwpose.convert('RGB')
frame_list.append(i_frame)
dwpose_list.append(i_dwpose)
have_frames = len(frame_list)>0
middle_indix = 0
if have_frames:
ref_frame = frame_list[middle_indix]
vit_frame = vit_transforms(ref_frame)
random_ref_frame_tmp = train_trans_pose(random_ref_frame)
random_ref_dwpose_tmp = train_trans_pose(random_ref_dwpose)
misc_data_tmp = torch.stack([train_trans_pose(ss) for ss in frame_list], dim=0)
video_data_tmp = torch.stack([train_trans(ss) for ss in frame_list], dim=0)
dwpose_data_tmp = torch.stack([train_trans_pose(ss) for ss in dwpose_list], dim=0)
video_data = torch.zeros(max_frames, 3, resolution[1], resolution[0])
dwpose_data = torch.zeros(max_frames, 3, resolution[1], resolution[0])
misc_data = torch.zeros(max_frames, 3, resolution[1], resolution[0])
random_ref_frame_data = torch.zeros(max_frames, 3, resolution[1], resolution[0]) # [32, 3, 512, 768]
random_ref_dwpose_data = torch.zeros(max_frames, 3, resolution[1], resolution[0])
if have_frames:
video_data[:len(frame_list), ...] = video_data_tmp
misc_data[:len(frame_list), ...] = misc_data_tmp
dwpose_data[:len(frame_list), ...] = dwpose_data_tmp
random_ref_frame_data[:,...] = random_ref_frame_tmp
random_ref_dwpose_data[:,...] = random_ref_dwpose_tmp
break
except Exception as e:
logging.info('{} read video frame failed with error: {}'.format(pose_file_path, e))
continue
return vit_frame, video_data, misc_data, dwpose_data, random_ref_frame_data, random_ref_dwpose_data, max_frames
def worker(gpu, cfg, cfg_update):
'''
Inference worker for each gpu
'''
for k, v in cfg_update.items():
if isinstance(v, dict) and k in cfg:
cfg[k].update(v)
else:
cfg[k] = v
cfg.gpu = gpu
cfg.seed = int(cfg.seed)
cfg.rank = cfg.pmi_rank * cfg.gpus_per_machine + gpu
setup_seed(cfg.seed + cfg.rank)
if not cfg.debug:
torch.cuda.set_device(gpu)
torch.backends.cudnn.benchmark = True
dist.init_process_group(backend='nccl', world_size=cfg.world_size, rank=cfg.rank)
# [Log] Save logging and make log dir
log_dir = generalized_all_gather(cfg.log_dir)[0]
inf_name = osp.basename(cfg.cfg_file).split('.')[0]
test_model = osp.basename(cfg.test_model).split('.')[0].split('_')[-1]
cfg.log_dir = osp.join(cfg.log_dir, '%s' % (inf_name))
os.makedirs(cfg.log_dir, exist_ok=True)
log_file = osp.join(cfg.log_dir, 'log_%02d.txt' % (cfg.rank))
cfg.log_file = log_file
reload(logging)
logging.basicConfig(
level=logging.INFO,
format='[%(asctime)s] %(levelname)s: %(message)s',
handlers=[
logging.FileHandler(filename=log_file),
logging.StreamHandler(stream=sys.stdout)])
logging.info(cfg)
logging.info(f"Running UniAnimate inference on gpu {gpu}")
# [Diffusion]
diffusion = DIFFUSION.build(cfg.Diffusion)
# [Data] Data Transform
train_trans = data.Compose([
data.Resize(cfg.resolution),
data.ToTensor(),
data.Normalize(mean=cfg.mean, std=cfg.std)
])
train_trans_pose = data.Compose([
data.Resize(cfg.resolution),
data.ToTensor(),
]
)
vit_transforms = T.Compose([
data.Resize(cfg.vit_resolution),
T.ToTensor(),
T.Normalize(mean=cfg.vit_mean, std=cfg.vit_std)])
# [Model] embedder
clip_encoder = EMBEDDER.build(cfg.embedder)
clip_encoder.model.to(gpu)
with torch.no_grad():
_, _, zero_y = clip_encoder(text="")
# [Model] auotoencoder
autoencoder = AUTO_ENCODER.build(cfg.auto_encoder)
autoencoder.eval() # freeze
for param in autoencoder.parameters():
param.requires_grad = False
autoencoder.cuda()
# [Model] UNet
if "config" in cfg.UNet:
cfg.UNet["config"] = cfg
cfg.UNet["zero_y"] = zero_y
model = MODEL.build(cfg.UNet)
state_dict = torch.load(cfg.test_model, map_location='cpu')
if 'state_dict' in state_dict:
state_dict = state_dict['state_dict']
if 'step' in state_dict:
resume_step = state_dict['step']
else:
resume_step = 0
status = model.load_state_dict(state_dict, strict=True)
logging.info('Load model from {} with status {}'.format(cfg.test_model, status))
model = model.to(gpu)
model.eval()
if hasattr(cfg, "CPU_CLIP_VAE") and cfg.CPU_CLIP_VAE:
model.to(torch.float16)
else:
model = DistributedDataParallel(model, device_ids=[gpu]) if not cfg.debug else model
torch.cuda.empty_cache()
test_list = cfg.test_list_path
num_videos = len(test_list)
logging.info(f'There are {num_videos} videos. with {cfg.round} times')
test_list = [item for _ in range(cfg.round) for item in test_list]
for idx, file_path in enumerate(test_list):
cfg.frame_interval, ref_image_key, pose_seq_key = file_path[0], file_path[1], file_path[2]
manual_seed = int(cfg.seed + cfg.rank + idx//num_videos)
setup_seed(manual_seed)
logging.info(f"[{idx}]/[{len(test_list)}] Begin to sample {ref_image_key}, pose sequence from {pose_seq_key} init seed {manual_seed} ...")
vit_frame, video_data, misc_data, dwpose_data, random_ref_frame_data, random_ref_dwpose_data, max_frames = load_video_frames(ref_image_key, pose_seq_key, train_trans, vit_transforms, train_trans_pose, max_frames=cfg.max_frames, frame_interval =cfg.frame_interval, resolution=cfg.resolution)
cfg.max_frames_new = max_frames
misc_data = misc_data.unsqueeze(0).to(gpu)
vit_frame = vit_frame.unsqueeze(0).to(gpu)
dwpose_data = dwpose_data.unsqueeze(0).to(gpu)
random_ref_frame_data = random_ref_frame_data.unsqueeze(0).to(gpu)
random_ref_dwpose_data = random_ref_dwpose_data.unsqueeze(0).to(gpu)
### save for visualization
misc_backups = copy(misc_data)
frames_num = misc_data.shape[1]
misc_backups = rearrange(misc_backups, 'b f c h w -> b c f h w')
mv_data_video = []
### local image (first frame)
image_local = []
if 'local_image' in cfg.video_compositions:
frames_num = misc_data.shape[1]
bs_vd_local = misc_data.shape[0]
image_local = misc_data[:,:1].clone().repeat(1,frames_num,1,1,1)
image_local_clone = rearrange(image_local, 'b f c h w -> b c f h w', b = bs_vd_local)
image_local = rearrange(image_local, 'b f c h w -> b c f h w', b = bs_vd_local)
if hasattr(cfg, "latent_local_image") and cfg.latent_local_image:
with torch.no_grad():
temporal_length = frames_num
encoder_posterior = autoencoder.encode(video_data[:,0])
local_image_data = get_first_stage_encoding(encoder_posterior).detach()
image_local = local_image_data.unsqueeze(1).repeat(1,temporal_length,1,1,1) # [10, 16, 4, 64, 40]
### encode the video_data
bs_vd = misc_data.shape[0]
misc_data = rearrange(misc_data, 'b f c h w -> (b f) c h w')
misc_data_list = torch.chunk(misc_data, misc_data.shape[0]//cfg.chunk_size,dim=0)
with torch.no_grad():
random_ref_frame = []
if 'randomref' in cfg.video_compositions:
random_ref_frame_clone = rearrange(random_ref_frame_data, 'b f c h w -> b c f h w')
if hasattr(cfg, "latent_random_ref") and cfg.latent_random_ref:
temporal_length = random_ref_frame_data.shape[1]
encoder_posterior = autoencoder.encode(random_ref_frame_data[:,0].sub(0.5).div_(0.5))
random_ref_frame_data = get_first_stage_encoding(encoder_posterior).detach()
random_ref_frame_data = random_ref_frame_data.unsqueeze(1).repeat(1,temporal_length,1,1,1) # [10, 16, 4, 64, 40]
random_ref_frame = rearrange(random_ref_frame_data, 'b f c h w -> b c f h w')
if 'dwpose' in cfg.video_compositions:
bs_vd_local = dwpose_data.shape[0]
dwpose_data_clone = rearrange(dwpose_data.clone(), 'b f c h w -> b c f h w', b = bs_vd_local)
if 'randomref_pose' in cfg.video_compositions:
dwpose_data = torch.cat([random_ref_dwpose_data[:,:1], dwpose_data], dim=1)
dwpose_data = rearrange(dwpose_data, 'b f c h w -> b c f h w', b = bs_vd_local)
y_visual = []
if 'image' in cfg.video_compositions:
with torch.no_grad():
vit_frame = vit_frame.squeeze(1)
y_visual = clip_encoder.encode_image(vit_frame).unsqueeze(1) # [60, 1024]
y_visual0 = y_visual.clone()
with amp.autocast(enabled=True):
pynvml.nvmlInit()
handle=pynvml.nvmlDeviceGetHandleByIndex(0)
meminfo=pynvml.nvmlDeviceGetMemoryInfo(handle)
cur_seed = torch.initial_seed()
logging.info(f"Current seed {cur_seed} ..., cfg.max_frames_new: {cfg.max_frames_new} ....")
noise = torch.randn([1, 4, cfg.max_frames_new, int(cfg.resolution[1]/cfg.scale), int(cfg.resolution[0]/cfg.scale)])
noise = noise.to(gpu)
if hasattr(cfg.Diffusion, "noise_strength"):
b, c, f, _, _= noise.shape
offset_noise = torch.randn(b, c, f, 1, 1, device=noise.device)
noise = noise + cfg.Diffusion.noise_strength * offset_noise
# construct model inputs (CFG)
full_model_kwargs=[{
'y': None,
"local_image": None if len(image_local) == 0 else image_local[:],
'image': None if len(y_visual) == 0 else y_visual0[:],
'dwpose': None if len(dwpose_data) == 0 else dwpose_data[:],
'randomref': None if len(random_ref_frame) == 0 else random_ref_frame[:],
},
{
'y': None,
"local_image": None,
'image': None,
'randomref': None,
'dwpose': None,
}]
# for visualization
full_model_kwargs_vis =[{
'y': None,
"local_image": None if len(image_local) == 0 else image_local_clone[:],
'image': None,
'dwpose': None if len(dwpose_data_clone) == 0 else dwpose_data_clone[:],
'randomref': None if len(random_ref_frame) == 0 else random_ref_frame_clone[:, :3],
},
{
'y': None,
"local_image": None,
'image': None,
'randomref': None,
'dwpose': None,
}]
partial_keys = [
['image', 'randomref', "dwpose"],
]
if hasattr(cfg, "partial_keys") and cfg.partial_keys:
partial_keys = cfg.partial_keys
for partial_keys_one in partial_keys:
model_kwargs_one = prepare_model_kwargs(partial_keys = partial_keys_one,
full_model_kwargs = full_model_kwargs,
use_fps_condition = cfg.use_fps_condition)
model_kwargs_one_vis = prepare_model_kwargs(partial_keys = partial_keys_one,
full_model_kwargs = full_model_kwargs_vis,
use_fps_condition = cfg.use_fps_condition)
noise_one = noise
if hasattr(cfg, "CPU_CLIP_VAE") and cfg.CPU_CLIP_VAE:
clip_encoder.cpu() # add this line
autoencoder.cpu() # add this line
torch.cuda.empty_cache() # add this line
video_data = diffusion.ddim_sample_loop(
noise=noise_one,
context_size=cfg.context_size,
context_stride=cfg.context_stride,
context_overlap=cfg.context_overlap,
model=model.eval(),
model_kwargs=model_kwargs_one,
guide_scale=cfg.guide_scale,
ddim_timesteps=cfg.ddim_timesteps,
eta=0.0)
if hasattr(cfg, "CPU_CLIP_VAE") and cfg.CPU_CLIP_VAE:
# if run forward of autoencoder or clip_encoder second times, load them again
clip_encoder.cuda()
autoencoder.cuda()
video_data = 1. / cfg.scale_factor * video_data # [1, 4, h, w]
video_data = rearrange(video_data, 'b c f h w -> (b f) c h w')
chunk_size = min(cfg.decoder_bs, video_data.shape[0])
video_data_list = torch.chunk(video_data, video_data.shape[0]//chunk_size, dim=0)
decode_data = []
for vd_data in video_data_list:
gen_frames = autoencoder.decode(vd_data)
decode_data.append(gen_frames)
video_data = torch.cat(decode_data, dim=0)
video_data = rearrange(video_data, '(b f) c h w -> b c f h w', b = cfg.batch_size).float()
text_size = cfg.resolution[-1]
cap_name = re.sub(r'[^\w\s]', '', ref_image_key.split("/")[-1].split('.')[0]) # .replace(' ', '_')
name = f'seed_{cur_seed}'
for ii in partial_keys_one:
name = name + "_" + ii
file_name = f'rank_{cfg.world_size:02d}_{cfg.rank:02d}_{idx:02d}_{name}_{cap_name}_{cfg.resolution[1]}x{cfg.resolution[0]}.mp4'
local_path = os.path.join(cfg.log_dir, f'{file_name}')
os.makedirs(os.path.dirname(local_path), exist_ok=True)
captions = "human"
del model_kwargs_one_vis[0][list(model_kwargs_one_vis[0].keys())[0]]
del model_kwargs_one_vis[1][list(model_kwargs_one_vis[1].keys())[0]]
save_video_multiple_conditions_not_gif_horizontal_3col(local_path, video_data.cpu(), model_kwargs_one_vis, misc_backups,
cfg.mean, cfg.std, nrow=1, save_fps=cfg.save_fps)
try:
save_t2vhigen_video_safe(local_path, video_data.cpu(), captions, cfg.mean, cfg.std, text_size)
logging.info('Save video to dir %s:' % (local_path))
except Exception as e:
logging.info(f'Step: save text or video error with {e}')
logging.info('Congratulations! The inference is completed!')
# synchronize to finish some processes
if not cfg.debug:
torch.cuda.synchronize()
dist.barrier()
def prepare_model_kwargs(partial_keys, full_model_kwargs, use_fps_condition=False):
if use_fps_condition is True:
partial_keys.append('fps')
partial_model_kwargs = [{}, {}]
for partial_key in partial_keys:
partial_model_kwargs[0][partial_key] = full_model_kwargs[0][partial_key]
partial_model_kwargs[1][partial_key] = full_model_kwargs[1][partial_key]
return partial_model_kwargs
+7
View File
@@ -0,0 +1,7 @@
from .clip_embedder import FrozenOpenCLIPEmbedder
from .autoencoder import DiagonalGaussianDistribution, AutoencoderKL
from .clip_embedder import *
from .autoencoder import *
from .unet import *
from .diffusions import *
from .embedding_manager import *
+690
View File
@@ -0,0 +1,690 @@
import torch
import logging
import collections
import numpy as np
import torch.nn as nn
import torch.nn.functional as F
from utils.registry_class import AUTO_ENCODER,DISTRIBUTION
def nonlinearity(x):
# swish
return x*torch.sigmoid(x)
def Normalize(in_channels, num_groups=32):
return torch.nn.GroupNorm(num_groups=num_groups, num_channels=in_channels, eps=1e-6, affine=True)
@torch.no_grad()
def get_first_stage_encoding(encoder_posterior, scale_factor=0.18215):
if isinstance(encoder_posterior, DiagonalGaussianDistribution):
z = encoder_posterior.sample()
elif isinstance(encoder_posterior, torch.Tensor):
z = encoder_posterior
else:
raise NotImplementedError(f"encoder_posterior of type '{type(encoder_posterior)}' not yet implemented")
return scale_factor * z
@AUTO_ENCODER.register_class()
class AutoencoderKL(nn.Module):
def __init__(self,
ddconfig,
embed_dim,
pretrained=None,
ignore_keys=[],
image_key="image",
colorize_nlabels=None,
monitor=None,
ema_decay=None,
learn_logvar=False,
use_vid_decoder=False,
**kwargs):
super().__init__()
self.learn_logvar = learn_logvar
self.image_key = image_key
self.encoder = Encoder(**ddconfig)
self.decoder = Decoder(**ddconfig)
assert ddconfig["double_z"]
self.quant_conv = torch.nn.Conv2d(2*ddconfig["z_channels"], 2*embed_dim, 1)
self.post_quant_conv = torch.nn.Conv2d(embed_dim, ddconfig["z_channels"], 1)
self.embed_dim = embed_dim
if colorize_nlabels is not None:
assert type(colorize_nlabels)==int
self.register_buffer("colorize", torch.randn(3, colorize_nlabels, 1, 1))
if monitor is not None:
self.monitor = monitor
self.use_ema = ema_decay is not None
if pretrained is not None:
self.init_from_ckpt(pretrained, ignore_keys=ignore_keys)
def init_from_ckpt(self, path, ignore_keys=list()):
sd = torch.load(path, map_location="cpu")["state_dict"]
keys = list(sd.keys())
sd_new = collections.OrderedDict()
for k in keys:
if k.find('first_stage_model') >= 0:
k_new = k.split('first_stage_model.')[-1]
sd_new[k_new] = sd[k]
self.load_state_dict(sd_new, strict=True)
logging.info(f"Restored from {path}")
def on_train_batch_end(self, *args, **kwargs):
if self.use_ema:
self.model_ema(self)
def encode(self, x):
h = self.encoder(x)
moments = self.quant_conv(h)
posterior = DiagonalGaussianDistribution(moments)
return posterior
def encode_firsr_stage(self, x, scale_factor=1.0):
h = self.encoder(x)
moments = self.quant_conv(h)
posterior = DiagonalGaussianDistribution(moments)
z = get_first_stage_encoding(posterior, scale_factor)
return z
def encode_ms(self, x):
hs = self.encoder(x, True)
h = hs[-1]
moments = self.quant_conv(h)
posterior = DiagonalGaussianDistribution(moments)
hs[-1] = h
return hs
def decode(self, z, **kwargs):
z = self.post_quant_conv(z)
dec = self.decoder(z, **kwargs)
return dec
def forward(self, input, sample_posterior=True):
posterior = self.encode(input)
if sample_posterior:
z = posterior.sample()
else:
z = posterior.mode()
dec = self.decode(z)
return dec, posterior
def get_input(self, batch, k):
x = batch[k]
if len(x.shape) == 3:
x = x[..., None]
x = x.permute(0, 3, 1, 2).to(memory_format=torch.contiguous_format).float()
return x
def get_last_layer(self):
return self.decoder.conv_out.weight
@torch.no_grad()
def log_images(self, batch, only_inputs=False, log_ema=False, **kwargs):
log = dict()
x = self.get_input(batch, self.image_key)
x = x.to(self.device)
if not only_inputs:
xrec, posterior = self(x)
if x.shape[1] > 3:
# colorize with random projection
assert xrec.shape[1] > 3
x = self.to_rgb(x)
xrec = self.to_rgb(xrec)
log["samples"] = self.decode(torch.randn_like(posterior.sample()))
log["reconstructions"] = xrec
if log_ema or self.use_ema:
with self.ema_scope():
xrec_ema, posterior_ema = self(x)
if x.shape[1] > 3:
# colorize with random projection
assert xrec_ema.shape[1] > 3
xrec_ema = self.to_rgb(xrec_ema)
log["samples_ema"] = self.decode(torch.randn_like(posterior_ema.sample()))
log["reconstructions_ema"] = xrec_ema
log["inputs"] = x
return log
def to_rgb(self, x):
assert self.image_key == "segmentation"
if not hasattr(self, "colorize"):
self.register_buffer("colorize", torch.randn(3, x.shape[1], 1, 1).to(x))
x = F.conv2d(x, weight=self.colorize)
x = 2.*(x-x.min())/(x.max()-x.min()) - 1.
return x
@AUTO_ENCODER.register_class()
class AutoencoderVideo(AutoencoderKL):
def __init__(self,
ddconfig,
embed_dim,
pretrained=None,
ignore_keys=[],
image_key="image",
colorize_nlabels=None,
monitor=None,
ema_decay=None,
use_vid_decoder=True,
learn_logvar=False,
**kwargs):
use_vid_decoder = True
super().__init__(ddconfig, embed_dim, pretrained, ignore_keys, image_key, colorize_nlabels, monitor, ema_decay, learn_logvar, use_vid_decoder, **kwargs)
def decode(self, z, **kwargs):
# z = self.post_quant_conv(z)
dec = self.decoder(z, **kwargs)
return dec
def encode(self, x):
h = self.encoder(x)
# moments = self.quant_conv(h)
moments = h
posterior = DiagonalGaussianDistribution(moments)
return posterior
class IdentityFirstStage(torch.nn.Module):
def __init__(self, *args, vq_interface=False, **kwargs):
self.vq_interface = vq_interface
super().__init__()
def encode(self, x, *args, **kwargs):
return x
def decode(self, x, *args, **kwargs):
return x
def quantize(self, x, *args, **kwargs):
if self.vq_interface:
return x, None, [None, None, None]
return x
def forward(self, x, *args, **kwargs):
return x
@DISTRIBUTION.register_class()
class DiagonalGaussianDistribution(object):
def __init__(self, parameters, deterministic=False):
self.parameters = parameters
self.mean, self.logvar = torch.chunk(parameters, 2, dim=1)
self.logvar = torch.clamp(self.logvar, -30.0, 20.0)
self.deterministic = deterministic
self.std = torch.exp(0.5 * self.logvar)
self.var = torch.exp(self.logvar)
if self.deterministic:
self.var = self.std = torch.zeros_like(self.mean).to(device=self.parameters.device)
def sample(self):
x = self.mean + self.std * torch.randn(self.mean.shape).to(device=self.parameters.device)
return x
def kl(self, other=None):
if self.deterministic:
return torch.Tensor([0.])
else:
if other is None:
return 0.5 * torch.sum(torch.pow(self.mean, 2)
+ self.var - 1.0 - self.logvar,
dim=[1, 2, 3])
else:
return 0.5 * torch.sum(
torch.pow(self.mean - other.mean, 2) / other.var
+ self.var / other.var - 1.0 - self.logvar + other.logvar,
dim=[1, 2, 3])
def nll(self, sample, dims=[1,2,3]):
if self.deterministic:
return torch.Tensor([0.])
logtwopi = np.log(2.0 * np.pi)
return 0.5 * torch.sum(
logtwopi + self.logvar + torch.pow(sample - self.mean, 2) / self.var,
dim=dims)
def mode(self):
return self.mean
# -------------------------------modules--------------------------------
class Downsample(nn.Module):
def __init__(self, in_channels, with_conv):
super().__init__()
self.with_conv = with_conv
if self.with_conv:
# no asymmetric padding in torch conv, must do it ourselves
self.conv = torch.nn.Conv2d(in_channels,
in_channels,
kernel_size=3,
stride=2,
padding=0)
def forward(self, x):
if self.with_conv:
pad = (0,1,0,1)
x = torch.nn.functional.pad(x, pad, mode="constant", value=0)
x = self.conv(x)
else:
x = torch.nn.functional.avg_pool2d(x, kernel_size=2, stride=2)
return x
class ResnetBlock(nn.Module):
def __init__(self, *, in_channels, out_channels=None, conv_shortcut=False,
dropout, temb_channels=512):
super().__init__()
self.in_channels = in_channels
out_channels = in_channels if out_channels is None else out_channels
self.out_channels = out_channels
self.use_conv_shortcut = conv_shortcut
self.norm1 = Normalize(in_channels)
self.conv1 = torch.nn.Conv2d(in_channels,
out_channels,
kernel_size=3,
stride=1,
padding=1)
if temb_channels > 0:
self.temb_proj = torch.nn.Linear(temb_channels,
out_channels)
self.norm2 = Normalize(out_channels)
self.dropout = torch.nn.Dropout(dropout)
self.conv2 = torch.nn.Conv2d(out_channels,
out_channels,
kernel_size=3,
stride=1,
padding=1)
if self.in_channels != self.out_channels:
if self.use_conv_shortcut:
self.conv_shortcut = torch.nn.Conv2d(in_channels,
out_channels,
kernel_size=3,
stride=1,
padding=1)
else:
self.nin_shortcut = torch.nn.Conv2d(in_channels,
out_channels,
kernel_size=1,
stride=1,
padding=0)
def forward(self, x, temb):
h = x
h = self.norm1(h)
h = nonlinearity(h)
h = self.conv1(h)
if temb is not None:
h = h + self.temb_proj(nonlinearity(temb))[:,:,None,None]
h = self.norm2(h)
h = nonlinearity(h)
h = self.dropout(h)
h = self.conv2(h)
if self.in_channels != self.out_channels:
if self.use_conv_shortcut:
x = self.conv_shortcut(x)
else:
x = self.nin_shortcut(x)
return x+h
class AttnBlock(nn.Module):
def __init__(self, in_channels):
super().__init__()
self.in_channels = in_channels
self.norm = Normalize(in_channels)
self.q = torch.nn.Conv2d(in_channels,
in_channels,
kernel_size=1,
stride=1,
padding=0)
self.k = torch.nn.Conv2d(in_channels,
in_channels,
kernel_size=1,
stride=1,
padding=0)
self.v = torch.nn.Conv2d(in_channels,
in_channels,
kernel_size=1,
stride=1,
padding=0)
self.proj_out = torch.nn.Conv2d(in_channels,
in_channels,
kernel_size=1,
stride=1,
padding=0)
def forward(self, x):
h_ = x
h_ = self.norm(h_)
q = self.q(h_)
k = self.k(h_)
v = self.v(h_)
# compute attention
b,c,h,w = q.shape
q = q.reshape(b,c,h*w)
q = q.permute(0,2,1) # b,hw,c
k = k.reshape(b,c,h*w) # b,c,hw
w_ = torch.bmm(q,k) # b,hw,hw w[b,i,j]=sum_c q[b,i,c]k[b,c,j]
w_ = w_ * (int(c)**(-0.5))
w_ = torch.nn.functional.softmax(w_, dim=2)
# attend to values
v = v.reshape(b,c,h*w)
w_ = w_.permute(0,2,1) # b,hw,hw (first hw of k, second of q)
h_ = torch.bmm(v,w_) # b, c,hw (hw of q) h_[b,c,j] = sum_i v[b,c,i] w_[b,i,j]
h_ = h_.reshape(b,c,h,w)
h_ = self.proj_out(h_)
return x+h_
class AttnBlock(nn.Module):
def __init__(self, in_channels):
super().__init__()
self.in_channels = in_channels
self.norm = Normalize(in_channels)
self.q = torch.nn.Conv2d(in_channels,
in_channels,
kernel_size=1,
stride=1,
padding=0)
self.k = torch.nn.Conv2d(in_channels,
in_channels,
kernel_size=1,
stride=1,
padding=0)
self.v = torch.nn.Conv2d(in_channels,
in_channels,
kernel_size=1,
stride=1,
padding=0)
self.proj_out = torch.nn.Conv2d(in_channels,
in_channels,
kernel_size=1,
stride=1,
padding=0)
def forward(self, x):
h_ = x
h_ = self.norm(h_)
q = self.q(h_)
k = self.k(h_)
v = self.v(h_)
# compute attention
b,c,h,w = q.shape
q = q.reshape(b,c,h*w)
q = q.permute(0,2,1) # b,hw,c
k = k.reshape(b,c,h*w) # b,c,hw
w_ = torch.bmm(q,k) # b,hw,hw w[b,i,j]=sum_c q[b,i,c]k[b,c,j]
w_ = w_ * (int(c)**(-0.5))
w_ = torch.nn.functional.softmax(w_, dim=2)
# attend to values
v = v.reshape(b,c,h*w)
w_ = w_.permute(0,2,1) # b,hw,hw (first hw of k, second of q)
h_ = torch.bmm(v,w_) # b, c,hw (hw of q) h_[b,c,j] = sum_i v[b,c,i] w_[b,i,j]
h_ = h_.reshape(b,c,h,w)
h_ = self.proj_out(h_)
return x+h_
class Upsample(nn.Module):
def __init__(self, in_channels, with_conv):
super().__init__()
self.with_conv = with_conv
if self.with_conv:
self.conv = torch.nn.Conv2d(in_channels,
in_channels,
kernel_size=3,
stride=1,
padding=1)
def forward(self, x):
x = torch.nn.functional.interpolate(x, scale_factor=2.0, mode="nearest")
if self.with_conv:
x = self.conv(x)
return x
class Downsample(nn.Module):
def __init__(self, in_channels, with_conv):
super().__init__()
self.with_conv = with_conv
if self.with_conv:
# no asymmetric padding in torch conv, must do it ourselves
self.conv = torch.nn.Conv2d(in_channels,
in_channels,
kernel_size=3,
stride=2,
padding=0)
def forward(self, x):
if self.with_conv:
pad = (0,1,0,1)
x = torch.nn.functional.pad(x, pad, mode="constant", value=0)
x = self.conv(x)
else:
x = torch.nn.functional.avg_pool2d(x, kernel_size=2, stride=2)
return x
class Encoder(nn.Module):
def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks,
attn_resolutions, dropout=0.0, resamp_with_conv=True, in_channels,
resolution, z_channels, double_z=True, use_linear_attn=False, attn_type="vanilla",
**ignore_kwargs):
super().__init__()
if use_linear_attn: attn_type = "linear"
self.ch = ch
self.temb_ch = 0
self.num_resolutions = len(ch_mult)
self.num_res_blocks = num_res_blocks
self.resolution = resolution
self.in_channels = in_channels
# downsampling
self.conv_in = torch.nn.Conv2d(in_channels,
self.ch,
kernel_size=3,
stride=1,
padding=1)
curr_res = resolution
in_ch_mult = (1,)+tuple(ch_mult)
self.in_ch_mult = in_ch_mult
self.down = nn.ModuleList()
for i_level in range(self.num_resolutions):
block = nn.ModuleList()
attn = nn.ModuleList()
block_in = ch*in_ch_mult[i_level]
block_out = ch*ch_mult[i_level]
for i_block in range(self.num_res_blocks):
block.append(ResnetBlock(in_channels=block_in,
out_channels=block_out,
temb_channels=self.temb_ch,
dropout=dropout))
block_in = block_out
if curr_res in attn_resolutions:
attn.append(AttnBlock(block_in))
down = nn.Module()
down.block = block
down.attn = attn
if i_level != self.num_resolutions-1:
down.downsample = Downsample(block_in, resamp_with_conv)
curr_res = curr_res // 2
self.down.append(down)
# middle
self.mid = nn.Module()
self.mid.block_1 = ResnetBlock(in_channels=block_in,
out_channels=block_in,
temb_channels=self.temb_ch,
dropout=dropout)
self.mid.attn_1 = AttnBlock(block_in)
self.mid.block_2 = ResnetBlock(in_channels=block_in,
out_channels=block_in,
temb_channels=self.temb_ch,
dropout=dropout)
# end
self.norm_out = Normalize(block_in)
self.conv_out = torch.nn.Conv2d(block_in,
2*z_channels if double_z else z_channels,
kernel_size=3,
stride=1,
padding=1)
def forward(self, x, return_feat=False):
# timestep embedding
temb = None
# downsampling
hs = [self.conv_in(x)]
for i_level in range(self.num_resolutions):
for i_block in range(self.num_res_blocks):
h = self.down[i_level].block[i_block](hs[-1], temb)
if len(self.down[i_level].attn) > 0:
h = self.down[i_level].attn[i_block](h)
hs.append(h)
if i_level != self.num_resolutions-1:
hs.append(self.down[i_level].downsample(hs[-1]))
# middle
h = hs[-1]
h = self.mid.block_1(h, temb)
h = self.mid.attn_1(h)
h = self.mid.block_2(h, temb)
# end
h = self.norm_out(h)
h = nonlinearity(h)
h = self.conv_out(h)
if return_feat:
hs[-1] = h
return hs
else:
return h
class Decoder(nn.Module):
def __init__(self, *, ch, out_ch, ch_mult=(1,2,4,8), num_res_blocks,
attn_resolutions, dropout=0.0, resamp_with_conv=True, in_channels,
resolution, z_channels, give_pre_end=False, tanh_out=False, use_linear_attn=False,
attn_type="vanilla", **ignorekwargs):
super().__init__()
if use_linear_attn: attn_type = "linear"
self.ch = ch
self.temb_ch = 0
self.num_resolutions = len(ch_mult)
self.num_res_blocks = num_res_blocks
self.resolution = resolution
self.in_channels = in_channels
self.give_pre_end = give_pre_end
self.tanh_out = tanh_out
# compute in_ch_mult, block_in and curr_res at lowest res
in_ch_mult = (1,)+tuple(ch_mult)
block_in = ch*ch_mult[self.num_resolutions-1]
curr_res = resolution // 2**(self.num_resolutions-1)
self.z_shape = (1,z_channels, curr_res, curr_res)
# logging.info("Working with z of shape {} = {} dimensions.".format(self.z_shape, np.prod(self.z_shape)))
# z to block_in
self.conv_in = torch.nn.Conv2d(z_channels,
block_in,
kernel_size=3,
stride=1,
padding=1)
# middle
self.mid = nn.Module()
self.mid.block_1 = ResnetBlock(in_channels=block_in,
out_channels=block_in,
temb_channels=self.temb_ch,
dropout=dropout)
self.mid.attn_1 = AttnBlock(block_in)
self.mid.block_2 = ResnetBlock(in_channels=block_in,
out_channels=block_in,
temb_channels=self.temb_ch,
dropout=dropout)
# upsampling
self.up = nn.ModuleList()
for i_level in reversed(range(self.num_resolutions)):
block = nn.ModuleList()
attn = nn.ModuleList()
block_out = ch*ch_mult[i_level]
for i_block in range(self.num_res_blocks+1):
block.append(ResnetBlock(in_channels=block_in,
out_channels=block_out,
temb_channels=self.temb_ch,
dropout=dropout))
block_in = block_out
if curr_res in attn_resolutions:
attn.append(AttnBlock(block_in))
up = nn.Module()
up.block = block
up.attn = attn
if i_level != 0:
up.upsample = Upsample(block_in, resamp_with_conv)
curr_res = curr_res * 2
self.up.insert(0, up) # prepend to get consistent order
# end
self.norm_out = Normalize(block_in)
self.conv_out = torch.nn.Conv2d(block_in,
out_ch,
kernel_size=3,
stride=1,
padding=1)
def forward(self, z, **kwargs):
#assert z.shape[1:] == self.z_shape[1:]
self.last_z_shape = z.shape
# timestep embedding
temb = None
# z to block_in
h = self.conv_in(z)
# middle
h = self.mid.block_1(h, temb)
h = self.mid.attn_1(h)
h = self.mid.block_2(h, temb)
# upsampling
for i_level in reversed(range(self.num_resolutions)):
for i_block in range(self.num_res_blocks+1):
h = self.up[i_level].block[i_block](h, temb)
if len(self.up[i_level].attn) > 0:
h = self.up[i_level].attn[i_block](h)
if i_level != 0:
h = self.up[i_level].upsample(h)
# end
if self.give_pre_end:
return h
h = self.norm_out(h)
h = nonlinearity(h)
h = self.conv_out(h)
if self.tanh_out:
h = torch.tanh(h)
return h
+212
View File
@@ -0,0 +1,212 @@
import os
import torch
import logging
import open_clip
import numpy as np
import torch.nn as nn
import torchvision.transforms as T
from utils.registry_class import EMBEDDER
@EMBEDDER.register_class()
class FrozenOpenCLIPEmbedder(nn.Module):
"""
Uses the OpenCLIP transformer encoder for text
"""
LAYERS = [
#"pooled",
"last",
"penultimate"
]
def __init__(self, pretrained, arch="ViT-H-14", device="cuda", max_length=77,
freeze=True, layer="last"):
super().__init__()
assert layer in self.LAYERS
model, _, _ = open_clip.create_model_and_transforms(arch, device=torch.device('cpu'), pretrained=pretrained)
del model.visual
self.model = model
self.device = device
self.max_length = max_length
if freeze:
self.freeze()
self.layer = layer
if self.layer == "last":
self.layer_idx = 0
elif self.layer == "penultimate":
self.layer_idx = 1
else:
raise NotImplementedError()
def freeze(self):
self.model = self.model.eval()
for param in self.parameters():
param.requires_grad = False
def forward(self, text):
tokens = open_clip.tokenize(text)
z = self.encode_with_transformer(tokens.to(self.device))
return z
def encode_with_transformer(self, text):
x = self.model.token_embedding(text) # [batch_size, n_ctx, d_model]
x = x + self.model.positional_embedding
x = x.permute(1, 0, 2) # NLD -> LND
x = self.text_transformer_forward(x, attn_mask=self.model.attn_mask)
x = x.permute(1, 0, 2) # LND -> NLD
x = self.model.ln_final(x)
return x
def text_transformer_forward(self, x: torch.Tensor, attn_mask = None):
for i, r in enumerate(self.model.transformer.resblocks):
if i == len(self.model.transformer.resblocks) - self.layer_idx:
break
if self.model.transformer.grad_checkpointing and not torch.jit.is_scripting():
x = checkpoint(r, x, attn_mask)
else:
x = r(x, attn_mask=attn_mask)
return x
def encode(self, text):
return self(text)
@EMBEDDER.register_class()
class FrozenOpenCLIPVisualEmbedder(nn.Module):
"""
Uses the OpenCLIP transformer encoder for text
"""
LAYERS = [
#"pooled",
"last",
"penultimate"
]
def __init__(self, pretrained, vit_resolution=(224, 224), arch="ViT-H-14", device="cuda", max_length=77,
freeze=True, layer="last"):
super().__init__()
assert layer in self.LAYERS
model, _, preprocess = open_clip.create_model_and_transforms(
arch, device=torch.device('cpu'), pretrained=pretrained)
del model.transformer
self.model = model
data_white = np.ones((vit_resolution[0], vit_resolution[1], 3), dtype=np.uint8)*255
self.white_image = preprocess(T.ToPILImage()(data_white)).unsqueeze(0)
self.device = device
self.max_length = max_length # 77
if freeze:
self.freeze()
self.layer = layer # 'penultimate'
if self.layer == "last":
self.layer_idx = 0
elif self.layer == "penultimate":
self.layer_idx = 1
else:
raise NotImplementedError()
def freeze(self):
self.model = self.model.eval()
for param in self.parameters():
param.requires_grad = False
def forward(self, image):
# tokens = open_clip.tokenize(text)
z = self.model.encode_image(image.to(self.device))
return z
def encode_with_transformer(self, text):
x = self.model.token_embedding(text) # [batch_size, n_ctx, d_model]
x = x + self.model.positional_embedding
x = x.permute(1, 0, 2) # NLD -> LND
x = self.text_transformer_forward(x, attn_mask=self.model.attn_mask)
x = x.permute(1, 0, 2) # LND -> NLD
x = self.model.ln_final(x)
return x
def text_transformer_forward(self, x: torch.Tensor, attn_mask = None):
for i, r in enumerate(self.model.transformer.resblocks):
if i == len(self.model.transformer.resblocks) - self.layer_idx:
break
if self.model.transformer.grad_checkpointing and not torch.jit.is_scripting():
x = checkpoint(r, x, attn_mask)
else:
x = r(x, attn_mask=attn_mask)
return x
def encode(self, text):
return self(text)
@EMBEDDER.register_class()
class FrozenOpenCLIPTextVisualEmbedder(nn.Module):
"""
Uses the OpenCLIP transformer encoder for text
"""
LAYERS = [
#"pooled",
"last",
"penultimate"
]
def __init__(self, pretrained, arch="ViT-H-14", device="cuda", max_length=77,
freeze=True, layer="last", **kwargs):
super().__init__()
assert layer in self.LAYERS
model, _, _ = open_clip.create_model_and_transforms(arch, device=torch.device('cpu'), pretrained=pretrained)
self.model = model
self.device = device
self.max_length = max_length
if freeze:
self.freeze()
self.layer = layer
if self.layer == "last":
self.layer_idx = 0
elif self.layer == "penultimate":
self.layer_idx = 1
else:
raise NotImplementedError()
def freeze(self):
self.model = self.model.eval()
for param in self.parameters():
param.requires_grad = False
def forward(self, image=None, text=None):
xi = self.model.encode_image(image.to(self.device)) if image is not None else None
tokens = open_clip.tokenize(text)
xt, x = self.encode_with_transformer(tokens.to(self.device))
return xi, xt, x
def encode_with_transformer(self, text):
x = self.model.token_embedding(text) # [batch_size, n_ctx, d_model]
x = x + self.model.positional_embedding
x = x.permute(1, 0, 2) # NLD -> LND
x = self.text_transformer_forward(x, attn_mask=self.model.attn_mask)
x = x.permute(1, 0, 2) # LND -> NLD
x = self.model.ln_final(x)
xt = x[torch.arange(x.shape[0]), text.argmax(dim=-1)] @ self.model.text_projection
return xt, x
def encode_image(self, image):
return self.model.visual(image)
def text_transformer_forward(self, x: torch.Tensor, attn_mask = None):
for i, r in enumerate(self.model.transformer.resblocks):
if i == len(self.model.transformer.resblocks) - self.layer_idx:
break
if self.model.transformer.grad_checkpointing and not torch.jit.is_scripting():
x = checkpoint(r, x, attn_mask)
else:
x = r(x, attn_mask=attn_mask)
return x
def encode(self, text):
return self(text)
+206
View File
@@ -0,0 +1,206 @@
import torch
import logging
import os.path as osp
from datetime import datetime
from easydict import EasyDict
import os
cfg = EasyDict(__name__='Config: VideoLDM Decoder')
# -------------------------------distributed training--------------------------
pmi_world_size = int(os.getenv('WORLD_SIZE', 1))
gpus_per_machine = torch.cuda.device_count()
world_size = pmi_world_size * gpus_per_machine
# -----------------------------------------------------------------------------
# ---------------------------Dataset Parameter---------------------------------
cfg.mean = [0.5, 0.5, 0.5]
cfg.std = [0.5, 0.5, 0.5]
cfg.max_words = 1000
cfg.num_workers = 8
cfg.prefetch_factor = 2
# PlaceHolder
cfg.resolution = [448, 256]
cfg.vit_out_dim = 1024
cfg.vit_resolution = 336
cfg.depth_clamp = 10.0
cfg.misc_size = 384
cfg.depth_std = 20.0
cfg.save_fps = 8
cfg.frame_lens = [32, 32, 32, 1]
cfg.sample_fps = [4, ]
cfg.vid_dataset = {
'type': 'VideoBaseDataset',
'data_list': [],
'max_words': cfg.max_words,
'resolution': cfg.resolution}
cfg.img_dataset = {
'type': 'ImageBaseDataset',
'data_list': ['laion_400m',],
'max_words': cfg.max_words,
'resolution': cfg.resolution}
cfg.batch_sizes = {
str(1):256,
str(4):4,
str(8):4,
str(16):4}
# -----------------------------------------------------------------------------
# ---------------------------Mode Parameters-----------------------------------
# Diffusion
cfg.Diffusion = {
'type': 'DiffusionDDIM',
'schedule': 'cosine', # cosine
'schedule_param': {
'num_timesteps': 1000,
'cosine_s': 0.008,
'zero_terminal_snr': True,
},
'mean_type': 'v', # [v, eps]
'loss_type': 'mse',
'var_type': 'fixed_small',
'rescale_timesteps': False,
'noise_strength': 0.1,
'ddim_timesteps': 50
}
cfg.ddim_timesteps = 50 # official: 250
cfg.use_div_loss = False
# classifier-free guidance
cfg.p_zero = 0.9
cfg.guide_scale = 3.0
# clip vision encoder
cfg.vit_mean = [0.48145466, 0.4578275, 0.40821073]
cfg.vit_std = [0.26862954, 0.26130258, 0.27577711]
# sketch
cfg.sketch_mean = [0.485, 0.456, 0.406]
cfg.sketch_std = [0.229, 0.224, 0.225]
# cfg.misc_size = 256
cfg.depth_std = 20.0
cfg.depth_clamp = 10.0
cfg.hist_sigma = 10.0
# Model
cfg.scale_factor = 0.18215
cfg.use_checkpoint = True
cfg.use_sharded_ddp = False
cfg.use_fsdp = False
cfg.use_fp16 = True
cfg.temporal_attention = True
cfg.UNet = {
'type': 'UNetSD',
'in_dim': 4,
'dim': 320,
'y_dim': cfg.vit_out_dim,
'context_dim': 1024,
'out_dim': 8,
'dim_mult': [1, 2, 4, 4],
'num_heads': 8,
'head_dim': 64,
'num_res_blocks': 2,
'attn_scales': [1 / 1, 1 / 2, 1 / 4],
'dropout': 0.1,
'temporal_attention': cfg.temporal_attention,
'temporal_attn_times': 1,
'use_checkpoint': cfg.use_checkpoint,
'use_fps_condition': False,
'use_sim_mask': False
}
# auotoencoder from stabel diffusion
cfg.guidances = []
cfg.auto_encoder = {
'type': 'AutoencoderKL',
'ddconfig': {
'double_z': True,
'z_channels': 4,
'resolution': 256,
'in_channels': 3,
'out_ch': 3,
'ch': 128,
'ch_mult': [1, 2, 4, 4],
'num_res_blocks': 2,
'attn_resolutions': [],
'dropout': 0.0,
'video_kernel_size': [3, 1, 1]
},
'embed_dim': 4,
'pretrained': 'models/v2-1_512-ema-pruned.ckpt'
}
# clip embedder
cfg.embedder = {
'type': 'FrozenOpenCLIPEmbedder',
'layer': 'penultimate',
'pretrained': 'models/open_clip_pytorch_model.bin'
}
# -----------------------------------------------------------------------------
# ---------------------------Training Settings---------------------------------
# training and optimizer
cfg.ema_decay = 0.9999
cfg.num_steps = 600000
cfg.lr = 5e-5
cfg.weight_decay = 0.0
cfg.betas = (0.9, 0.999)
cfg.eps = 1.0e-8
cfg.chunk_size = 16
cfg.decoder_bs = 8
cfg.alpha = 0.7
cfg.save_ckp_interval = 1000
# scheduler
cfg.warmup_steps = 10
cfg.decay_mode = 'cosine'
# acceleration
cfg.use_ema = True
if world_size<2:
cfg.use_ema = False
cfg.load_from = None
# -----------------------------------------------------------------------------
# ----------------------------Pretrain Settings---------------------------------
cfg.Pretrain = {
'type': 'pretrain_specific_strategies',
'fix_weight': False,
'grad_scale': 0.2,
'resume_checkpoint': 'models/jiuniu_0267000.pth',
'sd_keys_path': 'models/stable_diffusion_image_key_temporal_attention_x1.json',
}
# -----------------------------------------------------------------------------
# -----------------------------Visual-------------------------------------------
# Visual videos
cfg.viz_interval = 1000
cfg.visual_train = {
'type': 'VisualTrainTextImageToVideo',
}
cfg.visual_inference = {
'type': 'VisualGeneratedVideos',
}
cfg.inference_list_path = ''
# logging
cfg.log_interval = 100
### Default log_dir
cfg.log_dir = 'outputs/'
# -----------------------------------------------------------------------------
# ---------------------------Others--------------------------------------------
# seed
cfg.seed = 8888
cfg.negative_prompt = 'Distorted, discontinuous, Ugly, blurry, low resolution, motionless, static, disfigured, disconnected limbs, Ugly faces, incomplete arms'
# -----------------------------------------------------------------------------
@@ -0,0 +1 @@
from .diffusion_ddim import *
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,498 @@
"""
GaussianDiffusion wraps operators for denoising diffusion models, including the
diffusion and denoising processes, as well as the loss evaluation.
"""
import torch
import torchsde
import random
from tqdm.auto import trange
__all__ = ['GaussianDiffusion']
def _i(tensor, t, x):
"""
Index tensor using t and format the output according to x.
"""
shape = (x.size(0), ) + (1, ) * (x.ndim - 1)
return tensor[t.to(tensor.device)].view(shape).to(x.device)
class BatchedBrownianTree:
"""
A wrapper around torchsde.BrownianTree that enables batches of entropy.
"""
def __init__(self, x, t0, t1, seed=None, **kwargs):
t0, t1, self.sign = self.sort(t0, t1)
w0 = kwargs.get('w0', torch.zeros_like(x))
if seed is None:
seed = torch.randint(0, 2 ** 63 - 1, []).item()
self.batched = True
try:
assert len(seed) == x.shape[0]
w0 = w0[0]
except TypeError:
seed = [seed]
self.batched = False
self.trees = [torchsde.BrownianTree(
t0, w0, t1, entropy=s, **kwargs
) for s in seed]
@staticmethod
def sort(a, b):
return (a, b, 1) if a < b else (b, a, -1)
def __call__(self, t0, t1):
t0, t1, sign = self.sort(t0, t1)
w = torch.stack([tree(t0, t1) for tree in self.trees]) * (self.sign * sign)
return w if self.batched else w[0]
class BrownianTreeNoiseSampler:
"""
A noise sampler backed by a torchsde.BrownianTree.
Args:
x (Tensor): The tensor whose shape, device and dtype to use to generate
random samples.
sigma_min (float): The low end of the valid interval.
sigma_max (float): The high end of the valid interval.
seed (int or List[int]): The random seed. If a list of seeds is
supplied instead of a single integer, then the noise sampler will
use one BrownianTree per batch item, each with its own seed.
transform (callable): A function that maps sigma to the sampler's
internal timestep.
"""
def __init__(self, x, sigma_min, sigma_max, seed=None, transform=lambda x: x):
self.transform = transform
t0 = self.transform(torch.as_tensor(sigma_min))
t1 = self.transform(torch.as_tensor(sigma_max))
self.tree = BatchedBrownianTree(x, t0, t1, seed)
def __call__(self, sigma, sigma_next):
t0 = self.transform(torch.as_tensor(sigma))
t1 = self.transform(torch.as_tensor(sigma_next))
return self.tree(t0, t1) / (t1 - t0).abs().sqrt()
def get_scalings(sigma):
c_out = -sigma
c_in = 1 / (sigma ** 2 + 1. ** 2) ** 0.5
return c_out, c_in
@torch.no_grad()
def sample_dpmpp_2m_sde(
noise,
model,
sigmas,
eta=1.,
s_noise=1.,
solver_type='midpoint',
show_progress=True
):
"""
DPM-Solver++ (2M) SDE.
"""
assert solver_type in {'heun', 'midpoint'}
x = noise * sigmas[0]
sigma_min, sigma_max = sigmas[sigmas > 0].min(), sigmas[sigmas < float('inf')].max()
noise_sampler = BrownianTreeNoiseSampler(x, sigma_min, sigma_max)
old_denoised = None
h_last = None
for i in trange(len(sigmas) - 1, disable=not show_progress):
if sigmas[i] == float('inf'):
# Euler method
denoised = model(noise, sigmas[i])
x = denoised + sigmas[i + 1] * noise
else:
_, c_in = get_scalings(sigmas[i])
denoised = model(x * c_in, sigmas[i])
if sigmas[i + 1] == 0:
# Denoising step
x = denoised
else:
# DPM-Solver++(2M) SDE
t, s = -sigmas[i].log(), -sigmas[i + 1].log()
h = s - t
eta_h = eta * h
x = sigmas[i + 1] / sigmas[i] * (-eta_h).exp() * x + \
(-h - eta_h).expm1().neg() * denoised
if old_denoised is not None:
r = h_last / h
if solver_type == 'heun':
x = x + ((-h - eta_h).expm1().neg() / (-h - eta_h) + 1) * \
(1 / r) * (denoised - old_denoised)
elif solver_type == 'midpoint':
x = x + 0.5 * (-h - eta_h).expm1().neg() * \
(1 / r) * (denoised - old_denoised)
x = x + noise_sampler(
sigmas[i],
sigmas[i + 1]
) * sigmas[i + 1] * (-2 * eta_h).expm1().neg().sqrt() * s_noise
old_denoised = denoised
h_last = h
return x
class GaussianDiffusion(object):
def __init__(self, sigmas, prediction_type='eps'):
assert prediction_type in {'x0', 'eps', 'v'}
self.sigmas = sigmas.float() # noise coefficients
self.alphas = torch.sqrt(1 - sigmas ** 2).float() # signal coefficients
self.num_timesteps = len(sigmas)
self.prediction_type = prediction_type
def diffuse(self, x0, t, noise=None):
"""
Add Gaussian noise to signal x0 according to:
q(x_t | x_0) = N(x_t | alpha_t x_0, sigma_t^2 I).
"""
noise = torch.randn_like(x0) if noise is None else noise
xt = _i(self.alphas, t, x0) * x0 + _i(self.sigmas, t, x0) * noise
return xt
def denoise(
self,
xt,
t,
s,
model,
model_kwargs={},
guide_scale=None,
guide_rescale=None,
clamp=None,
percentile=None
):
"""
Apply one step of denoising from the posterior distribution q(x_s | x_t, x0).
Since x0 is not available, estimate the denoising results using the learned
distribution p(x_s | x_t, \hat{x}_0 == f(x_t)).
"""
s = t - 1 if s is None else s
# hyperparams
sigmas = _i(self.sigmas, t, xt)
alphas = _i(self.alphas, t, xt)
alphas_s = _i(self.alphas, s.clamp(0), xt)
alphas_s[s < 0] = 1.
sigmas_s = torch.sqrt(1 - alphas_s ** 2)
# precompute variables
betas = 1 - (alphas / alphas_s) ** 2
coef1 = betas * alphas_s / sigmas ** 2
coef2 = (alphas * sigmas_s ** 2) / (alphas_s * sigmas ** 2)
var = betas * (sigmas_s / sigmas) ** 2
log_var = torch.log(var).clamp_(-20, 20)
# prediction
if guide_scale is None:
assert isinstance(model_kwargs, dict)
out = model(xt, t=t, **model_kwargs)
else:
# classifier-free guidance (arXiv:2207.12598)
# model_kwargs[0]: conditional kwargs
# model_kwargs[1]: non-conditional kwargs
assert isinstance(model_kwargs, list) and len(model_kwargs) == 2
y_out = model(xt, t=t, **model_kwargs[0])
if guide_scale == 1.:
out = y_out
else:
u_out = model(xt, t=t, **model_kwargs[1])
out = u_out + guide_scale * (y_out - u_out)
# rescale the output according to arXiv:2305.08891
if guide_rescale is not None:
assert guide_rescale >= 0 and guide_rescale <= 1
ratio = (y_out.flatten(1).std(dim=1) / (
out.flatten(1).std(dim=1) + 1e-12
)).view((-1, ) + (1, ) * (y_out.ndim - 1))
out *= guide_rescale * ratio + (1 - guide_rescale) * 1.0
# compute x0
if self.prediction_type == 'x0':
x0 = out
elif self.prediction_type == 'eps':
x0 = (xt - sigmas * out) / alphas
elif self.prediction_type == 'v':
x0 = alphas * xt - sigmas * out
else:
raise NotImplementedError(
f'prediction_type {self.prediction_type} not implemented'
)
# restrict the range of x0
if percentile is not None:
# NOTE: percentile should only be used when data is within range [-1, 1]
assert percentile > 0 and percentile <= 1
s = torch.quantile(x0.flatten(1).abs(), percentile, dim=1)
s = s.clamp_(1.0).view((-1, ) + (1, ) * (xt.ndim - 1))
x0 = torch.min(s, torch.max(-s, x0)) / s
elif clamp is not None:
x0 = x0.clamp(-clamp, clamp)
# recompute eps using the restricted x0
eps = (xt - alphas * x0) / sigmas
# compute mu (mean of posterior distribution) using the restricted x0
mu = coef1 * x0 + coef2 * xt
return mu, var, log_var, x0, eps
@torch.no_grad()
def sample(
self,
noise,
model,
model_kwargs={},
condition_fn=None,
guide_scale=None,
guide_rescale=None,
clamp=None,
percentile=None,
solver='euler_a',
steps=20,
t_max=None,
t_min=None,
discretization=None,
discard_penultimate_step=None,
return_intermediate=None,
show_progress=False,
seed=-1,
**kwargs
):
# sanity check
assert isinstance(steps, (int, torch.LongTensor))
assert t_max is None or (t_max > 0 and t_max <= self.num_timesteps - 1)
assert t_min is None or (t_min >= 0 and t_min < self.num_timesteps - 1)
assert discretization in (None, 'leading', 'linspace', 'trailing')
assert discard_penultimate_step in (None, True, False)
assert return_intermediate in (None, 'x0', 'xt')
# function of diffusion solver
solver_fn = {
# 'heun': sample_heun,
'dpmpp_2m_sde': sample_dpmpp_2m_sde
}[solver]
# options
schedule = 'karras' if 'karras' in solver else None
discretization = discretization or 'linspace'
seed = seed if seed >= 0 else random.randint(0, 2 ** 31)
if isinstance(steps, torch.LongTensor):
discard_penultimate_step = False
if discard_penultimate_step is None:
discard_penultimate_step = True if solver in (
'dpm2',
'dpm2_ancestral',
'dpmpp_2m_sde',
'dpm2_karras',
'dpm2_ancestral_karras',
'dpmpp_2m_sde_karras'
) else False
# function for denoising xt to get x0
intermediates = []
def model_fn(xt, sigma):
# denoising
t = self._sigma_to_t(sigma).repeat(len(xt)).round().long()
x0 = self.denoise(
xt, t, None, model, model_kwargs, guide_scale, guide_rescale, clamp,
percentile
)[-2]
# collect intermediate outputs
if return_intermediate == 'xt':
intermediates.append(xt)
elif return_intermediate == 'x0':
intermediates.append(x0)
return x0
# get timesteps
if isinstance(steps, int):
steps += 1 if discard_penultimate_step else 0
t_max = self.num_timesteps - 1 if t_max is None else t_max
t_min = 0 if t_min is None else t_min
# discretize timesteps
if discretization == 'leading':
steps = torch.arange(
t_min, t_max + 1, (t_max - t_min + 1) / steps
).flip(0)
elif discretization == 'linspace':
steps = torch.linspace(t_max, t_min, steps)
elif discretization == 'trailing':
steps = torch.arange(t_max, t_min - 1, -((t_max - t_min + 1) / steps))
else:
raise NotImplementedError(
f'{discretization} discretization not implemented'
)
steps = steps.clamp_(t_min, t_max)
steps = torch.as_tensor(steps, dtype=torch.float32, device=noise.device)
# get sigmas
sigmas = self._t_to_sigma(steps)
sigmas = torch.cat([sigmas, sigmas.new_zeros([1])])
if schedule == 'karras':
if sigmas[0] == float('inf'):
sigmas = karras_schedule(
n=len(steps) - 1,
sigma_min=sigmas[sigmas > 0].min().item(),
sigma_max=sigmas[sigmas < float('inf')].max().item(),
rho=7.
).to(sigmas)
sigmas = torch.cat([
sigmas.new_tensor([float('inf')]), sigmas, sigmas.new_zeros([1])
])
else:
sigmas = karras_schedule(
n=len(steps),
sigma_min=sigmas[sigmas > 0].min().item(),
sigma_max=sigmas.max().item(),
rho=7.
).to(sigmas)
sigmas = torch.cat([sigmas, sigmas.new_zeros([1])])
if discard_penultimate_step:
sigmas = torch.cat([sigmas[:-2], sigmas[-1:]])
# sampling
x0 = solver_fn(
noise,
model_fn,
sigmas,
show_progress=show_progress,
**kwargs
)
return (x0, intermediates) if return_intermediate is not None else x0
@torch.no_grad()
def ddim_reverse_sample(
self,
xt,
t,
model,
model_kwargs={},
clamp=None,
percentile=None,
guide_scale=None,
guide_rescale=None,
ddim_timesteps=20,
reverse_steps=600
):
r"""Sample from p(x_{t+1} | x_t) using DDIM reverse ODE (deterministic).
"""
stride = reverse_steps // ddim_timesteps
# predict distribution of p(x_{t-1} | x_t)
_, _, _, x0, eps = self.denoise(
xt, t, None, model, model_kwargs, guide_scale, guide_rescale, clamp,
percentile
)
# derive variables
s = (t + stride).clamp(0, reverse_steps-1)
# hyperparams
sigmas = _i(self.sigmas, t, xt)
alphas = _i(self.alphas, t, xt)
alphas_s = _i(self.alphas, s.clamp(0), xt)
alphas_s[s < 0] = 1.
sigmas_s = torch.sqrt(1 - alphas_s ** 2)
# reverse sample
mu = alphas_s * x0 + sigmas_s * eps
return mu, x0
@torch.no_grad()
def ddim_reverse_sample_loop(
self,
x0,
model,
model_kwargs={},
clamp=None,
percentile=None,
guide_scale=None,
guide_rescale=None,
ddim_timesteps=20,
reverse_steps=600
):
# prepare input
b = x0.size(0)
xt = x0
# reconstruction steps
steps = torch.arange(0, reverse_steps, reverse_steps // ddim_timesteps)
for step in steps:
t = torch.full((b, ), step, dtype=torch.long, device=xt.device)
xt, _ = self.ddim_reverse_sample(xt, t, model, model_kwargs, clamp, percentile, guide_scale, guide_rescale, ddim_timesteps, reverse_steps)
return xt
def _sigma_to_t(self, sigma):
if sigma == float('inf'):
t = torch.full_like(sigma, len(self.sigmas) - 1)
else:
log_sigmas = torch.sqrt(
self.sigmas ** 2 / (1 - self.sigmas ** 2)
).log().to(sigma)
log_sigma = sigma.log()
dists = log_sigma - log_sigmas[:, None]
low_idx = dists.ge(0).cumsum(dim=0).argmax(dim=0).clamp(
max=log_sigmas.shape[0] - 2
)
high_idx = low_idx + 1
low, high = log_sigmas[low_idx], log_sigmas[high_idx]
w = (low - log_sigma) / (low - high)
w = w.clamp(0, 1)
t = (1 - w) * low_idx + w * high_idx
t = t.view(sigma.shape)
if t.ndim == 0:
t = t.unsqueeze(0)
return t
def _t_to_sigma(self, t):
t = t.float()
low_idx, high_idx, w = t.floor().long(), t.ceil().long(), t.frac()
log_sigmas = torch.sqrt(self.sigmas ** 2 / (1 - self.sigmas ** 2)).log().to(t)
log_sigma = (1 - w) * log_sigmas[low_idx] + w * log_sigmas[high_idx]
log_sigma[torch.isnan(log_sigma) | torch.isinf(log_sigma)] = float('inf')
return log_sigma.exp()
def prev_step(self, model_out, t, xt, inference_steps=50):
prev_t = t - self.num_timesteps // inference_steps
sigmas = _i(self.sigmas, t, xt)
alphas = _i(self.alphas, t, xt)
alphas_prev = _i(self.alphas, prev_t.clamp(0), xt)
alphas_prev[prev_t < 0] = 1.
sigmas_prev = torch.sqrt(1 - alphas_prev ** 2)
x0 = alphas * xt - sigmas * model_out
eps = (xt - alphas * x0) / sigmas
prev_sample = alphas_prev * x0 + sigmas_prev * eps
return prev_sample
def next_step(self, model_out, t, xt, inference_steps=50):
t, next_t = min(t - self.num_timesteps // inference_steps, 999), t
sigmas = _i(self.sigmas, t, xt)
alphas = _i(self.alphas, t, xt)
alphas_next = _i(self.alphas, next_t.clamp(0), xt)
alphas_next[next_t < 0] = 1.
sigmas_next = torch.sqrt(1 - alphas_next ** 2)
x0 = alphas * xt - sigmas * model_out
eps = (xt - alphas * x0) / sigmas
next_sample = alphas_next * x0 + sigmas_next * eps
return next_sample
def get_noise_pred_single(self, xt, t, model, model_kwargs):
assert isinstance(model_kwargs, dict)
out = model(xt, t=t, **model_kwargs)
return out
@@ -0,0 +1,28 @@
import torch
import math
__all__ = ['kl_divergence', 'discretized_gaussian_log_likelihood']
def kl_divergence(mu1, logvar1, mu2, logvar2):
return 0.5 * (-1.0 + logvar2 - logvar1 + torch.exp(logvar1 - logvar2) + ((mu1 - mu2) ** 2) * torch.exp(-logvar2))
def standard_normal_cdf(x):
r"""A fast approximation of the cumulative distribution function of the standard normal.
"""
return 0.5 * (1.0 + torch.tanh(math.sqrt(2.0 / math.pi) * (x + 0.044715 * torch.pow(x, 3))))
def discretized_gaussian_log_likelihood(x0, mean, log_scale):
assert x0.shape == mean.shape == log_scale.shape
cx = x0 - mean
inv_stdv = torch.exp(-log_scale)
cdf_plus = standard_normal_cdf(inv_stdv * (cx + 1.0 / 255.0))
cdf_min = standard_normal_cdf(inv_stdv * (cx - 1.0 / 255.0))
log_cdf_plus = torch.log(cdf_plus.clamp(min=1e-12))
log_one_minus_cdf_min = torch.log((1.0 - cdf_min).clamp(min=1e-12))
cdf_delta = cdf_plus - cdf_min
log_probs = torch.where(
x0 < -0.999,
log_cdf_plus,
torch.where(x0 > 0.999, log_one_minus_cdf_min, torch.log(cdf_delta.clamp(min=1e-12))))
assert log_probs.shape == x0.shape
return log_probs
@@ -0,0 +1,166 @@
import math
import torch
def beta_schedule(schedule='cosine',
num_timesteps=1000,
zero_terminal_snr=False,
**kwargs):
# compute betas
betas = {
# 'logsnr_cosine_interp': logsnr_cosine_interp_schedule,
'linear': linear_schedule,
'linear_sd': linear_sd_schedule,
'quadratic': quadratic_schedule,
'cosine': cosine_schedule
}[schedule](num_timesteps, **kwargs)
if zero_terminal_snr and abs(betas.max() - 1.0) > 0.0001:
betas = rescale_zero_terminal_snr(betas)
return betas
def sigma_schedule(schedule='cosine',
num_timesteps=1000,
zero_terminal_snr=False,
**kwargs):
# compute betas
betas = {
'logsnr_cosine_interp': logsnr_cosine_interp_schedule,
'linear': linear_schedule,
'linear_sd': linear_sd_schedule,
'quadratic': quadratic_schedule,
'cosine': cosine_schedule
}[schedule](num_timesteps, **kwargs)
if schedule == 'logsnr_cosine_interp':
sigma = betas
else:
sigma = betas_to_sigmas(betas)
if zero_terminal_snr and abs(sigma.max() - 1.0) > 0.0001:
sigma = rescale_zero_terminal_snr(sigma)
return sigma
def linear_schedule(num_timesteps, init_beta, last_beta, **kwargs):
scale = 1000.0 / num_timesteps
init_beta = init_beta or scale * 0.0001
ast_beta = last_beta or scale * 0.02
return torch.linspace(init_beta, last_beta, num_timesteps, dtype=torch.float64)
def logsnr_cosine_interp_schedule(
num_timesteps,
scale_min=2,
scale_max=4,
logsnr_min=-15,
logsnr_max=15,
**kwargs):
return logsnrs_to_sigmas(
_logsnr_cosine_interp(num_timesteps, logsnr_min, logsnr_max, scale_min, scale_max))
def linear_sd_schedule(num_timesteps, init_beta, last_beta, **kwargs):
return torch.linspace(init_beta ** 0.5, last_beta ** 0.5, num_timesteps, dtype=torch.float64) ** 2
def quadratic_schedule(num_timesteps, init_beta, last_beta, **kwargs):
init_beta = init_beta or 0.0015
last_beta = last_beta or 0.0195
return torch.linspace(init_beta ** 0.5, last_beta ** 0.5, num_timesteps, dtype=torch.float64) ** 2
def cosine_schedule(num_timesteps, cosine_s=0.008, **kwargs):
betas = []
for step in range(num_timesteps):
t1 = step / num_timesteps
t2 = (step + 1) / num_timesteps
fn = lambda u: math.cos((u + cosine_s) / (1 + cosine_s) * math.pi / 2) ** 2
betas.append(min(1.0 - fn(t2) / fn(t1), 0.999))
return torch.tensor(betas, dtype=torch.float64)
# def cosine_schedule(n, cosine_s=0.008, **kwargs):
# ramp = torch.linspace(0, 1, n + 1)
# square_alphas = torch.cos((ramp + cosine_s) / (1 + cosine_s) * torch.pi / 2) ** 2
# betas = (1 - square_alphas[1:] / square_alphas[:-1]).clamp(max=0.999)
# return betas_to_sigmas(betas)
def betas_to_sigmas(betas):
return torch.sqrt(1 - torch.cumprod(1 - betas, dim=0))
def sigmas_to_betas(sigmas):
square_alphas = 1 - sigmas**2
betas = 1 - torch.cat(
[square_alphas[:1], square_alphas[1:] / square_alphas[:-1]])
return betas
def sigmas_to_logsnrs(sigmas):
square_sigmas = sigmas**2
return torch.log(square_sigmas / (1 - square_sigmas))
def _logsnr_cosine(n, logsnr_min=-15, logsnr_max=15):
t_min = math.atan(math.exp(-0.5 * logsnr_min))
t_max = math.atan(math.exp(-0.5 * logsnr_max))
t = torch.linspace(1, 0, n)
logsnrs = -2 * torch.log(torch.tan(t_min + t * (t_max - t_min)))
return logsnrs
def _logsnr_cosine_shifted(n, logsnr_min=-15, logsnr_max=15, scale=2):
logsnrs = _logsnr_cosine(n, logsnr_min, logsnr_max)
logsnrs += 2 * math.log(1 / scale)
return logsnrs
def karras_schedule(n, sigma_min=0.002, sigma_max=80.0, rho=7.0):
ramp = torch.linspace(1, 0, n)
min_inv_rho = sigma_min**(1 / rho)
max_inv_rho = sigma_max**(1 / rho)
sigmas = (max_inv_rho + ramp * (min_inv_rho - max_inv_rho))**rho
sigmas = torch.sqrt(sigmas**2 / (1 + sigmas**2))
return sigmas
def _logsnr_cosine_interp(n,
logsnr_min=-15,
logsnr_max=15,
scale_min=2,
scale_max=4):
t = torch.linspace(1, 0, n)
logsnrs_min = _logsnr_cosine_shifted(n, logsnr_min, logsnr_max, scale_min)
logsnrs_max = _logsnr_cosine_shifted(n, logsnr_min, logsnr_max, scale_max)
logsnrs = t * logsnrs_min + (1 - t) * logsnrs_max
return logsnrs
def logsnrs_to_sigmas(logsnrs):
return torch.sqrt(torch.sigmoid(-logsnrs))
def rescale_zero_terminal_snr(betas):
"""
Rescale Schedule to Zero Terminal SNR
"""
# Convert betas to alphas_bar_sqrt
alphas = 1 - betas
alphas_bar = alphas.cumprod(0)
alphas_bar_sqrt = alphas_bar.sqrt()
# Store old values. 8 alphas_bar_sqrt_0 = a
alphas_bar_sqrt_0 = alphas_bar_sqrt[0].clone()
alphas_bar_sqrt_T = alphas_bar_sqrt[-1].clone()
# Shift so last timestep is zero.
alphas_bar_sqrt -= alphas_bar_sqrt_T
# Scale so first timestep is back to old value.
alphas_bar_sqrt *= alphas_bar_sqrt_0 / (alphas_bar_sqrt_0 - alphas_bar_sqrt_T)
# Convert alphas_bar_sqrt to betas
alphas_bar = alphas_bar_sqrt ** 2
alphas = alphas_bar[1:] / alphas_bar[:-1]
alphas = torch.cat([alphas_bar[0:1], alphas])
betas = 1 - alphas
return betas
@@ -0,0 +1,179 @@
import torch
from torch import nn
import torch.nn.functional as F
import open_clip
from functools import partial
from utils.registry_class import EMBEDMANAGER
DEFAULT_PLACEHOLDER_TOKEN = ["*"]
PROGRESSIVE_SCALE = 2000
per_img_token_list = [
'א', 'ב', 'ג', 'ד', 'ה', 'ו', 'ז', 'ח', 'ט', 'י', 'כ', 'ל', 'מ', 'נ', 'ס', 'ע', 'פ', 'צ', 'ק', 'ר', 'ש', 'ת',
]
def get_clip_token_for_string(string):
tokens = open_clip.tokenize(string)
return tokens[0, 1]
def get_embedding_for_clip_token(embedder, token):
return embedder(token.unsqueeze(0))[0]
@EMBEDMANAGER.register_class()
class EmbeddingManager(nn.Module):
def __init__(
self,
embedder,
placeholder_strings=None,
initializer_words=None,
per_image_tokens=False,
num_vectors_per_token=1,
progressive_words=False,
temporal_prompt_length=1,
token_dim=1024,
**kwargs
):
super().__init__()
self.string_to_token_dict = {}
self.string_to_param_dict = nn.ParameterDict()
self.initial_embeddings = nn.ParameterDict() # These should not be optimized
self.progressive_words = progressive_words
self.progressive_counter = 0
self.max_vectors_per_token = num_vectors_per_token
get_embedding_for_tkn = partial(get_embedding_for_clip_token, embedder.model.token_embedding.cpu())
if per_image_tokens:
placeholder_strings.extend(per_img_token_list)
for idx, placeholder_string in enumerate(placeholder_strings):
token = get_clip_token_for_string(placeholder_string)
if initializer_words and idx < len(initializer_words):
init_word_token = get_clip_token_for_string(initializer_words[idx])
with torch.no_grad():
init_word_embedding = get_embedding_for_tkn(init_word_token)
token_params = torch.nn.Parameter(init_word_embedding.unsqueeze(0).repeat(num_vectors_per_token, 1), requires_grad=True)
self.initial_embeddings[placeholder_string] = torch.nn.Parameter(init_word_embedding.unsqueeze(0).repeat(num_vectors_per_token, 1), requires_grad=False)
else:
token_params = torch.nn.Parameter(torch.rand(size=(num_vectors_per_token, token_dim), requires_grad=True))
self.string_to_token_dict[placeholder_string] = token
self.string_to_param_dict[placeholder_string] = token_params
def forward(
self,
tokenized_text,
embedded_text,
):
b, n, device = *tokenized_text.shape, tokenized_text.device
for placeholder_string, placeholder_token in self.string_to_token_dict.items():
placeholder_embedding = self.string_to_param_dict[placeholder_string].to(device)
if self.max_vectors_per_token == 1: # If there's only one vector per token, we can do a simple replacement
placeholder_idx = torch.where(tokenized_text == placeholder_token.to(device))
embedded_text[placeholder_idx] = placeholder_embedding
else: # otherwise, need to insert and keep track of changing indices
if self.progressive_words:
self.progressive_counter += 1
max_step_tokens = 1 + self.progressive_counter // PROGRESSIVE_SCALE
else:
max_step_tokens = self.max_vectors_per_token
num_vectors_for_token = min(placeholder_embedding.shape[0], max_step_tokens)
placeholder_rows, placeholder_cols = torch.where(tokenized_text == placeholder_token.to(device))
if placeholder_rows.nelement() == 0:
continue
sorted_cols, sort_idx = torch.sort(placeholder_cols, descending=True)
sorted_rows = placeholder_rows[sort_idx]
for idx in range(len(sorted_rows)):
row = sorted_rows[idx]
col = sorted_cols[idx]
new_token_row = torch.cat([tokenized_text[row][:col], placeholder_token.repeat(num_vectors_for_token).to(device), tokenized_text[row][col + 1:]], axis=0)[:n]
new_embed_row = torch.cat([embedded_text[row][:col], placeholder_embedding[:num_vectors_for_token], embedded_text[row][col + 1:]], axis=0)[:n]
embedded_text[row] = new_embed_row
tokenized_text[row] = new_token_row
return embedded_text
def forward_with_text_img(
self,
tokenized_text,
embedded_text,
embedded_img,
):
device = tokenized_text.device
for placeholder_string, placeholder_token in self.string_to_token_dict.items():
placeholder_embedding = self.string_to_param_dict[placeholder_string].to(device)
placeholder_idx = torch.where(tokenized_text == placeholder_token.to(device))
embedded_text[placeholder_idx] = embedded_text[placeholder_idx] + embedded_img + placeholder_embedding
return embedded_text
def forward_with_text(
self,
tokenized_text,
embedded_text
):
device = tokenized_text.device
for placeholder_string, placeholder_token in self.string_to_token_dict.items():
placeholder_embedding = self.string_to_param_dict[placeholder_string].to(device)
placeholder_idx = torch.where(tokenized_text == placeholder_token.to(device))
embedded_text[placeholder_idx] = embedded_text[placeholder_idx] + placeholder_embedding
return embedded_text
def save(self, ckpt_path):
torch.save({"string_to_token": self.string_to_token_dict,
"string_to_param": self.string_to_param_dict}, ckpt_path)
def load(self, ckpt_path):
ckpt = torch.load(ckpt_path, map_location='cpu')
string_to_token = ckpt["string_to_token"]
string_to_param = ckpt["string_to_param"]
for string, token in string_to_token.items():
self.string_to_token_dict[string] = token
for string, param in string_to_param.items():
self.string_to_param_dict[string] = param
def get_embedding_norms_squared(self):
all_params = torch.cat(list(self.string_to_param_dict.values()), axis=0) # num_placeholders x embedding_dim
param_norm_squared = (all_params * all_params).sum(axis=-1) # num_placeholders
return param_norm_squared
def embedding_parameters(self):
return self.string_to_param_dict.parameters()
def embedding_to_coarse_loss(self):
loss = 0.
num_embeddings = len(self.initial_embeddings)
for key in self.initial_embeddings:
optimized = self.string_to_param_dict[key]
coarse = self.initial_embeddings[key].clone().to(optimized.device)
loss = loss + (optimized - coarse) @ (optimized - coarse).T / num_embeddings
return loss
@@ -0,0 +1,2 @@
from .unet_unianimate import *
+103
View File
@@ -0,0 +1,103 @@
import torch
import torch.nn as nn
import torch.cuda.amp as amp
import torch.nn.functional as F
import math
import os
import time
import numpy as np
import random
# from flash_attn.flash_attention import FlashAttention
class FlashAttentionBlock(nn.Module):
def __init__(self, dim, context_dim=None, num_heads=None, head_dim=None, batch_size=4):
# consider head_dim first, then num_heads
num_heads = dim // head_dim if head_dim else num_heads
head_dim = dim // num_heads
assert num_heads * head_dim == dim
super(FlashAttentionBlock, self).__init__()
self.dim = dim
self.context_dim = context_dim
self.num_heads = num_heads
self.head_dim = head_dim
self.scale = math.pow(head_dim, -0.25)
# layers
self.norm = nn.GroupNorm(32, dim)
self.to_qkv = nn.Conv2d(dim, dim * 3, 1)
if context_dim is not None:
self.context_kv = nn.Linear(context_dim, dim * 2)
self.proj = nn.Conv2d(dim, dim, 1)
if self.head_dim <= 128 and (self.head_dim % 8) == 0:
new_scale = math.pow(head_dim, -0.5)
self.flash_attn = FlashAttention(softmax_scale=None, attention_dropout=0.0)
# zero out the last layer params
nn.init.zeros_(self.proj.weight)
# self.apply(self._init_weight)
def _init_weight(self, module):
if isinstance(module, nn.Linear):
module.weight.data.normal_(mean=0.0, std=0.15)
if module.bias is not None:
module.bias.data.zero_()
elif isinstance(module, nn.Conv2d):
module.weight.data.normal_(mean=0.0, std=0.15)
if module.bias is not None:
module.bias.data.zero_()
def forward(self, x, context=None):
r"""x: [B, C, H, W].
context: [B, L, C] or None.
"""
identity = x
b, c, h, w, n, d = *x.size(), self.num_heads, self.head_dim
# compute query, key, value
x = self.norm(x)
q, k, v = self.to_qkv(x).view(b, n * 3, d, h * w).chunk(3, dim=1)
if context is not None:
ck, cv = self.context_kv(context).reshape(b, -1, n * 2, d).permute(0, 2, 3, 1).chunk(2, dim=1)
k = torch.cat([ck, k], dim=-1)
v = torch.cat([cv, v], dim=-1)
cq = torch.zeros([b, n, d, 4], dtype=q.dtype, device=q.device)
q = torch.cat([q, cq], dim=-1)
qkv = torch.cat([q,k,v], dim=1)
origin_dtype = qkv.dtype
qkv = qkv.permute(0, 3, 1, 2).reshape(b, -1, 3, n, d).half().contiguous()
out, _ = self.flash_attn(qkv)
out.to(origin_dtype)
if context is not None:
out = out[:, :-4, :, :]
out = out.permute(0, 2, 3, 1).reshape(b, c, h, w)
# output
x = self.proj(out)
return x + identity
if __name__ == '__main__':
batch_size = 8
flash_net = FlashAttentionBlock(dim=1280, context_dim=512, num_heads=None, head_dim=64, batch_size=batch_size).cuda()
x = torch.randn([batch_size, 1280, 32, 32], dtype=torch.float32).cuda()
context = torch.randn([batch_size, 4, 512], dtype=torch.float32).cuda()
# context = None
flash_net.eval()
with amp.autocast(enabled=True):
# warm up
for i in range(5):
y = flash_net(x, context)
torch.cuda.synchronize()
s1 = time.time()
for i in range(10):
y = flash_net(x, context)
torch.cuda.synchronize()
s2 = time.time()
print(f'Average cost time {(s2-s1)*1000/10} ms')
@@ -0,0 +1,659 @@
import math
import torch
import xformers
import xformers.ops
import torch.nn as nn
from einops import rearrange
import torch.nn.functional as F
from rotary_embedding_torch import RotaryEmbedding
from fairscale.nn.checkpoint import checkpoint_wrapper
from .util import *
# from .mha_flash import FlashAttentionBlock
from utils.registry_class import MODEL
USE_TEMPORAL_TRANSFORMER = True
class PreNormattention(nn.Module):
def __init__(self, dim, fn):
super().__init__()
self.norm = nn.LayerNorm(dim)
self.fn = fn
def forward(self, x, **kwargs):
return self.fn(self.norm(x), **kwargs) + x
class PreNormattention_qkv(nn.Module):
def __init__(self, dim, fn):
super().__init__()
self.norm = nn.LayerNorm(dim)
self.fn = fn
def forward(self, q, k, v, **kwargs):
return self.fn(self.norm(q), self.norm(k), self.norm(v), **kwargs) + q
class Attention(nn.Module):
def __init__(self, dim, heads = 8, dim_head = 64, dropout = 0.):
super().__init__()
inner_dim = dim_head * heads
project_out = not (heads == 1 and dim_head == dim)
self.heads = heads
self.scale = dim_head ** -0.5
self.attend = nn.Softmax(dim = -1)
self.to_qkv = nn.Linear(dim, inner_dim * 3, bias = False)
self.to_out = nn.Sequential(
nn.Linear(inner_dim, dim),
nn.Dropout(dropout)
) if project_out else nn.Identity()
def forward(self, x):
b, n, _, h = *x.shape, self.heads
qkv = self.to_qkv(x).chunk(3, dim = -1)
q, k, v = map(lambda t: rearrange(t, 'b n (h d) -> b h n d', h = h), qkv)
dots = einsum('b h i d, b h j d -> b h i j', q, k) * self.scale
attn = self.attend(dots)
out = einsum('b h i j, b h j d -> b h i d', attn, v)
out = rearrange(out, 'b h n d -> b n (h d)')
return self.to_out(out)
class Attention_qkv(nn.Module):
def __init__(self, dim, heads = 8, dim_head = 64, dropout = 0.):
super().__init__()
inner_dim = dim_head * heads
project_out = not (heads == 1 and dim_head == dim)
self.heads = heads
self.scale = dim_head ** -0.5
self.attend = nn.Softmax(dim = -1)
self.to_q = nn.Linear(dim, inner_dim, bias = False)
self.to_k = nn.Linear(dim, inner_dim, bias = False)
self.to_v = nn.Linear(dim, inner_dim, bias = False)
self.to_out = nn.Sequential(
nn.Linear(inner_dim, dim),
nn.Dropout(dropout)
) if project_out else nn.Identity()
def forward(self, q, k, v):
b, n, _, h = *q.shape, self.heads
bk = k.shape[0]
q = self.to_q(q)
k = self.to_k(k)
v = self.to_v(v)
q = rearrange(q, 'b n (h d) -> b h n d', h = h)
k = rearrange(k, 'b n (h d) -> b h n d', b=bk, h = h)
v = rearrange(v, 'b n (h d) -> b h n d', b=bk, h = h)
dots = einsum('b h i d, b h j d -> b h i j', q, k) * self.scale
attn = self.attend(dots)
out = einsum('b h i j, b h j d -> b h i d', attn, v)
out = rearrange(out, 'b h n d -> b n (h d)')
return self.to_out(out)
class PostNormattention(nn.Module):
def __init__(self, dim, fn):
super().__init__()
self.norm = nn.LayerNorm(dim)
self.fn = fn
def forward(self, x, **kwargs):
return self.norm(self.fn(x, **kwargs) + x)
class Transformer_v2(nn.Module):
def __init__(self, heads=8, dim=2048, dim_head_k=256, dim_head_v=256, dropout_atte = 0.05, mlp_dim=2048, dropout_ffn = 0.05, depth=1):
super().__init__()
self.layers = nn.ModuleList([])
self.depth = depth
for _ in range(depth):
self.layers.append(nn.ModuleList([
PreNormattention(dim, Attention(dim, heads = heads, dim_head = dim_head_k, dropout = dropout_atte)),
FeedForward(dim, mlp_dim, dropout = dropout_ffn),
]))
def forward(self, x):
for attn, ff in self.layers[:1]:
x = attn(x)
x = ff(x) + x
if self.depth > 1:
for attn, ff in self.layers[1:]:
x = attn(x)
x = ff(x) + x
return x
class DropPath(nn.Module):
r"""DropPath but without rescaling and supports optional all-zero and/or all-keep.
"""
def __init__(self, p):
super(DropPath, self).__init__()
self.p = p
def forward(self, *args, zero=None, keep=None):
if not self.training:
return args[0] if len(args) == 1 else args
# params
x = args[0]
b = x.size(0)
n = (torch.rand(b) < self.p).sum()
# non-zero and non-keep mask
mask = x.new_ones(b, dtype=torch.bool)
if keep is not None:
mask[keep] = False
if zero is not None:
mask[zero] = False
# drop-path index
index = torch.where(mask)[0]
index = index[torch.randperm(len(index))[:n]]
if zero is not None:
index = torch.cat([index, torch.where(zero)[0]], dim=0)
# drop-path multiplier
multiplier = x.new_ones(b)
multiplier[index] = 0.0
output = tuple(u * self.broadcast(multiplier, u) for u in args)
return output[0] if len(args) == 1 else output
def broadcast(self, src, dst):
assert src.size(0) == dst.size(0)
shape = (dst.size(0), ) + (1, ) * (dst.ndim - 1)
return src.view(shape)
@MODEL.register_class()
class UNetSD_UniAnimate(nn.Module):
def __init__(self,
config=None,
in_dim=4,
dim=512,
y_dim=512,
context_dim=1024,
hist_dim = 156,
concat_dim = 8,
out_dim=6,
dim_mult=[1, 2, 3, 4],
num_heads=None,
head_dim=64,
num_res_blocks=3,
attn_scales=[1 / 2, 1 / 4, 1 / 8],
use_scale_shift_norm=True,
dropout=0.1,
temporal_attn_times=1,
temporal_attention = True,
use_checkpoint=False,
use_image_dataset=False,
use_fps_condition= False,
use_sim_mask = False,
misc_dropout = 0.5,
training=True,
inpainting=True,
p_all_zero=0.1,
p_all_keep=0.1,
zero_y = None,
black_image_feature = None,
adapter_transformer_layers = 1,
num_tokens=4,
**kwargs
):
embed_dim = dim * 4
num_heads=num_heads if num_heads else dim//32
super(UNetSD_UniAnimate, self).__init__()
self.zero_y = zero_y
self.black_image_feature = black_image_feature
self.cfg = config
self.in_dim = in_dim
self.dim = dim
self.y_dim = y_dim
self.context_dim = context_dim
self.num_tokens = num_tokens
self.hist_dim = hist_dim
self.concat_dim = concat_dim
self.embed_dim = embed_dim
self.out_dim = out_dim
self.dim_mult = dim_mult
self.num_heads = num_heads
self.head_dim = head_dim
self.num_res_blocks = num_res_blocks
self.attn_scales = attn_scales
self.use_scale_shift_norm = use_scale_shift_norm
self.temporal_attn_times = temporal_attn_times
self.temporal_attention = temporal_attention
self.use_checkpoint = use_checkpoint
self.use_image_dataset = use_image_dataset
self.use_fps_condition = use_fps_condition
self.use_sim_mask = use_sim_mask
self.training=training
self.inpainting = inpainting
self.video_compositions = self.cfg.video_compositions
self.misc_dropout = misc_dropout
self.p_all_zero = p_all_zero
self.p_all_keep = p_all_keep
use_linear_in_temporal = False
transformer_depth = 1
disabled_sa = False
# params
enc_dims = [dim * u for u in [1] + dim_mult]
dec_dims = [dim * u for u in [dim_mult[-1]] + dim_mult[::-1]]
shortcut_dims = []
scale = 1.0
self.resolution = config.resolution
# embeddings
self.time_embed = nn.Sequential(
nn.Linear(dim, embed_dim),
nn.SiLU(),
nn.Linear(embed_dim, embed_dim))
if 'image' in self.video_compositions:
self.pre_image_condition = nn.Sequential(
nn.Linear(self.context_dim, self.context_dim),
nn.SiLU(),
nn.Linear(self.context_dim, self.context_dim*self.num_tokens))
if 'local_image' in self.video_compositions:
self.local_image_embedding = nn.Sequential(
nn.Conv2d(3, concat_dim * 4, 3, padding=1),
nn.SiLU(),
nn.AdaptiveAvgPool2d((self.resolution[1]//2, self.resolution[0]//2)),
nn.Conv2d(concat_dim * 4, concat_dim * 4, 3, stride=2, padding=1),
nn.SiLU(),
nn.Conv2d(concat_dim * 4, concat_dim, 3, stride=2, padding=1))
self.local_image_embedding_after = Transformer_v2(heads=2, dim=concat_dim, dim_head_k=concat_dim, dim_head_v=concat_dim, dropout_atte = 0.05, mlp_dim=concat_dim, dropout_ffn = 0.05, depth=adapter_transformer_layers)
if 'dwpose' in self.video_compositions:
self.dwpose_embedding = nn.Sequential(
nn.Conv2d(3, concat_dim * 4, 3, padding=1),
nn.SiLU(),
nn.AdaptiveAvgPool2d((self.resolution[1]//2, self.resolution[0]//2)),
nn.Conv2d(concat_dim * 4, concat_dim * 4, 3, stride=2, padding=1),
nn.SiLU(),
nn.Conv2d(concat_dim * 4, concat_dim, 3, stride=2, padding=1))
self.dwpose_embedding_after = Transformer_v2(heads=2, dim=concat_dim, dim_head_k=concat_dim, dim_head_v=concat_dim, dropout_atte = 0.05, mlp_dim=concat_dim, dropout_ffn = 0.05, depth=adapter_transformer_layers)
if 'randomref_pose' in self.video_compositions:
randomref_dim = 4
self.randomref_pose2_embedding = nn.Sequential(
nn.Conv2d(3, concat_dim * 4, 3, padding=1),
nn.SiLU(),
nn.AdaptiveAvgPool2d((self.resolution[1]//2, self.resolution[0]//2)),
nn.Conv2d(concat_dim * 4, concat_dim * 4, 3, stride=2, padding=1),
nn.SiLU(),
nn.Conv2d(concat_dim * 4, concat_dim+randomref_dim, 3, stride=2, padding=1))
self.randomref_pose2_embedding_after = Transformer_v2(heads=2, dim=concat_dim+randomref_dim, dim_head_k=concat_dim+randomref_dim, dim_head_v=concat_dim+randomref_dim, dropout_atte = 0.05, mlp_dim=concat_dim+randomref_dim, dropout_ffn = 0.05, depth=adapter_transformer_layers)
if 'randomref' in self.video_compositions:
randomref_dim = 4
self.randomref_embedding2 = nn.Sequential(
nn.Conv2d(randomref_dim, concat_dim * 4, 3, padding=1),
nn.SiLU(),
nn.Conv2d(concat_dim * 4, concat_dim * 4, 3, stride=1, padding=1),
nn.SiLU(),
nn.Conv2d(concat_dim * 4, concat_dim+randomref_dim, 3, stride=1, padding=1))
self.randomref_embedding_after2 = Transformer_v2(heads=2, dim=concat_dim+randomref_dim, dim_head_k=concat_dim+randomref_dim, dim_head_v=concat_dim+randomref_dim, dropout_atte = 0.05, mlp_dim=concat_dim+randomref_dim, dropout_ffn = 0.05, depth=adapter_transformer_layers)
### Condition Dropout
self.misc_dropout = DropPath(misc_dropout)
if temporal_attention and not USE_TEMPORAL_TRANSFORMER:
self.rotary_emb = RotaryEmbedding(min(32, head_dim))
self.time_rel_pos_bias = RelativePositionBias(heads = num_heads, max_distance = 32) # realistically will not be able to generate that many frames of video... yet
if self.use_fps_condition:
self.fps_embedding = nn.Sequential(
nn.Linear(dim, embed_dim),
nn.SiLU(),
nn.Linear(embed_dim, embed_dim))
nn.init.zeros_(self.fps_embedding[-1].weight)
nn.init.zeros_(self.fps_embedding[-1].bias)
# encoder
self.input_blocks = nn.ModuleList()
self.pre_image = nn.Sequential()
init_block = nn.ModuleList([nn.Conv2d(self.in_dim + concat_dim, dim, 3, padding=1)])
#### need an initial temporal attention?
if temporal_attention:
if USE_TEMPORAL_TRANSFORMER:
init_block.append(TemporalTransformer(dim, num_heads, head_dim, depth=transformer_depth, context_dim=context_dim,
disable_self_attn=disabled_sa, use_linear=use_linear_in_temporal, multiply_zero=use_image_dataset))
else:
init_block.append(TemporalAttentionMultiBlock(dim, num_heads, head_dim, rotary_emb=self.rotary_emb, temporal_attn_times=temporal_attn_times, use_image_dataset=use_image_dataset))
self.input_blocks.append(init_block)
shortcut_dims.append(dim)
for i, (in_dim, out_dim) in enumerate(zip(enc_dims[:-1], enc_dims[1:])):
for j in range(num_res_blocks):
block = nn.ModuleList([ResBlock(in_dim, embed_dim, dropout, out_channels=out_dim, use_scale_shift_norm=False, use_image_dataset=use_image_dataset,)])
if scale in attn_scales:
block.append(
SpatialTransformer(
out_dim, out_dim // head_dim, head_dim, depth=1, context_dim=self.context_dim,
disable_self_attn=False, use_linear=True
)
)
if self.temporal_attention:
if USE_TEMPORAL_TRANSFORMER:
block.append(TemporalTransformer(out_dim, out_dim // head_dim, head_dim, depth=transformer_depth, context_dim=context_dim,
disable_self_attn=disabled_sa, use_linear=use_linear_in_temporal, multiply_zero=use_image_dataset))
else:
block.append(TemporalAttentionMultiBlock(out_dim, num_heads, head_dim, rotary_emb = self.rotary_emb, use_image_dataset=use_image_dataset, use_sim_mask=use_sim_mask, temporal_attn_times=temporal_attn_times))
in_dim = out_dim
self.input_blocks.append(block)
shortcut_dims.append(out_dim)
# downsample
if i != len(dim_mult) - 1 and j == num_res_blocks - 1:
downsample = Downsample(
out_dim, True, dims=2, out_channels=out_dim
)
shortcut_dims.append(out_dim)
scale /= 2.0
self.input_blocks.append(downsample)
# middle
self.middle_block = nn.ModuleList([
ResBlock(out_dim, embed_dim, dropout, use_scale_shift_norm=False, use_image_dataset=use_image_dataset,),
SpatialTransformer(
out_dim, out_dim // head_dim, head_dim, depth=1, context_dim=self.context_dim,
disable_self_attn=False, use_linear=True
)])
if self.temporal_attention:
if USE_TEMPORAL_TRANSFORMER:
self.middle_block.append(
TemporalTransformer(
out_dim, out_dim // head_dim, head_dim, depth=transformer_depth, context_dim=context_dim,
disable_self_attn=disabled_sa, use_linear=use_linear_in_temporal,
multiply_zero=use_image_dataset,
)
)
else:
self.middle_block.append(TemporalAttentionMultiBlock(out_dim, num_heads, head_dim, rotary_emb = self.rotary_emb, use_image_dataset=use_image_dataset, use_sim_mask=use_sim_mask, temporal_attn_times=temporal_attn_times))
self.middle_block.append(ResBlock(out_dim, embed_dim, dropout, use_scale_shift_norm=False))
# decoder
self.output_blocks = nn.ModuleList()
for i, (in_dim, out_dim) in enumerate(zip(dec_dims[:-1], dec_dims[1:])):
for j in range(num_res_blocks + 1):
block = nn.ModuleList([ResBlock(in_dim + shortcut_dims.pop(), embed_dim, dropout, out_dim, use_scale_shift_norm=False, use_image_dataset=use_image_dataset, )])
if scale in attn_scales:
block.append(
SpatialTransformer(
out_dim, out_dim // head_dim, head_dim, depth=1, context_dim=1024,
disable_self_attn=False, use_linear=True
)
)
if self.temporal_attention:
if USE_TEMPORAL_TRANSFORMER:
block.append(
TemporalTransformer(
out_dim, out_dim // head_dim, head_dim, depth=transformer_depth, context_dim=context_dim,
disable_self_attn=disabled_sa, use_linear=use_linear_in_temporal, multiply_zero=use_image_dataset
)
)
else:
block.append(TemporalAttentionMultiBlock(out_dim, num_heads, head_dim, rotary_emb =self.rotary_emb, use_image_dataset=use_image_dataset, use_sim_mask=use_sim_mask, temporal_attn_times=temporal_attn_times))
in_dim = out_dim
# upsample
if i != len(dim_mult) - 1 and j == num_res_blocks:
upsample = Upsample(out_dim, True, dims=2.0, out_channels=out_dim)
scale *= 2.0
block.append(upsample)
self.output_blocks.append(block)
# head
self.out = nn.Sequential(
nn.GroupNorm(32, out_dim),
nn.SiLU(),
nn.Conv2d(out_dim, self.out_dim, 3, padding=1))
# zero out the last layer params
nn.init.zeros_(self.out[-1].weight)
def forward(self,
x,
t,
y = None,
depth = None,
image = None,
motion = None,
local_image = None,
single_sketch = None,
masked = None,
canny = None,
sketch = None,
dwpose = None,
randomref = None,
histogram = None,
fps = None,
video_mask = None,
focus_present_mask = None,
prob_focus_present = 0., # probability at which a given batch sample will focus on the present (0. is all off, 1. is completely arrested attention across time)
mask_last_frame_num = 0 # mask last frame num
):
assert self.inpainting or masked is None, 'inpainting is not supported'
batch, c, f, h, w= x.shape
frames = f
device = x.device
self.batch = batch
#### image and video joint training, if mask_last_frame_num is set, prob_focus_present will be ignored
if mask_last_frame_num > 0:
focus_present_mask = None
video_mask[-mask_last_frame_num:] = False
else:
focus_present_mask = default(focus_present_mask, lambda: prob_mask_like((batch,), prob_focus_present, device = device))
if self.temporal_attention and not USE_TEMPORAL_TRANSFORMER:
time_rel_pos_bias = self.time_rel_pos_bias(x.shape[2], device = x.device)
else:
time_rel_pos_bias = None
# all-zero and all-keep masks
zero = torch.zeros(batch, dtype=torch.bool).to(x.device)
keep = torch.zeros(batch, dtype=torch.bool).to(x.device)
if self.training:
nzero = (torch.rand(batch) < self.p_all_zero).sum()
nkeep = (torch.rand(batch) < self.p_all_keep).sum()
index = torch.randperm(batch)
zero[index[0:nzero]] = True
keep[index[nzero:nzero + nkeep]] = True
assert not (zero & keep).any()
misc_dropout = partial(self.misc_dropout, zero = zero, keep = keep)
concat = x.new_zeros(batch, self.concat_dim, f, h, w)
# local_image_embedding (first frame)
if local_image is not None:
local_image = rearrange(local_image, 'b c f h w -> (b f) c h w')
local_image = self.local_image_embedding(local_image)
h = local_image.shape[2]
local_image = self.local_image_embedding_after(rearrange(local_image, '(b f) c h w -> (b h w) f c', b = batch))
local_image = rearrange(local_image, '(b h w) f c -> b c f h w', b = batch, h = h)
concat = concat + misc_dropout(local_image)
if dwpose is not None:
if 'randomref_pose' in self.video_compositions:
dwpose_random_ref = dwpose[:,:,:1].clone()
dwpose = dwpose[:,:,1:]
dwpose = rearrange(dwpose, 'b c f h w -> (b f) c h w')
dwpose = self.dwpose_embedding(dwpose)
h = dwpose.shape[2]
dwpose = self.dwpose_embedding_after(rearrange(dwpose, '(b f) c h w -> (b h w) f c', b = batch))
dwpose = rearrange(dwpose, '(b h w) f c -> b c f h w', b = batch, h = h)
concat = concat + misc_dropout(dwpose)
randomref_b = x.new_zeros(batch, self.concat_dim+4, 1, h, w)
if randomref is not None:
randomref = rearrange(randomref[:,:,:1,], 'b c f h w -> (b f) c h w')
randomref = self.randomref_embedding2(randomref)
h = randomref.shape[2]
randomref = self.randomref_embedding_after2(rearrange(randomref, '(b f) c h w -> (b h w) f c', b = batch))
if 'randomref_pose' in self.video_compositions:
dwpose_random_ref = rearrange(dwpose_random_ref, 'b c f h w -> (b f) c h w')
dwpose_random_ref = self.randomref_pose2_embedding(dwpose_random_ref)
dwpose_random_ref = self.randomref_pose2_embedding_after(rearrange(dwpose_random_ref, '(b f) c h w -> (b h w) f c', b = batch))
randomref = randomref + dwpose_random_ref
randomref_a = rearrange(randomref, '(b h w) f c -> b c f h w', b = batch, h = h)
randomref_b = randomref_b + randomref_a
x = torch.cat([randomref_b, torch.cat([x, concat], dim=1)], dim=2)
x = rearrange(x, 'b c f h w -> (b f) c h w')
x = self.pre_image(x)
x = rearrange(x, '(b f) c h w -> b c f h w', b = batch)
# embeddings
if self.use_fps_condition and fps is not None:
e = self.time_embed(sinusoidal_embedding(t, self.dim)) + self.fps_embedding(sinusoidal_embedding(fps, self.dim))
else:
e = self.time_embed(sinusoidal_embedding(t, self.dim))
context = x.new_zeros(batch, 0, self.context_dim)
if image is not None:
y_context = self.zero_y.repeat(batch, 1, 1)
context = torch.cat([context, y_context], dim=1)
image_context = misc_dropout(self.pre_image_condition(image).view(-1, self.num_tokens, self.context_dim)) # torch.cat([y[:,:-1,:], self.pre_image_condition(y[:,-1:,:]) ], dim=1)
context = torch.cat([context, image_context], dim=1)
else:
y_context = self.zero_y.repeat(batch, 1, 1)
context = torch.cat([context, y_context], dim=1)
image_context = torch.zeros_like(self.zero_y.repeat(batch, 1, 1))[:,:self.num_tokens]
context = torch.cat([context, image_context], dim=1)
# repeat f times for spatial e and context
e = e.repeat_interleave(repeats=f+1, dim=0)
context = context.repeat_interleave(repeats=f+1, dim=0)
## always in shape (b f) c h w, except for temporal layer
x = rearrange(x, 'b c f h w -> (b f) c h w')
# encoder
xs = []
for block in self.input_blocks:
x = self._forward_single(block, x, e, context, time_rel_pos_bias, focus_present_mask, video_mask)
xs.append(x)
# middle
for block in self.middle_block:
x = self._forward_single(block, x, e, context, time_rel_pos_bias,focus_present_mask, video_mask)
# decoder
for block in self.output_blocks:
x = torch.cat([x, xs.pop()], dim=1)
x = self._forward_single(block, x, e, context, time_rel_pos_bias,focus_present_mask, video_mask, reference=xs[-1] if len(xs) > 0 else None)
# head
x = self.out(x)
# reshape back to (b c f h w)
x = rearrange(x, '(b f) c h w -> b c f h w', b = batch)
return x[:,:,1:]
def _forward_single(self, module, x, e, context, time_rel_pos_bias, focus_present_mask, video_mask, reference=None):
if isinstance(module, ResidualBlock):
module = checkpoint_wrapper(module) if self.use_checkpoint else module
x = x.contiguous()
x = module(x, e, reference)
elif isinstance(module, ResBlock):
module = checkpoint_wrapper(module) if self.use_checkpoint else module
x = x.contiguous()
x = module(x, e, self.batch)
elif isinstance(module, SpatialTransformer):
module = checkpoint_wrapper(module) if self.use_checkpoint else module
x = module(x, context)
elif isinstance(module, TemporalTransformer):
module = checkpoint_wrapper(module) if self.use_checkpoint else module
x = rearrange(x, '(b f) c h w -> b c f h w', b = self.batch)
x = module(x, context)
x = rearrange(x, 'b c f h w -> (b f) c h w')
elif isinstance(module, CrossAttention):
module = checkpoint_wrapper(module) if self.use_checkpoint else module
x = module(x, context)
elif isinstance(module, MemoryEfficientCrossAttention):
module = checkpoint_wrapper(module) if self.use_checkpoint else module
x = module(x, context)
elif isinstance(module, BasicTransformerBlock):
module = checkpoint_wrapper(module) if self.use_checkpoint else module
x = module(x, context)
elif isinstance(module, FeedForward):
x = module(x, context)
elif isinstance(module, Upsample):
x = module(x)
elif isinstance(module, Downsample):
x = module(x)
elif isinstance(module, Resample):
x = module(x, reference)
elif isinstance(module, TemporalAttentionBlock):
module = checkpoint_wrapper(module) if self.use_checkpoint else module
x = rearrange(x, '(b f) c h w -> b c f h w', b = self.batch)
x = module(x, time_rel_pos_bias, focus_present_mask, video_mask)
x = rearrange(x, 'b c f h w -> (b f) c h w')
elif isinstance(module, TemporalAttentionMultiBlock):
module = checkpoint_wrapper(module) if self.use_checkpoint else module
x = rearrange(x, '(b f) c h w -> b c f h w', b = self.batch)
x = module(x, time_rel_pos_bias, focus_present_mask, video_mask)
x = rearrange(x, 'b c f h w -> (b f) c h w')
elif isinstance(module, InitTemporalConvBlock):
module = checkpoint_wrapper(module) if self.use_checkpoint else module
x = rearrange(x, '(b f) c h w -> b c f h w', b = self.batch)
x = module(x)
x = rearrange(x, 'b c f h w -> (b f) c h w')
elif isinstance(module, TemporalConvBlock):
module = checkpoint_wrapper(module) if self.use_checkpoint else module
x = rearrange(x, '(b f) c h w -> b c f h w', b = self.batch)
x = module(x)
x = rearrange(x, 'b c f h w -> (b f) c h w')
elif isinstance(module, nn.ModuleList):
for block in module:
x = self._forward_single(block, x, e, context, time_rel_pos_bias, focus_present_mask, video_mask, reference)
else:
x = module(x)
return x
File diff suppressed because it is too large Load Diff
View File
+78
View File
@@ -0,0 +1,78 @@
import os, yaml
from copy import deepcopy, copy
# def get prior and ldm config
def assign_prior_mudule_cfg(cfg):
'''
'''
#
prior_cfg = deepcopy(cfg)
vldm_cfg = deepcopy(cfg)
with open(cfg.prior_cfg, 'r') as f:
_cfg_update = yaml.load(f.read(), Loader=yaml.SafeLoader)
# _cfg_update = _cfg_update.cfg_dict
for k, v in _cfg_update.items():
if isinstance(v, dict) and k in cfg:
prior_cfg[k].update(v)
else:
prior_cfg[k] = v
with open(cfg.vldm_cfg, 'r') as f:
_cfg_update = yaml.load(f.read(), Loader=yaml.SafeLoader)
# _cfg_update = _cfg_update.cfg_dict
for k, v in _cfg_update.items():
if isinstance(v, dict) and k in cfg:
vldm_cfg[k].update(v)
else:
vldm_cfg[k] = v
return prior_cfg, vldm_cfg
# def get prior and ldm config
def assign_vldm_vsr_mudule_cfg(cfg):
'''
'''
#
vldm_cfg = deepcopy(cfg)
vsr_cfg = deepcopy(cfg)
with open(cfg.vldm_cfg, 'r') as f:
_cfg_update = yaml.load(f.read(), Loader=yaml.SafeLoader)
# _cfg_update = _cfg_update.cfg_dict
for k, v in _cfg_update.items():
if isinstance(v, dict) and k in cfg:
vldm_cfg[k].update(v)
else:
vldm_cfg[k] = v
with open(cfg.vsr_cfg, 'r') as f:
_cfg_update = yaml.load(f.read(), Loader=yaml.SafeLoader)
# _cfg_update = _cfg_update.cfg_dict
for k, v in _cfg_update.items():
if isinstance(v, dict) and k in cfg:
vsr_cfg[k].update(v)
else:
vsr_cfg[k] = v
return vldm_cfg, vsr_cfg
# def get prior and ldm config
def assign_signle_cfg(cfg, _cfg_update, tname):
'''
'''
#
vldm_cfg = deepcopy(cfg)
if os.path.exists(_cfg_update[tname]):
with open(_cfg_update[tname], 'r') as f:
_cfg_update = yaml.load(f.read(), Loader=yaml.SafeLoader)
# _cfg_update = _cfg_update.cfg_dict
for k, v in _cfg_update.items():
if isinstance(v, dict) and k in cfg:
vldm_cfg[k].update(v)
else:
vldm_cfg[k] = v
return vldm_cfg
+230
View File
@@ -0,0 +1,230 @@
import os
import yaml
import json
import copy
import argparse
import utils.logging as logging
logger = logging.get_logger(__name__)
class Config(object):
def __init__(self, load=True, cfg_dict=None, cfg_level=None):
self._level = "cfg" + ("." + cfg_level if cfg_level is not None else "")
if load:
self.args = self._parse_args()
logger.info("Loading config from {}.".format(self.args.cfg_file))
self.need_initialization = True
cfg_base = self._load_yaml(self.args) # self._initialize_cfg()
cfg_dict = self._load_yaml(self.args)
cfg_dict = self._merge_cfg_from_base(cfg_base, cfg_dict)
cfg_dict = self._update_from_args(cfg_dict)
self.cfg_dict = cfg_dict
self._update_dict(cfg_dict)
def _parse_args(self):
parser = argparse.ArgumentParser(
description="Argparser for configuring [code base name to think of] codebase"
)
parser.add_argument(
"--cfg",
dest="cfg_file",
help="Path to the configuration file",
default='configs/UniAnimate_infer.yaml'
)
parser.add_argument(
"--init_method",
help="Initialization method, includes TCP or shared file-system",
default="tcp://localhost:9999",
type=str,
)
parser.add_argument(
'--debug',
action='store_true',
default=False,
help='Into debug information'
)
parser.add_argument(
"opts",
help="other configurations",
default=None,
nargs=argparse.REMAINDER)
return parser.parse_args()
def _path_join(self, path_list):
path = ""
for p in path_list:
path+= p + '/'
return path[:-1]
def _update_from_args(self, cfg_dict):
args = self.args
for var in vars(args):
cfg_dict[var] = getattr(args, var)
return cfg_dict
def _initialize_cfg(self):
if self.need_initialization:
self.need_initialization = False
if os.path.exists('./configs/base.yaml'):
with open("./configs/base.yaml", 'r') as f:
cfg = yaml.load(f.read(), Loader=yaml.SafeLoader)
else:
with open(os.path.realpath(__file__).split('/')[-3] + "/configs/base.yaml", 'r') as f:
cfg = yaml.load(f.read(), Loader=yaml.SafeLoader)
return cfg
def _load_yaml(self, args, file_name=""):
assert args.cfg_file is not None
if not file_name == "": # reading from base file
with open(file_name, 'r') as f:
cfg = yaml.load(f.read(), Loader=yaml.SafeLoader)
else:
if os.getcwd().split("/")[-1] == args.cfg_file.split("/")[0]:
args.cfg_file = args.cfg_file.replace(os.getcwd().split("/")[-1], "./")
with open(args.cfg_file, 'r') as f:
cfg = yaml.load(f.read(), Loader=yaml.SafeLoader)
file_name = args.cfg_file
if "_BASE_RUN" not in cfg.keys() and "_BASE_MODEL" not in cfg.keys() and "_BASE" not in cfg.keys():
# return cfg if the base file is being accessed
cfg = self._merge_cfg_from_command_update(args, cfg)
return cfg
if "_BASE" in cfg.keys():
if cfg["_BASE"][1] == '.':
prev_count = cfg["_BASE"].count('..')
cfg_base_file = self._path_join(file_name.split('/')[:(-1-cfg["_BASE"].count('..'))] + cfg["_BASE"].split('/')[prev_count:])
else:
cfg_base_file = cfg["_BASE"].replace(
"./",
args.cfg_file.replace(args.cfg_file.split('/')[-1], "")
)
cfg_base = self._load_yaml(args, cfg_base_file)
cfg = self._merge_cfg_from_base(cfg_base, cfg)
else:
if "_BASE_RUN" in cfg.keys():
if cfg["_BASE_RUN"][1] == '.':
prev_count = cfg["_BASE_RUN"].count('..')
cfg_base_file = self._path_join(file_name.split('/')[:(-1-prev_count)] + cfg["_BASE_RUN"].split('/')[prev_count:])
else:
cfg_base_file = cfg["_BASE_RUN"].replace(
"./",
args.cfg_file.replace(args.cfg_file.split('/')[-1], "")
)
cfg_base = self._load_yaml(args, cfg_base_file)
cfg = self._merge_cfg_from_base(cfg_base, cfg, preserve_base=True)
if "_BASE_MODEL" in cfg.keys():
if cfg["_BASE_MODEL"][1] == '.':
prev_count = cfg["_BASE_MODEL"].count('..')
cfg_base_file = self._path_join(file_name.split('/')[:(-1-cfg["_BASE_MODEL"].count('..'))] + cfg["_BASE_MODEL"].split('/')[prev_count:])
else:
cfg_base_file = cfg["_BASE_MODEL"].replace(
"./",
args.cfg_file.replace(args.cfg_file.split('/')[-1], "")
)
cfg_base = self._load_yaml(args, cfg_base_file)
cfg = self._merge_cfg_from_base(cfg_base, cfg)
cfg = self._merge_cfg_from_command(args, cfg)
return cfg
def _merge_cfg_from_base(self, cfg_base, cfg_new, preserve_base=False):
for k,v in cfg_new.items():
if k in cfg_base.keys():
if isinstance(v, dict):
self._merge_cfg_from_base(cfg_base[k], v)
else:
cfg_base[k] = v
else:
if "BASE" not in k or preserve_base:
cfg_base[k] = v
return cfg_base
def _merge_cfg_from_command_update(self, args, cfg):
if len(args.opts) == 0:
return cfg
assert len(args.opts) % 2 == 0, 'Override list {} has odd length: {}.'.format(
args.opts, len(args.opts)
)
keys = args.opts[0::2]
vals = args.opts[1::2]
for key, val in zip(keys, vals):
cfg[key] = val
return cfg
def _merge_cfg_from_command(self, args, cfg):
assert len(args.opts) % 2 == 0, 'Override list {} has odd length: {}.'.format(
args.opts, len(args.opts)
)
keys = args.opts[0::2]
vals = args.opts[1::2]
# maximum supported depth 3
for idx, key in enumerate(keys):
key_split = key.split('.')
assert len(key_split) <= 4, 'Key depth error. \nMaximum depth: 3\n Get depth: {}'.format(
len(key_split)
)
assert key_split[0] in cfg.keys(), 'Non-existant key: {}.'.format(
key_split[0]
)
if len(key_split) == 2:
assert key_split[1] in cfg[key_split[0]].keys(), 'Non-existant key: {}.'.format(
key
)
elif len(key_split) == 3:
assert key_split[1] in cfg[key_split[0]].keys(), 'Non-existant key: {}.'.format(
key
)
assert key_split[2] in cfg[key_split[0]][key_split[1]].keys(), 'Non-existant key: {}.'.format(
key
)
elif len(key_split) == 4:
assert key_split[1] in cfg[key_split[0]].keys(), 'Non-existant key: {}.'.format(
key
)
assert key_split[2] in cfg[key_split[0]][key_split[1]].keys(), 'Non-existant key: {}.'.format(
key
)
assert key_split[3] in cfg[key_split[0]][key_split[1]][key_split[2]].keys(), 'Non-existant key: {}.'.format(
key
)
if len(key_split) == 1:
cfg[key_split[0]] = vals[idx]
elif len(key_split) == 2:
cfg[key_split[0]][key_split[1]] = vals[idx]
elif len(key_split) == 3:
cfg[key_split[0]][key_split[1]][key_split[2]] = vals[idx]
elif len(key_split) == 4:
cfg[key_split[0]][key_split[1]][key_split[2]][key_split[3]] = vals[idx]
return cfg
def _update_dict(self, cfg_dict):
def recur(key, elem):
if type(elem) is dict:
return key, Config(load=False, cfg_dict=elem, cfg_level=key)
else:
if type(elem) is str and elem[1:3]=="e-":
elem = float(elem)
return key, elem
dic = dict(recur(k, v) for k, v in cfg_dict.items())
self.__dict__.update(dic)
def get_args(self):
return self.args
def __repr__(self):
return "{}\n".format(self.dump())
def dump(self):
return json.dumps(self.cfg_dict, indent=2)
def deep_copy(self):
return copy.deepcopy(self)
if __name__ == '__main__':
# debug
cfg = Config(load=True)
print(cfg.DATA)
+430
View File
@@ -0,0 +1,430 @@
#!/usr/bin/env python3
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
import torch
import torch.nn.functional as F
import torch.distributed as dist
import functools
import pickle
import numpy as np
from collections import OrderedDict
from torch.autograd import Function
__all__ = ['is_dist_initialized',
'get_world_size',
'get_rank',
'new_group',
'destroy_process_group',
'barrier',
'broadcast',
'all_reduce',
'reduce',
'gather',
'all_gather',
'reduce_dict',
'get_global_gloo_group',
'generalized_all_gather',
'generalized_gather',
'scatter',
'reduce_scatter',
'send',
'recv',
'isend',
'irecv',
'shared_random_seed',
'diff_all_gather',
'diff_all_reduce',
'diff_scatter',
'diff_copy',
'spherical_kmeans',
'sinkhorn']
#-------------------------------- Distributed operations --------------------------------#
def is_dist_initialized():
return dist.is_available() and dist.is_initialized()
def get_world_size(group=None):
return dist.get_world_size(group) if is_dist_initialized() else 1
def get_rank(group=None):
return dist.get_rank(group) if is_dist_initialized() else 0
def new_group(ranks=None, **kwargs):
if is_dist_initialized():
return dist.new_group(ranks, **kwargs)
return None
def destroy_process_group():
if is_dist_initialized():
dist.destroy_process_group()
def barrier(group=None, **kwargs):
if get_world_size(group) > 1:
dist.barrier(group, **kwargs)
def broadcast(tensor, src, group=None, **kwargs):
if get_world_size(group) > 1:
return dist.broadcast(tensor, src, group, **kwargs)
def all_reduce(tensor, op=dist.ReduceOp.SUM, group=None, **kwargs):
if get_world_size(group) > 1:
return dist.all_reduce(tensor, op, group, **kwargs)
def reduce(tensor, dst, op=dist.ReduceOp.SUM, group=None, **kwargs):
if get_world_size(group) > 1:
return dist.reduce(tensor, dst, op, group, **kwargs)
def gather(tensor, dst=0, group=None, **kwargs):
rank = get_rank() # global rank
world_size = get_world_size(group)
if world_size == 1:
return [tensor]
tensor_list = [torch.empty_like(tensor) for _ in range(world_size)] if rank == dst else None
dist.gather(tensor, tensor_list, dst, group, **kwargs)
return tensor_list
def all_gather(tensor, uniform_size=True, group=None, **kwargs):
world_size = get_world_size(group)
if world_size == 1:
return [tensor]
assert tensor.is_contiguous(), 'ops.all_gather requires the tensor to be contiguous()'
if uniform_size:
tensor_list = [torch.empty_like(tensor) for _ in range(world_size)]
dist.all_gather(tensor_list, tensor, group, **kwargs)
return tensor_list
else:
# collect tensor shapes across GPUs
shape = tuple(tensor.shape)
shape_list = generalized_all_gather(shape, group)
# flatten the tensor
tensor = tensor.reshape(-1)
size = int(np.prod(shape))
size_list = [int(np.prod(u)) for u in shape_list]
max_size = max(size_list)
# pad to maximum size
if size != max_size:
padding = tensor.new_zeros(max_size - size)
tensor = torch.cat([tensor, padding], dim=0)
# all_gather
tensor_list = [torch.empty_like(tensor) for _ in range(world_size)]
dist.all_gather(tensor_list, tensor, group, **kwargs)
# reshape tensors
tensor_list = [t[:n].view(s) for t, n, s in zip(
tensor_list, size_list, shape_list)]
return tensor_list
@torch.no_grad()
def reduce_dict(input_dict, group=None, reduction='mean', **kwargs):
assert reduction in ['mean', 'sum']
world_size = get_world_size(group)
if world_size == 1:
return input_dict
# ensure that the orders of keys are consistent across processes
if isinstance(input_dict, OrderedDict):
keys = list(input_dict.keys)
else:
keys = sorted(input_dict.keys())
vals = [input_dict[key] for key in keys]
vals = torch.stack(vals, dim=0)
dist.reduce(vals, dst=0, group=group, **kwargs)
if dist.get_rank(group) == 0 and reduction == 'mean':
vals /= world_size
dist.broadcast(vals, src=0, group=group, **kwargs)
reduced_dict = type(input_dict)([
(key, val) for key, val in zip(keys, vals)])
return reduced_dict
@functools.lru_cache()
def get_global_gloo_group():
backend = dist.get_backend()
assert backend in ['gloo', 'nccl']
if backend == 'nccl':
return dist.new_group(backend='gloo')
else:
return dist.group.WORLD
def _serialize_to_tensor(data, group):
backend = dist.get_backend(group)
assert backend in ['gloo', 'nccl']
device = torch.device('cpu' if backend == 'gloo' else 'cuda')
buffer = pickle.dumps(data)
if len(buffer) > 1024 ** 3:
logger = logging.getLogger(__name__)
logger.warning(
'Rank {} trying to all-gather {:.2f} GB of data on device'
'{}'.format(get_rank(), len(buffer) / (1024 ** 3), device))
storage = torch.ByteStorage.from_buffer(buffer)
tensor = torch.ByteTensor(storage).to(device=device)
return tensor
def _pad_to_largest_tensor(tensor, group):
world_size = dist.get_world_size(group=group)
assert world_size >= 1, \
'gather/all_gather must be called from ranks within' \
'the give group!'
local_size = torch.tensor(
[tensor.numel()], dtype=torch.int64, device=tensor.device)
size_list = [torch.zeros(
[1], dtype=torch.int64, device=tensor.device)
for _ in range(world_size)]
# gather tensors and compute the maximum size
dist.all_gather(size_list, local_size, group=group)
size_list = [int(size.item()) for size in size_list]
max_size = max(size_list)
# pad tensors to the same size
if local_size != max_size:
padding = torch.zeros(
(max_size - local_size, ),
dtype=torch.uint8, device=tensor.device)
tensor = torch.cat((tensor, padding), dim=0)
return size_list, tensor
def generalized_all_gather(data, group=None):
if get_world_size(group) == 1:
return [data]
if group is None:
group = get_global_gloo_group()
tensor = _serialize_to_tensor(data, group)
size_list, tensor = _pad_to_largest_tensor(tensor, group)
max_size = max(size_list)
# receiving tensors from all ranks
tensor_list = [torch.empty(
(max_size, ), dtype=torch.uint8, device=tensor.device)
for _ in size_list]
dist.all_gather(tensor_list, tensor, group=group)
data_list = []
for size, tensor in zip(size_list, tensor_list):
buffer = tensor.cpu().numpy().tobytes()[:size]
data_list.append(pickle.loads(buffer))
return data_list
def generalized_gather(data, dst=0, group=None):
world_size = get_world_size(group)
if world_size == 1:
return [data]
if group is None:
group = get_global_gloo_group()
rank = dist.get_rank() # global rank
tensor = _serialize_to_tensor(data, group)
size_list, tensor = _pad_to_largest_tensor(tensor, group)
# receiving tensors from all ranks to dst
if rank == dst:
max_size = max(size_list)
tensor_list = [torch.empty(
(max_size, ), dtype=torch.uint8, device=tensor.device)
for _ in size_list]
dist.gather(tensor, tensor_list, dst=dst, group=group)
data_list = []
for size, tensor in zip(size_list, tensor_list):
buffer = tensor.cpu().numpy().tobytes()[:size]
data_list.append(pickle.loads(buffer))
return data_list
else:
dist.gather(tensor, [], dst=dst, group=group)
return []
def scatter(data, scatter_list=None, src=0, group=None, **kwargs):
r"""NOTE: only supports CPU tensor communication.
"""
if get_world_size(group) > 1:
return dist.scatter(data, scatter_list, src, group, **kwargs)
def reduce_scatter(output, input_list, op=dist.ReduceOp.SUM, group=None, **kwargs):
if get_world_size(group) > 1:
return dist.reduce_scatter(output, input_list, op, group, **kwargs)
def send(tensor, dst, group=None, **kwargs):
if get_world_size(group) > 1:
assert tensor.is_contiguous(), 'ops.send requires the tensor to be contiguous()'
return dist.send(tensor, dst, group, **kwargs)
def recv(tensor, src=None, group=None, **kwargs):
if get_world_size(group) > 1:
assert tensor.is_contiguous(), 'ops.recv requires the tensor to be contiguous()'
return dist.recv(tensor, src, group, **kwargs)
def isend(tensor, dst, group=None, **kwargs):
if get_world_size(group) > 1:
assert tensor.is_contiguous(), 'ops.isend requires the tensor to be contiguous()'
return dist.isend(tensor, dst, group, **kwargs)
def irecv(tensor, src=None, group=None, **kwargs):
if get_world_size(group) > 1:
assert tensor.is_contiguous(), 'ops.irecv requires the tensor to be contiguous()'
return dist.irecv(tensor, src, group, **kwargs)
def shared_random_seed(group=None):
seed = np.random.randint(2 ** 31)
all_seeds = generalized_all_gather(seed, group)
return all_seeds[0]
#-------------------------------- Differentiable operations --------------------------------#
def _all_gather(x):
if not (dist.is_available() and dist.is_initialized()) or dist.get_world_size() == 1:
return x
rank = dist.get_rank()
world_size = dist.get_world_size()
tensors = [torch.empty_like(x) for _ in range(world_size)]
tensors[rank] = x
dist.all_gather(tensors, x)
return torch.cat(tensors, dim=0).contiguous()
def _all_reduce(x):
if not (dist.is_available() and dist.is_initialized()) or dist.get_world_size() == 1:
return x
dist.all_reduce(x)
return x
def _split(x):
if not (dist.is_available() and dist.is_initialized()) or dist.get_world_size() == 1:
return x
rank = dist.get_rank()
world_size = dist.get_world_size()
return x.chunk(world_size, dim=0)[rank].contiguous()
class DiffAllGather(Function):
r"""Differentiable all-gather.
"""
@staticmethod
def symbolic(graph, input):
return _all_gather(input)
@staticmethod
def forward(ctx, input):
return _all_gather(input)
@staticmethod
def backward(ctx, grad_output):
return _split(grad_output)
class DiffAllReduce(Function):
r"""Differentiable all-reducd.
"""
@staticmethod
def symbolic(graph, input):
return _all_reduce(input)
@staticmethod
def forward(ctx, input):
return _all_reduce(input)
@staticmethod
def backward(ctx, grad_output):
return grad_output
class DiffScatter(Function):
r"""Differentiable scatter.
"""
@staticmethod
def symbolic(graph, input):
return _split(input)
@staticmethod
def symbolic(ctx, input):
return _split(input)
@staticmethod
def backward(ctx, grad_output):
return _all_gather(grad_output)
class DiffCopy(Function):
r"""Differentiable copy that reduces all gradients during backward.
"""
@staticmethod
def symbolic(graph, input):
return input
@staticmethod
def forward(ctx, input):
return input
@staticmethod
def backward(ctx, grad_output):
return _all_reduce(grad_output)
diff_all_gather = DiffAllGather.apply
diff_all_reduce = DiffAllReduce.apply
diff_scatter = DiffScatter.apply
diff_copy = DiffCopy.apply
#-------------------------------- Distributed algorithms --------------------------------#
@torch.no_grad()
def spherical_kmeans(feats, num_clusters, num_iters=10):
k, n, c = num_clusters, *feats.size()
ones = feats.new_ones(n, dtype=torch.long)
# distributed settings
rank = get_rank()
world_size = get_world_size()
# init clusters
rand_inds = torch.randperm(n)[:int(np.ceil(k / world_size))]
clusters = torch.cat(all_gather(feats[rand_inds]), dim=0)[:k]
# variables
new_clusters = feats.new_zeros(k, c)
counts = feats.new_zeros(k, dtype=torch.long)
# iterative Expectation-Maximization
for step in range(num_iters + 1):
# Expectation step
simmat = torch.mm(feats, clusters.t())
scores, assigns = simmat.max(dim=1)
if step == num_iters:
break
# Maximization step
new_clusters.zero_().scatter_add_(0, assigns.unsqueeze(1).repeat(1, c), feats)
all_reduce(new_clusters)
counts.zero_()
counts.index_add_(0, assigns, ones)
all_reduce(counts)
mask = (counts > 0)
clusters[mask] = new_clusters[mask] / counts[mask].view(-1, 1)
clusters = F.normalize(clusters, p=2, dim=1)
return clusters, assigns, scores
@torch.no_grad()
def sinkhorn(Q, eps=0.5, num_iters=3):
# normalize Q
Q = torch.exp(Q / eps).t()
sum_Q = Q.sum()
all_reduce(sum_Q)
Q /= sum_Q
# variables
n, m = Q.size()
u = Q.new_zeros(n)
r = Q.new_ones(n) / n
c = Q.new_ones(m) / (m * get_world_size())
# iterative update
cur_sum = Q.sum(dim=1)
all_reduce(cur_sum)
for i in range(num_iters):
u = cur_sum
Q *= (r / u).unsqueeze(1)
Q *= (c / Q.sum(dim=0)).unsqueeze(0)
cur_sum = Q.sum(dim=1)
all_reduce(cur_sum)
return (Q / Q.sum(dim=0, keepdim=True)).t().float()
+90
View File
@@ -0,0 +1,90 @@
#!/usr/bin/env python3
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
"""Logging."""
import builtins
import decimal
import functools
import logging
import os
import sys
import simplejson
# from fvcore.common.file_io import PathManager
import utils.distributed as du
def _suppress_print():
"""
Suppresses printing from the current process.
"""
def print_pass(*objects, sep=" ", end="\n", file=sys.stdout, flush=False):
pass
builtins.print = print_pass
# @functools.lru_cache(maxsize=None)
# def _cached_log_stream(filename):
# return PathManager.open(filename, "a")
def setup_logging(cfg, log_file):
"""
Sets up the logging for multiple processes. Only enable the logging for the
master process, and suppress logging for the non-master processes.
"""
if du.is_master_proc():
# Enable logging for the master process.
logging.root.handlers = []
else:
# Suppress logging for non-master processes.
_suppress_print()
logger = logging.getLogger()
logger.setLevel(logging.INFO)
logger.propagate = False
plain_formatter = logging.Formatter(
"[%(asctime)s][%(levelname)s] %(name)s: %(lineno)4d: %(message)s",
datefmt="%m/%d %H:%M:%S",
)
if du.is_master_proc():
ch = logging.StreamHandler(stream=sys.stdout)
ch.setLevel(logging.DEBUG)
ch.setFormatter(plain_formatter)
logger.addHandler(ch)
if log_file is not None and du.is_master_proc(du.get_world_size()):
filename = os.path.join(cfg.OUTPUT_DIR, log_file)
fh = logging.FileHandler(filename)
fh.setLevel(logging.DEBUG)
fh.setFormatter(plain_formatter)
logger.addHandler(fh)
def get_logger(name):
"""
Retrieve the logger with the specified name or, if name is None, return a
logger which is the root logger of the hierarchy.
Args:
name (string): name of the logger.
"""
return logging.getLogger(name)
def log_json_stats(stats):
"""
Logs json stats.
Args:
stats (dict): a dictionary of statistical information to log.
"""
stats = {
k: decimal.Decimal("{:.6f}".format(v)) if isinstance(v, float) else v
for k, v in stats.items()
}
json_stats = simplejson.dumps(stats, sort_keys=True, use_decimal=True)
logger = get_logger(__name__)
logger.info("{:s}".format(json_stats))
+16
View File
@@ -0,0 +1,16 @@
import os
# source_mp4_dir = "outputs/UniAnimate_infer"
# target_gif_dir = "outputs/UniAnimate_infer_gif"
source_mp4_dir = "outputs/UniAnimate_infer_long"
target_gif_dir = "outputs/UniAnimate_infer_long_gif"
os.makedirs(target_gif_dir, exist_ok=True)
for video in os.listdir(source_mp4_dir):
video_dir = os.path.join(source_mp4_dir, video)
gif_dir = os.path.join(target_gif_dir, video.replace(".mp4", ".gif"))
cmd = f'ffmpeg -i {video_dir} {gif_dir}'
os.system(cmd)
+9
View File
@@ -0,0 +1,9 @@
import socket
from contextlib import closing
def find_free_port():
""" https://stackoverflow.com/questions/1365265/on-localhost-how-do-i-pick-a-free-port-number """
with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
s.bind(('', 0))
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
return str(s.getsockname()[1])
+2
View File
@@ -0,0 +1,2 @@
from .lr_scheduler import *
from .adafactor import *
+230
View File
@@ -0,0 +1,230 @@
import math
import torch
from torch.optim import Optimizer
from torch.optim.lr_scheduler import LambdaLR
__all__ = ['Adafactor']
class Adafactor(Optimizer):
"""
AdaFactor pytorch implementation can be used as a drop in replacement for Adam original fairseq code:
https://github.com/pytorch/fairseq/blob/master/fairseq/optim/adafactor.py
Paper: *Adafactor: Adaptive Learning Rates with Sublinear Memory Cost* https://arxiv.org/abs/1804.04235 Note that
this optimizer internally adjusts the learning rate depending on the `scale_parameter`, `relative_step` and
`warmup_init` options. To use a manual (external) learning rate schedule you should set `scale_parameter=False` and
`relative_step=False`.
Arguments:
params (`Iterable[nn.parameter.Parameter]`):
Iterable of parameters to optimize or dictionaries defining parameter groups.
lr (`float`, *optional*):
The external learning rate.
eps (`Tuple[float, float]`, *optional*, defaults to (1e-30, 1e-3)):
Regularization constants for square gradient and parameter scale respectively
clip_threshold (`float`, *optional*, defaults 1.0):
Threshold of root mean square of final gradient update
decay_rate (`float`, *optional*, defaults to -0.8):
Coefficient used to compute running averages of square
beta1 (`float`, *optional*):
Coefficient used for computing running averages of gradient
weight_decay (`float`, *optional*, defaults to 0):
Weight decay (L2 penalty)
scale_parameter (`bool`, *optional*, defaults to `True`):
If True, learning rate is scaled by root mean square
relative_step (`bool`, *optional*, defaults to `True`):
If True, time-dependent learning rate is computed instead of external learning rate
warmup_init (`bool`, *optional*, defaults to `False`):
Time-dependent learning rate computation depends on whether warm-up initialization is being used
This implementation handles low-precision (FP16, bfloat) values, but we have not thoroughly tested.
Recommended T5 finetuning settings (https://discuss.huggingface.co/t/t5-finetuning-tips/684/3):
- Training without LR warmup or clip_threshold is not recommended.
- use scheduled LR warm-up to fixed LR
- use clip_threshold=1.0 (https://arxiv.org/abs/1804.04235)
- Disable relative updates
- Use scale_parameter=False
- Additional optimizer operations like gradient clipping should not be used alongside Adafactor
Example:
```python
Adafactor(model.parameters(), scale_parameter=False, relative_step=False, warmup_init=False, lr=1e-3)
```
Others reported the following combination to work well:
```python
Adafactor(model.parameters(), scale_parameter=True, relative_step=True, warmup_init=True, lr=None)
```
When using `lr=None` with [`Trainer`] you will most likely need to use [`~optimization.AdafactorSchedule`]
scheduler as following:
```python
from transformers.optimization import Adafactor, AdafactorSchedule
optimizer = Adafactor(model.parameters(), scale_parameter=True, relative_step=True, warmup_init=True, lr=None)
lr_scheduler = AdafactorSchedule(optimizer)
trainer = Trainer(..., optimizers=(optimizer, lr_scheduler))
```
Usage:
```python
# replace AdamW with Adafactor
optimizer = Adafactor(
model.parameters(),
lr=1e-3,
eps=(1e-30, 1e-3),
clip_threshold=1.0,
decay_rate=-0.8,
beta1=None,
weight_decay=0.0,
relative_step=False,
scale_parameter=False,
warmup_init=False,
)
```"""
def __init__(
self,
params,
lr=None,
eps=(1e-30, 1e-3),
clip_threshold=1.0,
decay_rate=-0.8,
beta1=None,
weight_decay=0.0,
scale_parameter=True,
relative_step=True,
warmup_init=False,
):
r"""require_version("torch>=1.5.0") # add_ with alpha
"""
if lr is not None and relative_step:
raise ValueError("Cannot combine manual `lr` and `relative_step=True` options")
if warmup_init and not relative_step:
raise ValueError("`warmup_init=True` requires `relative_step=True`")
defaults = dict(
lr=lr,
eps=eps,
clip_threshold=clip_threshold,
decay_rate=decay_rate,
beta1=beta1,
weight_decay=weight_decay,
scale_parameter=scale_parameter,
relative_step=relative_step,
warmup_init=warmup_init,
)
super().__init__(params, defaults)
@staticmethod
def _get_lr(param_group, param_state):
rel_step_sz = param_group["lr"]
if param_group["relative_step"]:
min_step = 1e-6 * param_state["step"] if param_group["warmup_init"] else 1e-2
rel_step_sz = min(min_step, 1.0 / math.sqrt(param_state["step"]))
param_scale = 1.0
if param_group["scale_parameter"]:
param_scale = max(param_group["eps"][1], param_state["RMS"])
return param_scale * rel_step_sz
@staticmethod
def _get_options(param_group, param_shape):
factored = len(param_shape) >= 2
use_first_moment = param_group["beta1"] is not None
return factored, use_first_moment
@staticmethod
def _rms(tensor):
return tensor.norm(2) / (tensor.numel() ** 0.5)
@staticmethod
def _approx_sq_grad(exp_avg_sq_row, exp_avg_sq_col):
# copy from fairseq's adafactor implementation:
# https://github.com/huggingface/transformers/blob/8395f14de6068012787d83989c3627c3df6a252b/src/transformers/optimization.py#L505
r_factor = (exp_avg_sq_row / exp_avg_sq_row.mean(dim=-1, keepdim=True)).rsqrt_().unsqueeze(-1)
c_factor = exp_avg_sq_col.unsqueeze(-2).rsqrt()
return torch.mul(r_factor, c_factor)
def step(self, closure=None):
"""
Performs a single optimization step
Arguments:
closure (callable, optional): A closure that reevaluates the model
and returns the loss.
"""
loss = None
if closure is not None:
loss = closure()
for group in self.param_groups:
for p in group["params"]:
if p.grad is None:
continue
grad = p.grad.data
if grad.dtype in {torch.float16, torch.bfloat16}:
grad = grad.float()
if grad.is_sparse:
raise RuntimeError("Adafactor does not support sparse gradients.")
state = self.state[p]
grad_shape = grad.shape
factored, use_first_moment = self._get_options(group, grad_shape)
# State Initialization
if len(state) == 0:
state["step"] = 0
if use_first_moment:
# Exponential moving average of gradient values
state["exp_avg"] = torch.zeros_like(grad)
if factored:
state["exp_avg_sq_row"] = torch.zeros(grad_shape[:-1]).to(grad)
state["exp_avg_sq_col"] = torch.zeros(grad_shape[:-2] + grad_shape[-1:]).to(grad)
else:
state["exp_avg_sq"] = torch.zeros_like(grad)
state["RMS"] = 0
else:
if use_first_moment:
state["exp_avg"] = state["exp_avg"].to(grad)
if factored:
state["exp_avg_sq_row"] = state["exp_avg_sq_row"].to(grad)
state["exp_avg_sq_col"] = state["exp_avg_sq_col"].to(grad)
else:
state["exp_avg_sq"] = state["exp_avg_sq"].to(grad)
p_data_fp32 = p.data
if p.data.dtype in {torch.float16, torch.bfloat16}:
p_data_fp32 = p_data_fp32.float()
state["step"] += 1
state["RMS"] = self._rms(p_data_fp32)
lr = self._get_lr(group, state)
beta2t = 1.0 - math.pow(state["step"], group["decay_rate"])
update = (grad**2) + group["eps"][0]
if factored:
exp_avg_sq_row = state["exp_avg_sq_row"]
exp_avg_sq_col = state["exp_avg_sq_col"]
exp_avg_sq_row.mul_(beta2t).add_(update.mean(dim=-1), alpha=(1.0 - beta2t))
exp_avg_sq_col.mul_(beta2t).add_(update.mean(dim=-2), alpha=(1.0 - beta2t))
# Approximation of exponential moving average of square of gradient
update = self._approx_sq_grad(exp_avg_sq_row, exp_avg_sq_col)
update.mul_(grad)
else:
exp_avg_sq = state["exp_avg_sq"]
exp_avg_sq.mul_(beta2t).add_(update, alpha=(1.0 - beta2t))
update = exp_avg_sq.rsqrt().mul_(grad)
update.div_((self._rms(update) / group["clip_threshold"]).clamp_(min=1.0))
update.mul_(lr)
if use_first_moment:
exp_avg = state["exp_avg"]
exp_avg.mul_(group["beta1"]).add_(update, alpha=(1 - group["beta1"]))
update = exp_avg
if group["weight_decay"] != 0:
p_data_fp32.add_(p_data_fp32, alpha=(-group["weight_decay"] * lr))
p_data_fp32.add_(-update)
if p.data.dtype in {torch.float16, torch.bfloat16}:
p.data.copy_(p_data_fp32)
return loss
+58
View File
@@ -0,0 +1,58 @@
import math
from torch.optim.lr_scheduler import _LRScheduler
__all__ = ['AnnealingLR']
class AnnealingLR(_LRScheduler):
def __init__(self, optimizer, base_lr, warmup_steps, total_steps, decay_mode='cosine', min_lr=0.0, last_step=-1):
assert decay_mode in ['linear', 'cosine', 'none']
self.optimizer = optimizer
self.base_lr = base_lr
self.warmup_steps = warmup_steps
self.total_steps = total_steps
self.decay_mode = decay_mode
self.min_lr = min_lr
self.current_step = last_step + 1
self.step(self.current_step)
def get_lr(self):
if self.warmup_steps > 0 and self.current_step <= self.warmup_steps:
return self.base_lr * self.current_step / self.warmup_steps
else:
ratio = (self.current_step - self.warmup_steps) / (self.total_steps - self.warmup_steps)
ratio = min(1.0, max(0.0, ratio))
if self.decay_mode == 'linear':
return self.base_lr * (1 - ratio)
elif self.decay_mode == 'cosine':
return self.base_lr * (math.cos(math.pi * ratio) + 1.0) / 2.0
else:
return self.base_lr
def step(self, current_step=None):
if current_step is None:
current_step = self.current_step + 1
self.current_step = current_step
new_lr = max(self.min_lr, self.get_lr())
if isinstance(self.optimizer, list):
for o in self.optimizer:
for group in o.param_groups:
group['lr'] = new_lr
else:
for group in self.optimizer.param_groups:
group['lr'] = new_lr
def state_dict(self):
return {
'base_lr': self.base_lr,
'warmup_steps': self.warmup_steps,
'total_steps': self.total_steps,
'decay_mode': self.decay_mode,
'current_step': self.current_step}
def load_state_dict(self, state_dict):
self.base_lr = state_dict['base_lr']
self.warmup_steps = state_dict['warmup_steps']
self.total_steps = state_dict['total_steps']
self.decay_mode = state_dict['decay_mode']
self.current_step = state_dict['current_step']
+155
View File
@@ -0,0 +1,155 @@
# Copyright 2021 Alibaba Group Holding Limited. All Rights Reserved.
# Registry class & build_from_config function partially modified from
# https://github.com/open-mmlab/mmcv/blob/master/mmcv/utils/registry.py
# Copyright 2018-2020 Open-MMLab. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
import inspect
import warnings
def build_from_config(cfg, registry, **kwargs):
""" Default builder function.
Args:
cfg (dict): A dict which contains parameters passes to target class or function.
Must contains key 'type', indicates the target class or function name.
registry (Registry): An registry to search target class or function.
kwargs (dict, optional): Other params not in config dict.
Returns:
Target class object or object returned by invoking function.
Raises:
TypeError:
KeyError:
Exception:
"""
if not isinstance(cfg, dict):
raise TypeError(f"config must be type dict, got {type(cfg)}")
if "type" not in cfg:
raise KeyError(f"config must contain key type, got {cfg}")
if not isinstance(registry, Registry):
raise TypeError(f"registry must be type Registry, got {type(registry)}")
cfg = copy.deepcopy(cfg)
req_type = cfg.pop("type")
req_type_entry = req_type
if isinstance(req_type, str):
req_type_entry = registry.get(req_type)
if req_type_entry is None:
raise KeyError(f"{req_type} not found in {registry.name} registry")
if kwargs is not None:
cfg.update(kwargs)
if inspect.isclass(req_type_entry):
try:
return req_type_entry(**cfg)
except Exception as e:
raise Exception(f"Failed to init class {req_type_entry}, with {e}")
elif inspect.isfunction(req_type_entry):
try:
return req_type_entry(**cfg)
except Exception as e:
raise Exception(f"Failed to invoke function {req_type_entry}, with {e}")
else:
raise TypeError(f"type must be str or class, got {type(req_type_entry)}")
class Registry(object):
""" A registry maps key to classes or functions.
Example:
>>> MODELS = Registry('MODELS')
>>> @MODELS.register_class()
>>> class ResNet(object):
>>> pass
>>> resnet = MODELS.build(dict(type="ResNet"))
>>>
>>> import torchvision
>>> @MODELS.register_function("InceptionV3")
>>> def get_inception_v3(pretrained=False, progress=True):
>>> return torchvision.models.inception_v3(pretrained=pretrained, progress=progress)
>>> inception_v3 = MODELS.build(dict(type='InceptionV3', pretrained=True))
Args:
name (str): Registry name.
build_func (func, None): Instance construct function. Default is build_from_config.
allow_types (tuple): Indicates how to construct the instance, by constructing class or invoking function.
"""
def __init__(self, name, build_func=None, allow_types=("class", "function")):
self.name = name
self.allow_types = allow_types
self.class_map = {}
self.func_map = {}
self.build_func = build_func or build_from_config
def get(self, req_type):
return self.class_map.get(req_type) or self.func_map.get(req_type)
def build(self, *args, **kwargs):
return self.build_func(*args, **kwargs, registry=self)
def register_class(self, name=None):
def _register(cls):
if not inspect.isclass(cls):
raise TypeError(f"Module must be type class, got {type(cls)}")
if "class" not in self.allow_types:
raise TypeError(f"Register {self.name} only allows type {self.allow_types}, got class")
module_name = name or cls.__name__
if module_name in self.class_map:
warnings.warn(f"Class {module_name} already registered by {self.class_map[module_name]}, "
f"will be replaced by {cls}")
self.class_map[module_name] = cls
return cls
return _register
def register_function(self, name=None):
def _register(func):
if not inspect.isfunction(func):
raise TypeError(f"Registry must be type function, got {type(func)}")
if "function" not in self.allow_types:
raise TypeError(f"Registry {self.name} only allows type {self.allow_types}, got function")
func_name = name or func.__name__
if func_name in self.class_map:
warnings.warn(f"Function {func_name} already registered by {self.func_map[func_name]}, "
f"will be replaced by {func}")
self.func_map[func_name] = func
return func
return _register
def _list(self):
keys = sorted(list(self.class_map.keys()) + list(self.func_map.keys()))
descriptions = []
for key in keys:
if key in self.class_map:
descriptions.append(f"{key}: {self.class_map[key]}")
else:
descriptions.append(
f"{key}: <function '{self.func_map[key].__module__}.{self.func_map[key].__name__}'>")
return "\n".join(descriptions)
def __repr__(self):
description = self._list()
description = '\n'.join(['\t' + s for s in description.split('\n')])
return f"{self.__class__.__name__} [{self.name}], \n" + description
+19
View File
@@ -0,0 +1,19 @@
from .registry import Registry, build_from_config
def build_func(cfg, registry, **kwargs):
"""
Except for config, if passing a list of dataset config, then return the concat type of it
"""
return build_from_config(cfg, registry, **kwargs)
AUTO_ENCODER = Registry("AUTO_ENCODER", build_func=build_func)
DATASETS = Registry("DATASETS", build_func=build_func)
DIFFUSION = Registry("DIFFUSION", build_func=build_func)
DISTRIBUTION = Registry("DISTRIBUTION", build_func=build_func)
EMBEDDER = Registry("EMBEDDER", build_func=build_func)
ENGINE = Registry("ENGINE", build_func=build_func)
INFER_ENGINE = Registry("INFER_ENGINE", build_func=build_func)
MODEL = Registry("MODEL", build_func=build_func)
PRETRAIN = Registry("PRETRAIN", build_func=build_func)
VISUAL = Registry("VISUAL", build_func=build_func)
EMBEDMANAGER = Registry("EMBEDMANAGER", build_func=build_func)
+11
View File
@@ -0,0 +1,11 @@
import torch
import random
import numpy as np
def setup_seed(seed):
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
np.random.seed(seed)
random.seed(seed)
torch.backends.cudnn.deterministic = True
+353
View File
@@ -0,0 +1,353 @@
import torch
import torchvision.transforms.functional as F
import random
import math
import numpy as np
from PIL import Image, ImageFilter
__all__ = ['Compose', 'Resize', 'Rescale', 'CenterCrop', 'CenterCropV2', 'CenterCropWide', 'RandomCrop', 'RandomCropV2', 'RandomHFlip',\
'GaussianBlur', 'ColorJitter', 'RandomGray', 'ToTensor', 'Normalize', "ResizeRandomCrop", "ExtractResizeRandomCrop", "ExtractResizeAssignCrop"]
class Compose(object):
def __init__(self, transforms):
self.transforms = transforms
def __getitem__(self, index):
if isinstance(index, slice):
return Compose(self.transforms[index])
else:
return self.transforms[index]
def __len__(self):
return len(self.transforms)
def __call__(self, rgb):
for t in self.transforms:
rgb = t(rgb)
return rgb
class Resize(object):
def __init__(self, size=256):
if isinstance(size, int):
size = (size, size)
self.size = size
def __call__(self, rgb):
if isinstance(rgb, list):
rgb = [u.resize(self.size, Image.BILINEAR) for u in rgb]
else:
rgb = rgb.resize(self.size, Image.BILINEAR)
return rgb
class Rescale(object):
def __init__(self, size=256, interpolation=Image.BILINEAR):
self.size = size
self.interpolation = interpolation
def __call__(self, rgb):
w, h = rgb[0].size
scale = self.size / min(w, h)
out_w, out_h = int(round(w * scale)), int(round(h * scale))
rgb = [u.resize((out_w, out_h), self.interpolation) for u in rgb]
return rgb
class CenterCrop(object):
def __init__(self, size=224):
self.size = size
def __call__(self, rgb):
w, h = rgb[0].size
assert min(w, h) >= self.size
x1 = (w - self.size) // 2
y1 = (h - self.size) // 2
rgb = [u.crop((x1, y1, x1 + self.size, y1 + self.size)) for u in rgb]
return rgb
class ResizeRandomCrop(object):
def __init__(self, size=256, size_short=292):
self.size = size
# self.min_area = min_area
self.size_short = size_short
def __call__(self, rgb):
# consistent crop between rgb and m
while min(rgb[0].size) >= 2 * self.size_short:
rgb = [u.resize((u.width // 2, u.height // 2), resample=Image.BOX) for u in rgb]
scale = self.size_short / min(rgb[0].size)
rgb = [u.resize((round(scale * u.width), round(scale * u.height)), resample=Image.BICUBIC) for u in rgb]
out_w = self.size
out_h = self.size
w, h = rgb[0].size # (518, 292)
x1 = random.randint(0, w - out_w)
y1 = random.randint(0, h - out_h)
rgb = [u.crop((x1, y1, x1 + out_w, y1 + out_h)) for u in rgb]
# rgb = [u.resize((self.size, self.size), Image.BILINEAR) for u in rgb]
# # center crop
# x1 = (img[0].width - self.size) // 2
# y1 = (img[0].height - self.size) // 2
# img = [u.crop((x1, y1, x1 + self.size, y1 + self.size)) for u in img]
return rgb
class ExtractResizeRandomCrop(object):
def __init__(self, size=256, size_short=292):
self.size = size
# self.min_area = min_area
self.size_short = size_short
def __call__(self, rgb):
# consistent crop between rgb and m
while min(rgb[0].size) >= 2 * self.size_short:
rgb = [u.resize((u.width // 2, u.height // 2), resample=Image.BOX) for u in rgb]
scale = self.size_short / min(rgb[0].size)
rgb = [u.resize((round(scale * u.width), round(scale * u.height)), resample=Image.BICUBIC) for u in rgb]
out_w = self.size
out_h = self.size
w, h = rgb[0].size # (518, 292)
x1 = random.randint(0, w - out_w)
y1 = random.randint(0, h - out_h)
rgb = [u.crop((x1, y1, x1 + out_w, y1 + out_h)) for u in rgb]
wh = [x1, y1, x1 + out_w, y1 + out_h]
return rgb, wh
class ExtractResizeAssignCrop(object):
def __init__(self, size=256, size_short=292):
self.size = size
# self.min_area = min_area
self.size_short = size_short
def __call__(self, rgb, wh):
# consistent crop between rgb and m
while min(rgb[0].size) >= 2 * self.size_short:
rgb = [u.resize((u.width // 2, u.height // 2), resample=Image.BOX) for u in rgb]
scale = self.size_short / min(rgb[0].size)
rgb = [u.resize((round(scale * u.width), round(scale * u.height)), resample=Image.BICUBIC) for u in rgb]
rgb = [u.crop(wh) for u in rgb]
rgb = [u.resize((self.size, self.size), Image.BILINEAR) for u in rgb]
return rgb
class CenterCropV2(object):
def __init__(self, size):
self.size = size
def __call__(self, img):
# fast resize
while min(img[0].size) >= 2 * self.size:
img = [u.resize((u.width // 2, u.height // 2), resample=Image.BOX) for u in img]
scale = self.size / min(img[0].size)
img = [u.resize((round(scale * u.width), round(scale * u.height)), resample=Image.BICUBIC) for u in img]
# center crop
x1 = (img[0].width - self.size) // 2
y1 = (img[0].height - self.size) // 2
img = [u.crop((x1, y1, x1 + self.size, y1 + self.size)) for u in img]
return img
class CenterCropWide(object):
def __init__(self, size, interpolation=Image.BOX):
self.size = size
self.interpolation = interpolation
def __call__(self, img):
if isinstance(img, list):
scale = min(img[0].size[0]/self.size[0], img[0].size[1]/self.size[1])
img = [u.resize((round(u.width // scale), round(u.height // scale)), resample=self.interpolation) for u in img]
# center crop
x1 = (img[0].width - self.size[0]) // 2
y1 = (img[0].height - self.size[1]) // 2
img = [u.crop((x1, y1, x1 + self.size[0], y1 + self.size[1])) for u in img]
return img
else:
scale = min(img.size[0]/self.size[0], img.size[1]/self.size[1])
img = img.resize((round(img.width // scale), round(img.height // scale)), resample=self.interpolation)
x1 = (img.width - self.size[0]) // 2
y1 = (img.height - self.size[1]) // 2
img = img.crop((x1, y1, x1 + self.size[0], y1 + self.size[1]))
return img
class RandomCrop(object):
def __init__(self, size=224, min_area=0.4):
self.size = size
self.min_area = min_area
def __call__(self, rgb):
# consistent crop between rgb and m
w, h = rgb[0].size
area = w * h
out_w, out_h = float('inf'), float('inf')
while out_w > w or out_h > h:
target_area = random.uniform(self.min_area, 1.0) * area
aspect_ratio = random.uniform(3. / 4., 4. / 3.)
out_w = int(round(math.sqrt(target_area * aspect_ratio)))
out_h = int(round(math.sqrt(target_area / aspect_ratio)))
x1 = random.randint(0, w - out_w)
y1 = random.randint(0, h - out_h)
rgb = [u.crop((x1, y1, x1 + out_w, y1 + out_h)) for u in rgb]
rgb = [u.resize((self.size, self.size), Image.BILINEAR) for u in rgb]
return rgb
class RandomCropV2(object):
def __init__(self, size=224, min_area=0.4, ratio=(3. / 4., 4. / 3.)):
if isinstance(size, (tuple, list)):
self.size = size
else:
self.size = (size, size)
self.min_area = min_area
self.ratio = ratio
def _get_params(self, img):
width, height = img.size
area = height * width
for _ in range(10):
target_area = random.uniform(self.min_area, 1.0) * area
log_ratio = (math.log(self.ratio[0]), math.log(self.ratio[1]))
aspect_ratio = math.exp(random.uniform(*log_ratio))
w = int(round(math.sqrt(target_area * aspect_ratio)))
h = int(round(math.sqrt(target_area / aspect_ratio)))
if 0 < w <= width and 0 < h <= height:
i = random.randint(0, height - h)
j = random.randint(0, width - w)
return i, j, h, w
# Fallback to central crop
in_ratio = float(width) / float(height)
if (in_ratio < min(self.ratio)):
w = width
h = int(round(w / min(self.ratio)))
elif (in_ratio > max(self.ratio)):
h = height
w = int(round(h * max(self.ratio)))
else: # whole image
w = width
h = height
i = (height - h) // 2
j = (width - w) // 2
return i, j, h, w
def __call__(self, rgb):
i, j, h, w = self._get_params(rgb[0])
rgb = [F.resized_crop(u, i, j, h, w, self.size) for u in rgb]
return rgb
class RandomHFlip(object):
def __init__(self, p=0.5):
self.p = p
def __call__(self, rgb):
if random.random() < self.p:
rgb = [u.transpose(Image.FLIP_LEFT_RIGHT) for u in rgb]
return rgb
class GaussianBlur(object):
def __init__(self, sigmas=[0.1, 2.0], p=0.5):
self.sigmas = sigmas
self.p = p
def __call__(self, rgb):
if random.random() < self.p:
sigma = random.uniform(*self.sigmas)
rgb = [u.filter(ImageFilter.GaussianBlur(radius=sigma)) for u in rgb]
return rgb
class ColorJitter(object):
def __init__(self, brightness=0.4, contrast=0.4, saturation=0.4, hue=0.1, p=0.5):
self.brightness = brightness
self.contrast = contrast
self.saturation = saturation
self.hue = hue
self.p = p
def __call__(self, rgb):
if random.random() < self.p:
brightness, contrast, saturation, hue = self._random_params()
transforms = [
lambda f: F.adjust_brightness(f, brightness),
lambda f: F.adjust_contrast(f, contrast),
lambda f: F.adjust_saturation(f, saturation),
lambda f: F.adjust_hue(f, hue)]
random.shuffle(transforms)
for t in transforms:
rgb = [t(u) for u in rgb]
return rgb
def _random_params(self):
brightness = random.uniform(
max(0, 1 - self.brightness), 1 + self.brightness)
contrast = random.uniform(
max(0, 1 - self.contrast), 1 + self.contrast)
saturation = random.uniform(
max(0, 1 - self.saturation), 1 + self.saturation)
hue = random.uniform(-self.hue, self.hue)
return brightness, contrast, saturation, hue
class RandomGray(object):
def __init__(self, p=0.2):
self.p = p
def __call__(self, rgb):
if random.random() < self.p:
rgb = [u.convert('L').convert('RGB') for u in rgb]
return rgb
class ToTensor(object):
def __call__(self, rgb):
if isinstance(rgb, list):
rgb = torch.stack([F.to_tensor(u) for u in rgb], dim=0)
else:
rgb = F.to_tensor(rgb)
return rgb
class Normalize(object):
def __init__(self, mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]):
self.mean = mean
self.std = std
def __call__(self, rgb):
rgb = rgb.clone()
rgb.clamp_(0, 1)
if not isinstance(self.mean, torch.Tensor):
self.mean = rgb.new_tensor(self.mean).view(-1)
if not isinstance(self.std, torch.Tensor):
self.std = rgb.new_tensor(self.std).view(-1)
if rgb.dim() == 4:
rgb.sub_(self.mean.view(1, -1, 1, 1)).div_(self.std.view(1, -1, 1, 1))
elif rgb.dim() == 3:
rgb.sub_(self.mean.view(-1, 1, 1)).div_(self.std.view(-1, 1, 1))
return rgb
+16
View File
@@ -0,0 +1,16 @@
import torch
def to_device(batch, device, non_blocking=False):
if isinstance(batch, (list, tuple)):
return type(batch)([
to_device(u, device, non_blocking)
for u in batch])
elif isinstance(batch, dict):
return type(batch)([
(k, to_device(v, device, non_blocking))
for k, v in batch.items()])
elif isinstance(batch, torch.Tensor) and batch.device != device:
batch = batch.to(device, non_blocking=non_blocking)
else:
return batch
return batch
+359
View File
@@ -0,0 +1,359 @@
import os
import os.path as osp
import sys
import cv2
import glob
import math
import torch
import gzip
import copy
import time
import json
import pickle
import base64
import imageio
import hashlib
import requests
import binascii
import zipfile
# import skvideo.io
import numpy as np
from io import BytesIO
import urllib.request
import torch.nn.functional as F
import torchvision.utils as tvutils
from multiprocessing.pool import ThreadPool as Pool
from einops import rearrange
from PIL import Image, ImageDraw, ImageFont
def gen_text_image(captions, text_size):
num_char = int(38 * (text_size / text_size))
font_size = int(text_size / 20)
font = ImageFont.truetype('data/font/DejaVuSans.ttf', size=font_size)
text_image_list = []
for text in captions:
txt_img = Image.new("RGB", (text_size, text_size), color="white")
draw = ImageDraw.Draw(txt_img)
lines = "\n".join(text[start:start + num_char] for start in range(0, len(text), num_char))
draw.text((0, 0), lines, fill="black", font=font)
txt_img = np.array(txt_img)
text_image_list.append(txt_img)
text_images = np.stack(text_image_list, axis=0)
text_images = torch.from_numpy(text_images)
return text_images
@torch.no_grad()
def save_video_refimg_and_text(
local_path,
ref_frame,
gen_video,
captions,
mean=[0.5, 0.5, 0.5],
std=[0.5, 0.5, 0.5],
text_size=256,
nrow=4,
save_fps=8,
retry=5):
'''
gen_video: BxCxFxHxW
'''
nrow = max(int(gen_video.size(0) / 2), 1)
vid_mean = torch.tensor(mean, device=gen_video.device).view(1, -1, 1, 1, 1) #ncfhw
vid_std = torch.tensor(std, device=gen_video.device).view(1, -1, 1, 1, 1) #ncfhw
text_images = gen_text_image(captions, text_size) # Tensor 8x256x256x3
text_images = text_images.unsqueeze(1) # Tensor 8x1x256x256x3
text_images = text_images.repeat_interleave(repeats=gen_video.size(2), dim=1) # 8x16x256x256x3
ref_frame = ref_frame.unsqueeze(2)
ref_frame = ref_frame.mul_(vid_std).add_(vid_mean)
ref_frame = ref_frame.repeat_interleave(repeats=gen_video.size(2), dim=2) # 8x16x256x256x3
ref_frame.clamp_(0, 1)
ref_frame = ref_frame * 255.0
ref_frame = rearrange(ref_frame, 'b c f h w -> b f h w c')
gen_video = gen_video.mul_(vid_std).add_(vid_mean) # 8x3x16x256x384
gen_video.clamp_(0, 1)
gen_video = gen_video * 255.0
images = rearrange(gen_video, 'b c f h w -> b f h w c')
images = torch.cat([ref_frame, images, text_images], dim=3)
images = rearrange(images, '(r j) f h w c -> f (r h) (j w) c', r=nrow)
images = [(img.numpy()).astype('uint8') for img in images]
for _ in [None] * retry:
try:
if len(images) == 1:
local_path = local_path + '.png'
cv2.imwrite(local_path, images[0][:,:,::-1], [int(cv2.IMWRITE_JPEG_QUALITY), 100])
else:
local_path = local_path + '.mp4'
frame_dir = os.path.join(os.path.dirname(local_path), '%s_frames' % (os.path.basename(local_path)))
os.system(f'rm -rf {frame_dir}'); os.makedirs(frame_dir, exist_ok=True)
for fid, frame in enumerate(images):
tpth = os.path.join(frame_dir, '%04d.png' % (fid+1))
cv2.imwrite(tpth, frame[:,:,::-1], [int(cv2.IMWRITE_JPEG_QUALITY), 100])
cmd = f'ffmpeg -y -f image2 -loglevel quiet -framerate {save_fps} -i {frame_dir}/%04d.png -vcodec libx264 -crf 17 -pix_fmt yuv420p {local_path}'
os.system(cmd); os.system(f'rm -rf {frame_dir}')
# os.system(f'rm -rf {local_path}')
exception = None
break
except Exception as e:
exception = e
continue
@torch.no_grad()
def save_i2vgen_video(
local_path,
image_id,
gen_video,
captions,
mean=[0.5, 0.5, 0.5],
std=[0.5, 0.5, 0.5],
text_size=256,
retry=5,
save_fps = 8
):
'''
Save both the generated video and the input conditions.
'''
vid_mean = torch.tensor(mean, device=gen_video.device).view(1, -1, 1, 1, 1) #ncfhw
vid_std = torch.tensor(std, device=gen_video.device).view(1, -1, 1, 1, 1) #ncfhw
text_images = gen_text_image(captions, text_size) # Tensor 1x256x256x3
text_images = text_images.unsqueeze(1) # Tensor 1x1x256x256x3
text_images = text_images.repeat_interleave(repeats=gen_video.size(2), dim=1) # 1x16x256x256x3
image_id = image_id.unsqueeze(2) # B, C, F, H, W
image_id = image_id.repeat_interleave(repeats=gen_video.size(2), dim=2) # 1x3x32x256x448
image_id = image_id.mul_(vid_std).add_(vid_mean) # 32x3x256x448
image_id.clamp_(0, 1)
image_id = image_id * 255.0
image_id = rearrange(image_id, 'b c f h w -> b f h w c')
gen_video = gen_video.mul_(vid_std).add_(vid_mean) # 8x3x16x256x384
gen_video.clamp_(0, 1)
gen_video = gen_video * 255.0
images = rearrange(gen_video, 'b c f h w -> b f h w c')
images = torch.cat([image_id, images, text_images], dim=3)
images = images[0]
images = [(img.numpy()).astype('uint8') for img in images]
exception = None
for _ in [None] * retry:
try:
frame_dir = os.path.join(os.path.dirname(local_path), '%s_frames' % (os.path.basename(local_path)))
os.system(f'rm -rf {frame_dir}'); os.makedirs(frame_dir, exist_ok=True)
for fid, frame in enumerate(images):
tpth = os.path.join(frame_dir, '%04d.png' % (fid+1))
cv2.imwrite(tpth, frame[:,:,::-1], [int(cv2.IMWRITE_JPEG_QUALITY), 100])
cmd = f'ffmpeg -y -f image2 -loglevel quiet -framerate {save_fps} -i {frame_dir}/%04d.png -vcodec libx264 -crf 17 -pix_fmt yuv420p {local_path}'
os.system(cmd); os.system(f'rm -rf {frame_dir}')
break
except Exception as e:
exception = e
continue
if exception is not None:
raise exception
@torch.no_grad()
def save_i2vgen_video_safe(
local_path,
gen_video,
captions,
mean=[0.5, 0.5, 0.5],
std=[0.5, 0.5, 0.5],
text_size=256,
retry=5,
save_fps = 8
):
'''
Save only the generated video, do not save the related reference conditions, and at the same time perform anomaly detection on the last frame.
'''
vid_mean = torch.tensor(mean, device=gen_video.device).view(1, -1, 1, 1, 1) #ncfhw
vid_std = torch.tensor(std, device=gen_video.device).view(1, -1, 1, 1, 1) #ncfhw
gen_video = gen_video.mul_(vid_std).add_(vid_mean) # 8x3x16x256x384
gen_video.clamp_(0, 1)
gen_video = gen_video * 255.0
images = rearrange(gen_video, 'b c f h w -> b f h w c')
images = images[0]
images = [(img.numpy()).astype('uint8') for img in images]
num_image = len(images)
exception = None
for _ in [None] * retry:
try:
if num_image == 1:
local_path = local_path + '.png'
cv2.imwrite(local_path, images[0][:,:,::-1], [int(cv2.IMWRITE_JPEG_QUALITY), 100])
else:
writer = imageio.get_writer(local_path, fps=save_fps, codec='libx264', quality=8)
for fid, frame in enumerate(images):
if fid == num_image-1: # Fix known bugs.
ratio = (np.sum((frame >= 117) & (frame <= 137)))/(frame.size)
if ratio > 0.4: continue
writer.append_data(frame)
writer.close()
break
except Exception as e:
exception = e
continue
if exception is not None:
raise exception
@torch.no_grad()
def save_t2vhigen_video_safe(
local_path,
gen_video,
captions,
mean=[0.5, 0.5, 0.5],
std=[0.5, 0.5, 0.5],
text_size=256,
retry=5,
save_fps = 8
):
'''
Save only the generated video, do not save the related reference conditions, and at the same time perform anomaly detection on the last frame.
'''
vid_mean = torch.tensor(mean, device=gen_video.device).view(1, -1, 1, 1, 1) #ncfhw
vid_std = torch.tensor(std, device=gen_video.device).view(1, -1, 1, 1, 1) #ncfhw
gen_video = gen_video.mul_(vid_std).add_(vid_mean) # 8x3x16x256x384
gen_video.clamp_(0, 1)
gen_video = gen_video * 255.0
images = rearrange(gen_video, 'b c f h w -> b f h w c')
images = images[0]
images = [(img.numpy()).astype('uint8') for img in images]
num_image = len(images)
exception = None
for _ in [None] * retry:
try:
if num_image == 1:
local_path = local_path + '.png'
cv2.imwrite(local_path, images[0][:,:,::-1], [int(cv2.IMWRITE_JPEG_QUALITY), 100])
else:
frame_dir = os.path.join(os.path.dirname(local_path), '%s_frames' % (os.path.basename(local_path)))
os.system(f'rm -rf {frame_dir}'); os.makedirs(frame_dir, exist_ok=True)
for fid, frame in enumerate(images):
if fid == num_image-1: # Fix known bugs.
ratio = (np.sum((frame >= 117) & (frame <= 137)))/(frame.size)
if ratio > 0.4: continue
tpth = os.path.join(frame_dir, '%04d.png' % (fid+1))
cv2.imwrite(tpth, frame[:,:,::-1], [int(cv2.IMWRITE_JPEG_QUALITY), 100])
cmd = f'ffmpeg -y -f image2 -loglevel quiet -framerate {save_fps} -i {frame_dir}/%04d.png -vcodec libx264 -crf 17 -pix_fmt yuv420p {local_path}'
os.system(cmd)
os.system(f'rm -rf {frame_dir}')
break
except Exception as e:
exception = e
continue
if exception is not None:
raise exception
@torch.no_grad()
def save_video_multiple_conditions_not_gif_horizontal_3col(local_path, video_tensor, model_kwargs, source_imgs,
mean=[0.5,0.5,0.5], std=[0.5,0.5,0.5], nrow=8, retry=5, save_fps=8):
mean=torch.tensor(mean,device=video_tensor.device).view(1,-1,1,1,1)#ncfhw
std=torch.tensor(std,device=video_tensor.device).view(1,-1,1,1,1)#ncfhw
video_tensor = video_tensor.mul_(std).add_(mean) #### unnormalize back to [0,1]
video_tensor.clamp_(0, 1)
b, c, n, h, w = video_tensor.shape
source_imgs = F.adaptive_avg_pool3d(source_imgs, (n, h, w))
source_imgs = source_imgs.cpu()
model_kwargs_channel3 = {}
for key, conditions in model_kwargs[0].items():
if conditions.size(1) == 1:
conditions = torch.cat([conditions, conditions, conditions], dim=1)
conditions = F.adaptive_avg_pool3d(conditions, (n, h, w))
if conditions.size(1) == 2:
conditions = torch.cat([conditions, conditions[:,:1,]], dim=1)
conditions = F.adaptive_avg_pool3d(conditions, (n, h, w))
elif conditions.size(1) == 3:
conditions = F.adaptive_avg_pool3d(conditions, (n, h, w))
elif conditions.size(1) == 4: # means it is a mask.
color = ((conditions[:, 0:3] + 1.)/2.) # .astype(np.float32)
alpha = conditions[:, 3:4] # .astype(np.float32)
conditions = color * alpha + 1.0 * (1.0 - alpha)
conditions = F.adaptive_avg_pool3d(conditions, (n, h, w))
model_kwargs_channel3[key] = conditions.cpu() if conditions.is_cuda else conditions
# filename = rand_name(suffix='.gif')
for _ in [None] * retry:
try:
vid_gif = rearrange(video_tensor, '(i j) c f h w -> c f (i h) (j w)', i = nrow)
cons_list = [rearrange(con, '(i j) c f h w -> c f (i h) (j w)', i = nrow) for _, con in model_kwargs_channel3.items()]
vid_gif = torch.cat(cons_list + [vid_gif,], dim=3)
vid_gif = vid_gif.permute(1,2,3,0)
images = vid_gif * 255.0
images = [(img.numpy()).astype('uint8') for img in images]
if len(images) == 1:
local_path = local_path.replace('.mp4', '.png')
cv2.imwrite(local_path, images[0][:,:,::-1], [int(cv2.IMWRITE_JPEG_QUALITY), 100])
# bucket.put_object_from_file(oss_key, local_path)
else:
outputs = []
for image_name in images:
x = Image.fromarray(image_name)
outputs.append(x)
from pathlib import Path
save_fmt = Path(local_path).suffix
if save_fmt == ".mp4":
with imageio.get_writer(local_path, fps=save_fps) as writer:
for img in outputs:
img_array = np.array(img) # Convert PIL Image to numpy array
writer.append_data(img_array)
elif save_fmt == ".gif":
outputs[0].save(
fp=local_path,
format="GIF",
append_images=outputs[1:],
save_all=True,
duration=(1 / save_fps * 1000),
loop=0,
)
else:
raise ValueError("Unsupported file type. Use .mp4 or .gif.")
# fourcc = cv2.VideoWriter_fourcc(*'mp4v')
# fps = save_fps
# image = images[0]
# media_writer = cv2.VideoWriter(local_path, fourcc, fps, (image.shape[1],image.shape[0]))
# for image_name in images:
# im = image_name[:,:,::-1]
# media_writer.write(im)
# media_writer.release()
exception = None
break
except Exception as e:
exception = e
continue
if exception is not None:
print('save video to {} failed, error: {}'.format(local_path, exception), flush=True)
+34
View File
@@ -0,0 +1,34 @@
import os
import shutil
now_dir = os.path.dirname(os.path.abspath(__file__))
from modelscope.hub.snapshot_download import snapshot_download
if not os.path.isfile(os.path.join(now_dir,"UniAnimate","checkpoints","unianimate_16f_32f_non_ema_223000.pth")):
snapshot_download('iic/unianimate', cache_dir=os.path.join(now_dir,'checkpoints'))
shutil.move(os.path.join(now_dir,"checkpoints","iic","unianimate"),os.path.join(now_dir,"UniAnimate","checkpoints"))
shutil.rmtree(os.path.join(now_dir,'checkpoints'))
else:
print("UniAnimate use cache models,make sure your 'UniAnimate/checkpoints' complete")
from .nodes import PoseAlignNode, UniAnimateNode,LoadImagePath,PreViewVideo,LoadVideo
# Set the web directory, any .js file in that directory will be loaded by the frontend as a frontend extension
WEB_DIRECTORY = "./web"
# A dictionary that contains all nodes you want to export with their names
# NOTE: names should be globally unique
NODE_CLASS_MAPPINGS = {
"PoseAlignNode":PoseAlignNode,
"UniAnimateNode":UniAnimateNode,
"LoadImagePath":LoadImagePath,
"PreViewVideo":PreViewVideo,
"LoadVideo":LoadVideo
}
# A dictionary that contains the friendly/humanly readable titles for the nodes
NODE_DISPLAY_NAME_MAPPINGS = {
"PoseAlignNode":"PoseAlignNode",
"UniAnimateNode":"UniAnimateNode",
"LoadImagePath":"LoadImagePath",
"PreViewVideo":"PreViewVideo",
"LoadVideo":"LoadVideo"
}
+164
View File
@@ -0,0 +1,164 @@
import os
import sys
import yaml
import folder_paths
from moviepy.editor import VideoFileClip
input_dir = folder_paths.get_input_directory()
output_dir = folder_paths.get_output_directory()
now_dir = os.path.dirname(os.path.abspath(__file__))
ckpt_dir = os.path.join(now_dir,"UniAnimate", "checkpoints")
python_exec = sys.executable or "python"
class PoseAlignNode:
@classmethod
def INPUT_TYPES(s):
return {
"required":{
"ref_name":("IMAGE",),
"source_video_path":("VIDEO",)
}
}
RETURN_TYPES = ("SEQUNCE",)
#RETURN_NAMES = ("image_output_name",)
FUNCTION = "align_pose"
#OUTPUT_NODE = False
CATEGORY = "AIFSH_UniAnimate"
def align_pose(self,ref_name,source_video_path):
base_name =os.path.basename(source_video_path)[:-4]
saved_pose_dir = os.path.join(output_dir,"UniAnimate",base_name)
os.makedirs(saved_pose_dir,exist_ok=True)
py_path = os.path.join(now_dir, "UniAnimate","run_align_pose.py")
cmd = f"""{python_exec} {py_path} --ref_name "{ref_name}" --source_video_paths "{source_video_path}" --saved_pose_dir "{saved_pose_dir}" """
print(cmd)
os.system(cmd)
return (saved_pose_dir, )
class UniAnimateNode:
@classmethod
def INPUT_TYPES(s):
return {
"required":{
"ref_name":("IMAGE",),
"pose_dir":("SEQUNCE",),
"frame_interval":([1,2],{
"default": 2,
}),
"max_frames":([96,64,48,32,24,16,'None'],{
"default": 32,
}),
"resolution":(["512*768","768*1216"],{
"default": "512*768"
}),
"context_overlap":([8,16],{
"default":8
}),
}
}
RETURN_TYPES = ("VIDEO",)
#RETURN_NAMES = ("image_output_name",)
FUNCTION = "generate"
#OUTPUT_NODE = False
CATEGORY = "AIFSH_UniAnimate"
def generate(self,ref_name,pose_dir,frame_interval,max_frames,resolution,context_overlap):
default_yaml_path = os.path.join(now_dir,"UniAnimate", "configs","UniAnimate_infer_long.yaml")
with open(default_yaml_path, 'r', encoding="utf-8") as f:
yaml_data = yaml.load(f.read(),Loader=yaml.SafeLoader)
log_dir = os.path.join(output_dir,"UniAnimate","log")
yaml_data['log_dir'] = os.path.join(output_dir,"UniAnimate","log")
os.makedirs(log_dir)
yaml_data["max_frames"] = max_frames
yaml_data['resolution'] = [512, 768] if '512' in resolution else [768, 1216]
yaml_data['context_overlap'] = context_overlap
yaml_data['test_list_path'] = [
[frame_interval,ref_name,pose_dir]
]
yaml_data['test_model'] = os.path.join(ckpt_dir,"unianimate_16f_32f_non_ema_223000.pth")
yaml_data['embedder']['pretrained'] = os.path.join(ckpt_dir,"open_clip_pytorch_model.bin")
yaml_data['auto_encoder']['pretrained'] = os.path.join(ckpt_dir,"v2-1_512-ema-pruned.ckpt")
tmp_yaml_path = os.path.join(now_dir,'tmp.yaml')
with open(tmp_yaml_path,'w', encoding="utf-8") as f:
yaml.dump(data=yaml_data,stream=f,Dumper=yaml.CDumper)
py_path = os.path.join(now_dir, "UniAnimate","inference.py")
cmd = f"""{python_exec} {py_path} --cfg "{tmp_yaml_path}" """
print(cmd)
os.system(cmd)
os.remove(tmp_yaml_path)
return (py_path, )
class LoadImagePath:
@classmethod
def INPUT_TYPES(s):
files = [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f))]
return {"required":
{"image": (sorted(files), {"image_upload": True})},
}
CATEGORY = "AIFSH_UniAnimate"
RETURN_TYPES = ("IMAGE",)
FUNCTION = "load_image"
def load_image(self, image):
image_path = folder_paths.get_annotated_filepath(image)
return (image_path,)
class PreViewVideo:
@classmethod
def INPUT_TYPES(s):
return {"required":{
"video":("VIDEO",),
}}
CATEGORY = "AIFSH_UniAnimate"
DESCRIPTION = "hello world!"
RETURN_TYPES = ()
OUTPUT_NODE = True
FUNCTION = "load_video"
def load_video(self, video):
video_name = os.path.basename(video)
video_path_name = os.path.basename(os.path.dirname(video))
return {"ui":{"video":[video_name,video_path_name]}}
class LoadVideo:
@classmethod
def INPUT_TYPES(s):
files = [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f)) and f.split('.')[-1].lower() in ["mp4", "webm","mkv","avi"]]
return {"required":{
"video":(files,),
}}
CATEGORY = "AIFSH_UniAnimate"
DESCRIPTION = "hello world!"
RETURN_TYPES = ("VIDEO","AUDIO")
OUTPUT_NODE = False
FUNCTION = "load_video"
def load_video(self, video):
video_path = os.path.join(input_dir,video)
video_clip = VideoFileClip(video_path)
audio_path = os.path.join(input_dir,video+".wav")
video_clip.audio.write_audiofile(audio_path)
return (video_path,audio_path,)
+11
View File
@@ -0,0 +1,11 @@
moviepy
PyYAML
modelscope
onnxruntime
open-clip-torch
opencv-python
rotary-embedding-torch
fairscale
imageio-ffmpeg
xformers
pytorch_lightning
+155
View File
@@ -0,0 +1,155 @@
import { app } from "../../../scripts/app.js";
import { api } from '../../../scripts/api.js'
function fitHeight(node) {
node.setSize([node.size[0], node.computeSize([node.size[0], node.size[1]])[1]])
node?.graph?.setDirtyCanvas(true);
}
function chainCallback(object, property, callback) {
if (object == undefined) {
//This should not happen.
console.error("Tried to add callback to non-existant object")
return;
}
if (property in object) {
const callback_orig = object[property]
object[property] = function () {
const r = callback_orig.apply(this, arguments);
callback.apply(this, arguments);
return r
};
} else {
object[property] = callback;
}
}
function addPreviewOptions(nodeType) {
chainCallback(nodeType.prototype, "getExtraMenuOptions", function(_, options) {
// The intended way of appending options is returning a list of extra options,
// but this isn't used in widgetInputs.js and would require
// less generalization of chainCallback
let optNew = []
try {
const previewWidget = this.widgets.find((w) => w.name === "videopreview");
let url = null
if (previewWidget.videoEl?.hidden == false && previewWidget.videoEl.src) {
//Use full quality video
//url = api.apiURL('/view?' + new URLSearchParams(previewWidget.value.params));
url = previewWidget.videoEl.src
}
if (url) {
optNew.push(
{
content: "Open preview",
callback: () => {
window.open(url, "_blank")
},
},
{
content: "Save preview",
callback: () => {
const a = document.createElement("a");
a.href = url;
a.setAttribute("download", new URLSearchParams(previewWidget.value.params).get("filename"));
document.body.append(a);
a.click();
requestAnimationFrame(() => a.remove());
},
}
);
}
if(options.length > 0 && options[0] != null && optNew.length > 0) {
optNew.push(null);
}
options.unshift(...optNew);
} catch (error) {
console.log(error);
}
});
}
function previewVideo(node,file,type){
var element = document.createElement("div");
const previewNode = node;
var previewWidget = node.addDOMWidget("videopreview", "preview", element, {
serialize: false,
hideOnZoom: false,
getValue() {
return element.value;
},
setValue(v) {
element.value = v;
},
});
previewWidget.computeSize = function(width) {
if (this.aspectRatio && !this.parentEl.hidden) {
let height = (previewNode.size[0]-20)/ this.aspectRatio + 10;
if (!(height > 0)) {
height = 0;
}
this.computedHeight = height + 10;
return [width, height];
}
return [width, -4];//no loaded src, widget should not display
}
// element.style['pointer-events'] = "none"
previewWidget.value = {hidden: false, paused: false, params: {}}
previewWidget.parentEl = document.createElement("div");
previewWidget.parentEl.className = "video_preview";
previewWidget.parentEl.style['width'] = "100%"
element.appendChild(previewWidget.parentEl);
previewWidget.videoEl = document.createElement("video");
previewWidget.videoEl.controls = true;
previewWidget.videoEl.loop = false;
previewWidget.videoEl.muted = false;
previewWidget.videoEl.style['width'] = "100%"
previewWidget.videoEl.addEventListener("loadedmetadata", () => {
previewWidget.aspectRatio = previewWidget.videoEl.videoWidth / previewWidget.videoEl.videoHeight;
fitHeight(this);
});
previewWidget.videoEl.addEventListener("error", () => {
//TODO: consider a way to properly notify the user why a preview isn't shown.
previewWidget.parentEl.hidden = true;
fitHeight(this);
});
let params = {
"filename": file,
"type": type,
}
previewWidget.parentEl.hidden = previewWidget.value.hidden;
previewWidget.videoEl.autoplay = !previewWidget.value.paused && !previewWidget.value.hidden;
let target_width = 256
if (element.style?.width) {
//overscale to allow scrolling. Endpoint won't return higher than native
target_width = element.style.width.slice(0,-2)*2;
}
if (!params.force_size || params.force_size.includes("?") || params.force_size == "Disabled") {
params.force_size = target_width+"x?"
} else {
let size = params.force_size.split("x")
let ar = parseInt(size[0])/parseInt(size[1])
params.force_size = target_width+"x"+(target_width/ar)
}
previewWidget.videoEl.src = api.apiURL('/view?' + new URLSearchParams(params));
previewWidget.videoEl.hidden = false;
previewWidget.parentEl.appendChild(previewWidget.videoEl)
}
app.registerExtension({
name: "UniAnimate.VideoPreviewer",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData?.name == "PreViewVideo") {
nodeType.prototype.onExecuted = function (data) {
previewVideo(this, data.video[0], data.video[1]);
}
addPreviewOptions(nodeType)
}
}
});
+203
View File
@@ -0,0 +1,203 @@
import { app } from "../../../scripts/app.js";
import { api } from '../../../scripts/api.js'
import { ComfyWidgets } from "../../../scripts/widgets.js"
function fitHeight(node) {
node.setSize([node.size[0], node.computeSize([node.size[0], node.size[1]])[1]])
node?.graph?.setDirtyCanvas(true);
}
function previewVideo(node,file){
while (node.widgets.length > 2){
node.widgets.pop()
}
try {
var el = document.getElementById("uploadVideo");
el.remove();
} catch (error) {
console.log(error);
}
var element = document.createElement("div");
element.id = "uploadVideo";
const previewNode = node;
var previewWidget = node.addDOMWidget("videopreview", "preview", element, {
serialize: false,
hideOnZoom: false,
getValue() {
return element.value;
},
setValue(v) {
element.value = v;
},
});
previewWidget.computeSize = function(width) {
if (this.aspectRatio && !this.parentEl.hidden) {
let height = (previewNode.size[0]-20)/ this.aspectRatio + 10;
if (!(height > 0)) {
height = 0;
}
this.computedHeight = height + 10;
return [width, height];
}
return [width, -4];//no loaded src, widget should not display
}
// element.style['pointer-events'] = "none"
previewWidget.value = {hidden: false, paused: false, params: {}}
previewWidget.parentEl = document.createElement("div");
previewWidget.parentEl.className = "video_preview";
previewWidget.parentEl.style['width'] = "100%"
element.appendChild(previewWidget.parentEl);
previewWidget.videoEl = document.createElement("video");
previewWidget.videoEl.controls = true;
previewWidget.videoEl.loop = false;
previewWidget.videoEl.muted = false;
previewWidget.videoEl.style['width'] = "100%"
previewWidget.videoEl.addEventListener("loadedmetadata", () => {
previewWidget.aspectRatio = previewWidget.videoEl.videoWidth / previewWidget.videoEl.videoHeight;
fitHeight(this);
});
previewWidget.videoEl.addEventListener("error", () => {
//TODO: consider a way to properly notify the user why a preview isn't shown.
previewWidget.parentEl.hidden = true;
fitHeight(this);
});
let params = {
"filename": file,
"type": "input",
}
previewWidget.parentEl.hidden = previewWidget.value.hidden;
previewWidget.videoEl.autoplay = !previewWidget.value.paused && !previewWidget.value.hidden;
let target_width = 256
if (element.style?.width) {
//overscale to allow scrolling. Endpoint won't return higher than native
target_width = element.style.width.slice(0,-2)*2;
}
if (!params.force_size || params.force_size.includes("?") || params.force_size == "Disabled") {
params.force_size = target_width+"x?"
} else {
let size = params.force_size.split("x")
let ar = parseInt(size[0])/parseInt(size[1])
params.force_size = target_width+"x"+(target_width/ar)
}
previewWidget.videoEl.src = api.apiURL('/view?' + new URLSearchParams(params));
previewWidget.videoEl.hidden = false;
previewWidget.parentEl.appendChild(previewWidget.videoEl)
}
function videoUpload(node, inputName, inputData, app) {
const videoWidget = node.widgets.find((w) => w.name === "video");
let uploadWidget;
/*
A method that returns the required style for the html
*/
var default_value = videoWidget.value;
Object.defineProperty(videoWidget, "value", {
set : function(value) {
this._real_value = value;
},
get : function() {
let value = "";
if (this._real_value) {
value = this._real_value;
} else {
return default_value;
}
if (value.filename) {
let real_value = value;
value = "";
if (real_value.subfolder) {
value = real_value.subfolder + "/";
}
value += real_value.filename;
if(real_value.type && real_value.type !== "input")
value += ` [${real_value.type}]`;
}
return value;
}
});
async function uploadFile(file, updateNode, pasted = false) {
try {
// Wrap file in formdata so it includes filename
const body = new FormData();
body.append("image", file);
if (pasted) body.append("subfolder", "pasted");
const resp = await api.fetchApi("/upload/image", {
method: "POST",
body,
});
if (resp.status === 200) {
const data = await resp.json();
// Add the file to the dropdown list and update the widget value
let path = data.name;
if (data.subfolder) path = data.subfolder + "/" + path;
if (!videoWidget.options.values.includes(path)) {
videoWidget.options.values.push(path);
}
if (updateNode) {
videoWidget.value = path;
previewVideo(node,path)
}
} else {
alert(resp.status + " - " + resp.statusText);
}
} catch (error) {
alert(error);
}
}
const fileInput = document.createElement("input");
Object.assign(fileInput, {
type: "file",
accept: "video/webm,video/mp4,video/mkv,video/avi",
style: "display: none",
onchange: async () => {
if (fileInput.files.length) {
await uploadFile(fileInput.files[0], true);
}
},
});
document.body.append(fileInput);
// Create the button widget for selecting the files
uploadWidget = node.addWidget("button", "choose video file to upload", "Video", () => {
fileInput.click();
});
uploadWidget.serialize = false;
previewVideo(node, videoWidget.value);
const cb = node.callback;
videoWidget.callback = function () {
previewVideo(node,videoWidget.value);
if (cb) {
return cb.apply(this, arguments);
}
};
return { widget: uploadWidget };
}
ComfyWidgets.VIDEOPLOAD = videoUpload;
app.registerExtension({
name: "UniAnimate.UploadVideo",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData?.name == "LoadVideo") {
nodeData.input.required.upload = ["VIDEOPLOAD"];
}
},
});