Files
CavinHuang-comfyui-nodes-docs/docs/Motionctrl Sample.md
T
2024-05-26 20:35:16 +08:00

8.2 KiB

Documentation

MotionctrlSample 类旨在通过将运动控制融入渲染过程来促进动态视觉内容的生成。它利用先进算法的力量解释用户定义的相机和轨迹输入,使得能够创造出带有逼真动画的复杂运动序列。该节点抽象了运动模拟的复杂性,允许用户专注于创造性表达而不是底层技术细节。

Input types

Required

  • prompt
    • 提示参数作为MotionctrlSample节点的创意输入,指导生成内容的整体主题和风格。它对于设置运动模拟发生的上下文至关重要,直接影响渲染过程中产生的叙述和视觉元素。
    • Comfy dtype: STRING
    • Python dtype: str
  • camera
    • 相机参数定义了捕捉运动序列的视角和视点。它在塑造渲染场景中的时空关系和深度感知方面起着关键作用,从而显著影响最终的视觉输出。
    • Comfy dtype: STRING
    • Python dtype: str
  • traj
    • 轨迹参数概述了场景中物体的路径和运动,成为运动模拟的基础。它对于指导动画的动态性和元素的相互作用至关重要,确保了运动表现的连贯性和流畅性。
    • Comfy dtype: STRING
    • Python dtype: str
  • frame_length
    • 帧长度参数指定了动画序列的持续时间,直接影响运动轨迹的详细程度和复杂性。较长的帧长度允许更复杂的运动模式和更平滑的过渡,而较短的帧长度可能导致更简单或更突然的运动。
    • Comfy dtype: INT
    • Python dtype: int
  • steps
    • 步数参数决定了运动模拟过程中生成的中间帧数,这影响动画的平滑度和质量。步数越高,运动轨迹越精细,而步数较低可能导致动画细节不足或不连贯。
    • Comfy dtype: INT
    • Python dtype: int
  • seed
    • 种子参数用于初始化随机数生成器,确保运动模拟结果是可复现和一致的。这对于调试和比较不同模拟运行是重要的方面。
    • Comfy dtype: INT
    • Python dtype: int

Output types

  • output
    • MotionctrlSample 节点的输出是一个动态视觉表示,以图像或图像序列的形式,封装了运动模拟的结果。它反映了输入参数的综合效应,展示了生成的运动内容的创造性和复杂性。
    • Comfy dtype: IMAGE
    • Python dtype: torch.Tensor

Usage tips

  • Infra type: GPU

Source code

