CoolFace
Apppublic

yslan/ObjCtrl-2.5D

sourceHugging Faceapache-2.0updated 2y agoView on Hugging Face
10likes
inference.py257 linesDownload Raw Back to cameractrl
1import argparse2import json3import os4import torch5import numpy as np6from tqdm import tqdm7from omegaconf import OmegaConf8from PIL import Image9from transformers import CLIPImageProcessor, CLIPVisionModelWithProjection10from diffusers import AutoencoderKLTemporalDecoder, EulerDiscreteScheduler11from diffusers.utils.import_utils import is_xformers_available12from packaging import version as pver13 14from cameractrl.pipelines.pipeline_animation import StableVideoDiffusionPipelinePoseCond15from cameractrl.models.unet import UNetSpatioTemporalConditionModelPoseCond16from cameractrl.models.pose_adaptor import CameraPoseEncoder17from cameractrl.utils.util import save_videos_grid18 19 20class Camera(object):21    def __init__(self, entry):22        fx, fy, cx, cy = entry[1:5]23        self.fx = fx24        self.fy = fy25        self.cx = cx26        self.cy = cy27        w2c_mat = np.array(entry[7:]).reshape(3, 4)28        w2c_mat_4x4 = np.eye(4)29        w2c_mat_4x4[:3, :] = w2c_mat30        self.w2c_mat = w2c_mat_4x431        self.c2w_mat = np.linalg.inv(w2c_mat_4x4)32 33 34def setup_for_distributed(is_master):35    """36    This function disables printing when not in master process37    """38    import builtins as __builtin__39    builtin_print = __builtin__.print40 41    def print(*args, **kwargs):42        force = kwargs.pop('force', False)43        if is_master or force:44            builtin_print(*args, **kwargs)45 46    __builtin__.print = print47 48 49def custom_meshgrid(*args):50    # ref: https://pytorch.org/docs/stable/generated/torch.meshgrid.html?highlight=meshgrid#torch.meshgrid51    if pver.parse(torch.__version__) < pver.parse('1.10'):52        return torch.meshgrid(*args)53    else:54        return torch.meshgrid(*args, indexing='ij')55 56 57def get_relative_pose(cam_params, zero_first_frame_scale):58    abs_w2cs = [cam_param.w2c_mat for cam_param in cam_params]59    abs_c2ws = [cam_param.c2w_mat for cam_param in cam_params]60    source_cam_c2w = abs_c2ws[0]61    if zero_first_frame_scale:62        cam_to_origin = 063    else:64        cam_to_origin = np.linalg.norm(source_cam_c2w[:3, 3])65    target_cam_c2w = np.array([66        [1, 0, 0, 0],67        [0, 1, 0, -cam_to_origin],68        [0, 0, 1, 0],69        [0, 0, 0, 1]70    ])71    abs2rel = target_cam_c2w @ abs_w2cs[0]72    ret_poses = [target_cam_c2w, ] + [abs2rel @ abs_c2w for abs_c2w in abs_c2ws[1:]]73    ret_poses = np.array(ret_poses, dtype=np.float32)74    return ret_poses75 76 77def ray_condition(K, c2w, H, W, device):78    # c2w: B, V, 4, 479    # K: B, V, 480 81    B = K.shape[0]82 83    j, i = custom_meshgrid(84        torch.linspace(0, H - 1, H, device=device, dtype=c2w.dtype),85        torch.linspace(0, W - 1, W, device=device, dtype=c2w.dtype),86    )87    i = i.reshape([1, 1, H * W]).expand([B, 1, H * W]) + 0.5  # [B, HxW]88    j = j.reshape([1, 1, H * W]).expand([B, 1, H * W]) + 0.5  # [B, HxW]89 90    fx, fy, cx, cy = K.chunk(4, dim=-1)  # B,V, 191 92    zs = torch.ones_like(i)  # [B, HxW]93    xs = (i - cx) / fx * zs94    ys = (j - cy) / fy * zs95    zs = zs.expand_as(ys)96 97    directions = torch.stack((xs, ys, zs), dim=-1)  # B, V, HW, 398    directions = directions / directions.norm(dim=-1, keepdim=True)  # B, V, HW, 399 100    rays_d = directions @ c2w[..., :3, :3].transpose(-1, -2)  # B, V, 3, HW101    rays_o = c2w[..., :3, 3]  # B, V, 3102    rays_o = rays_o[:, :, None].expand_as(rays_d)  # B, V, 3, HW103    # c2w @ dirctions104    rays_dxo = torch.linalg.cross(rays_o, rays_d)105    plucker = torch.cat([rays_dxo, rays_d], dim=-1)106    plucker = plucker.reshape(B, c2w.shape[1], H, W, 6)  # B, V, H, W, 6107    return plucker108 109 110def get_pipeline(ori_model_path, unet_subfolder, down_block_types, up_block_types, pose_encoder_kwargs,111                 attention_processor_kwargs, pose_adaptor_ckpt, enable_xformers, device):112    noise_scheduler = EulerDiscreteScheduler.from_pretrained(ori_model_path, subfolder="scheduler")113    feature_extractor = CLIPImageProcessor.from_pretrained(ori_model_path, subfolder="feature_extractor")114    image_encoder = CLIPVisionModelWithProjection.from_pretrained(ori_model_path, subfolder="image_encoder")115    vae = AutoencoderKLTemporalDecoder.from_pretrained(ori_model_path, subfolder="vae")116    unet = UNetSpatioTemporalConditionModelPoseCond.from_pretrained(ori_model_path,117                                                                    subfolder=unet_subfolder,118                                                                    down_block_types=down_block_types,119                                                                    up_block_types=up_block_types)120    pose_encoder = CameraPoseEncoder(**pose_encoder_kwargs)121    print("Setting the attention processors")122    unet.set_pose_cond_attn_processor(enable_xformers=(enable_xformers and is_xformers_available()), **attention_processor_kwargs)123    print(f"Loading weights of camera encoder and attention processor from {pose_adaptor_ckpt}")124    ckpt_dict = torch.load(pose_adaptor_ckpt, map_location=unet.device)125    pose_encoder_state_dict = ckpt_dict['pose_encoder_state_dict']126    pose_encoder_m, pose_encoder_u = pose_encoder.load_state_dict(pose_encoder_state_dict)127    assert len(pose_encoder_m) == 0 and len(pose_encoder_u) == 0128    attention_processor_state_dict = ckpt_dict['attention_processor_state_dict']129    _, attention_processor_u = unet.load_state_dict(attention_processor_state_dict, strict=False)130    assert len(attention_processor_u) == 0131    print("Loading done")132    vae.to(device)133    image_encoder.to(device)134    unet.to(device)135    pipeline = StableVideoDiffusionPipelinePoseCond(136        vae=vae,137        image_encoder=image_encoder,138        unet=unet,139        scheduler=noise_scheduler,140        feature_extractor=feature_extractor,141        pose_encoder=pose_encoder142    )143    pipeline = pipeline.to(device)144    return pipeline145 146 147def main(args):148    os.makedirs(os.path.join(args.out_root, 'generated_videos'), exist_ok=True)149    os.makedirs(os.path.join(args.out_root, 'reference_images'), exist_ok=True)150    rank = args.local_rank151    setup_for_distributed(rank == 0)152    gpu_id = rank % torch.cuda.device_count()153    model_configs = OmegaConf.load(args.model_config)154    device = f"cuda:{gpu_id}"155    print(f'Constructing pipeline')156    pipeline = get_pipeline(args.ori_model_path, model_configs['unet_subfolder'], model_configs['down_block_types'],157                            model_configs['up_block_types'], model_configs['pose_encoder_kwargs'],158                            model_configs['attention_processor_kwargs'], args.pose_adaptor_ckpt, args.enable_xformers, device)159    160    print('Done')161 162    print('Loading K, R, t matrix')163    with open(args.trajectory_file, 'r') as f:164        poses = f.readlines()165    poses = [pose.strip().split(' ') for pose in poses[1:]]166    cam_params = [[float(x) for x in pose] for pose in poses]167    cam_params = [Camera(cam_param) for cam_param in cam_params]168 169    sample_wh_ratio = args.image_width / args.image_height170    pose_wh_ratio = args.original_pose_width / args.original_pose_height171    if pose_wh_ratio > sample_wh_ratio:172        resized_ori_w = args.image_height * pose_wh_ratio173        for cam_param in cam_params:174            cam_param.fx = resized_ori_w * cam_param.fx / args.image_width175    else:176        resized_ori_h = args.image_width / pose_wh_ratio177        for cam_param in cam_params:178            cam_param.fy = resized_ori_h * cam_param.fy / args.image_height179    intrinsic = np.asarray([[cam_param.fx * args.image_width,180                             cam_param.fy * args.image_height,181                             cam_param.cx * args.image_width,182                             cam_param.cy * args.image_height]183                            for cam_param in cam_params], dtype=np.float32)184    K = torch.as_tensor(intrinsic)[None]  # [1, 1, 4]185    c2ws = get_relative_pose(cam_params, zero_first_frame_scale=True)186    c2ws = torch.as_tensor(c2ws)[None]  # [1, n_frame, 4, 4]187    plucker_embedding = ray_condition(K, c2ws, args.image_height, args.image_width, device='cpu')       # b f h w 6188    plucker_embedding = plucker_embedding.permute(0, 1, 4, 2, 3).contiguous().to(device=device)189 190    prompt_dict = json.load(open(args.prompt_file, 'r'))191    prompt_images = prompt_dict['image_paths']192    prompt_captions = prompt_dict['captions']193    N = int(len(prompt_images) // args.n_procs)194    remainder = int(len(prompt_images) % args.n_procs)195    prompts_per_gpu = [N + 1 if gpu_id < remainder else N for gpu_id in range(args.n_procs)]196    low_idx = sum(prompts_per_gpu[:gpu_id])197    high_idx = low_idx + prompts_per_gpu[gpu_id]198    prompt_images = prompt_images[low_idx: high_idx]199    prompt_captions = prompt_captions[low_idx: high_idx]200    print(f"rank {rank} / {torch.cuda.device_count()}, number of prompts: {len(prompt_images)}")201 202    generator = torch.Generator(device=device)203    generator.manual_seed(42)204 205    for prompt_image, prompt_caption in tqdm(zip(prompt_images, prompt_captions)):206        # save_name = "_".join(prompt_caption.split(" "))207        save_name = prompt_caption.split('.')[0]208        condition_image = Image.open(prompt_image)209        with torch.no_grad():210            sample = pipeline(211                image=condition_image,212                pose_embedding=plucker_embedding,213                height=args.image_height,214                width=args.image_width,215                num_frames=args.num_frames,216                num_inference_steps=args.num_inference_steps,217                min_guidance_scale=args.min_guidance_scale,218                max_guidance_scale=args.max_guidance_scale,219                do_image_process=True,220                generator=generator,221                output_type='pt'222            ).frames[0].transpose(0, 1).cpu()      # [3, f, h, w] 0-1223        resized_condition_image = condition_image.resize((args.image_width, args.image_height))224        # save_videos_grid(sample[None], f"{os.path.join(args.out_root, 'generated_videos')}/{save_name}.mp4", rescale=False)225        save_videos_grid(sample[None], f"{os.path.join(args.out_root, 'generated_videos')}/{save_name}.gif", rescale=False)226        resized_condition_image.save(os.path.join(args.out_root, 'reference_images', f'{save_name}.png'))227 228 229if __name__ == '__main__':230    parser = argparse.ArgumentParser()231    parser.add_argument("--out_root", type=str)232    parser.add_argument("--image_height", type=int, default=320)233    parser.add_argument("--image_width", type=int, default=576)234    parser.add_argument("--num_frames", type=int, default=14, help="14 for svd and 25 for svd-xt", choices=[14, 25])235    parser.add_argument("--ori_model_path", type=str)236    parser.add_argument("--unet_subfolder", type=str, default='unet')237    parser.add_argument("--enable_xformers", action='store_true')238    parser.add_argument("--pose_adaptor_ckpt", default=None)239    parser.add_argument("--num_inference_steps", type=int, default=25)240    parser.add_argument("--min_guidance_scale", type=float, default=1.0)241    parser.add_argument("--max_guidance_scale", type=float, default=3.0)242    parser.add_argument("--prompt_file", required=True, help='prompts path, json or txt')243    parser.add_argument("--trajectory_file", required=True)244    parser.add_argument("--original_pose_width", type=int, default=1280)245    parser.add_argument("--original_pose_height", type=int, default=720)246    parser.add_argument("--model_config", required=True)247    parser.add_argument("--n_procs", type=int, default=8)248 249    # DDP args250    parser.add_argument("--world_size", default=1, type=int,251                        help="number of the distributed processes.")252    parser.add_argument('--local-rank', type=int, default=-1,253                        help='Replica rank on the current node. This field is required '254                             'by `torch.distributed.launch`.')255    args = parser.parse_args()256    main(args)257