class MotionctrlSample:

    @classmethod
    def INPUT_TYPES(cls):
        return {'required': {'prompt': ('STRING', {'multiline': True, 'default': 'a rose swaying in the wind'}), 'camera': ('STRING', {'multiline': True, 'default': '[[1,0,0,0,0,1,0,0,0,0,1,0.2]]'}), 'traj': ('STRING', {'multiline': True, 'default': '[[117, 102]]'}), 'frame_length': ('INT', {'default': 16}), 'steps': ('INT', {'default': 50}), 'seed': ('INT', {'default': 1234})}, 'optional': {'traj_tool': ('STRING', {'multiline': False, 'default': 'https://chaojie.github.io/ComfyUI-MotionCtrl/tools/draw.html'}), 'draw_traj_dot': ('BOOLEAN', {'default': False}), 'draw_camera_dot': ('BOOLEAN', {'default': False}), 'ckpt_name': (['motionctrl.pth'], {'default': 'motionctrl.pth'})}}
    RETURN_TYPES = ('IMAGE',)
    FUNCTION = 'run_inference'
    CATEGORY = 'motionctrl'

    def run_inference(self, prompt, camera, traj, frame_length, steps, seed, traj_tool='https://chaojie.github.io/ComfyUI-MotionCtrl/tools/draw.html', draw_traj_dot=False, draw_camera_dot=False, ckpt_name='motionctrl.pth'):
        gpu_num = 1
        gpu_no = 0
        ckpt_path = folder_paths.get_full_path('checkpoints', ckpt_name)
        comfy_path = os.path.dirname(folder_paths.__file__)
        config_path = os.path.join(comfy_path, 'custom_nodes/ComfyUI-MotionCtrl/configs/inference/config_both.yaml')
        args = {'savedir': f'./output/both_seed20230211', 'ckpt_path': f'{ckpt_path}', 'adapter_ckpt': None, 'base': f'{config_path}', 'condtype': 'both', 'prompt_dir': None, 'n_samples': 1, 'ddim_steps': 50, 'ddim_eta': 1.0, 'bs': 1, 'height': 256, 'width': 256, 'unconditional_guidance_scale': 1.0, 'unconditional_guidance_scale_temporal': None, 'seed': 1234, 'cond_T': 800, 'save_imgs': True, 'cond_dir': './custom_nodes/ComfyUI-MotionCtrl/examples/'}
        prompts = prompt
        RT = process_camera(camera, frame_length).reshape(-1, 12)
        RT_list = process_camera_list(camera, frame_length)
        traj_flow = process_traj(traj, frame_length).transpose(3, 0, 1, 2)
        print(prompts)
        print(RT.shape)
        print(traj_flow.shape)
        args['savedir'] = f"./output/{args['condtype']}_seed{args['seed']}"
        config = OmegaConf.load(args['base'])
        OmegaConf.update(config, 'model.params.unet_config.params.temporal_length', frame_length)
        model_config = config.pop('model', OmegaConf.create())
        model = instantiate_from_config(model_config)
        model = model.cuda(gpu_no)
        assert os.path.exists(args['ckpt_path']), f"Error: checkpoint {args['ckpt_path']} Not Found!"
        print(f"Loading checkpoint from {args['ckpt_path']}")
        model = load_model_checkpoint(model, args['ckpt_path'], args['adapter_ckpt'])
        model.eval()
        assert args['height'] % 16 == 0 and args['width'] % 16 == 0, 'Error: image size [h,w] should be multiples of 16!'
        (h, w) = (args['height'] // 8, args['width'] // 8)
        channels = model.channels
        frames = model.temporal_length
        noise_shape = [args['bs'], channels, frames, h, w]
        savedir = os.path.join(args['savedir'], 'samples')
        os.makedirs(savedir, exist_ok=True)
        unconditional_guidance_scale = 7.5
        unconditional_guidance_scale_temporal = None
        n_samples = 1
        ddim_steps = steps
        ddim_eta = 1.0
        cond_T = 800
        if n_samples < 1:
            n_samples = 1
        if n_samples > 4:
            n_samples = 4
        seed_everything(seed)
        camera_poses = RT
        trajs = traj_flow
        camera_poses = torch.tensor(camera_poses).float()
        trajs = torch.tensor(trajs).float()
        camera_poses = camera_poses.unsqueeze(0)
        trajs = trajs.unsqueeze(0)
        if torch.cuda.is_available():
            camera_poses = camera_poses.cuda()
            trajs = trajs.cuda()
        ddim_sampler = DDIMSampler(model)
        batch_size = noise_shape[0]
        prompts = prompt
        if isinstance(prompts, str):
            prompts = [prompts]
        for i in range(len(prompts)):
            prompts[i] = f'{prompts[i]}, {post_prompt}'
        cond = model.get_learned_conditioning(prompts)
        if camera_poses is not None:
            RT = camera_poses[..., None]
        else:
            RT = None
        traj_features = None
        if trajs is not None:
            traj_features = model.get_traj_features(trajs)
        else:
            traj_features = None
        uc = None
        if unconditional_guidance_scale != 1.0:
            prompts = batch_size * [DEFAULT_NEGATIVE_PROMPT]
            uc = model.get_learned_conditioning(prompts)
            if traj_features is not None:
                un_motion = model.get_traj_features(torch.zeros_like(trajs))
            else:
                un_motion = None
            uc = {'features_adapter': un_motion, 'uc': uc}
        else:
            uc = None
        batch_images = []
        batch_variants = []
        for _ in range(n_samples):
            if ddim_sampler is not None:
                (samples, _) = ddim_sampler.sample(S=ddim_steps, conditioning=cond, batch_size=noise_shape[0], shape=noise_shape[1:], verbose=False, unconditional_guidance_scale=unconditional_guidance_scale, unconditional_conditioning=uc, eta=ddim_eta, temporal_length=noise_shape[2], conditional_guidance_scale_temporal=unconditional_guidance_scale_temporal, features_adapter=traj_features, pose_emb=RT, cond_T=cond_T)
            batch_images = model.decode_first_stage(samples)
            batch_variants.append(batch_images)
        batch_variants = torch.stack(batch_variants, dim=1)
        batch_variants = batch_variants[0]
        ret = save_results(batch_variants, fps=10, traj=traj, draw_traj_dot=draw_traj_dot, cameras=RT_list, draw_camera_dot=draw_camera_dot)
        return ret