CoolFace
Apppublic

yslan/ObjCtrl-2.5D

sourceHugging Faceapache-2.0updated 2y agoView on Hugging Face
10likes
app.py814 linesDownload Raw Back to root
1try:2    import spaces3except:4    pass5 6import os7import gradio as gr8import json9import ast10 11import torch12from gradio_image_prompter import ImagePrompter13from sam2.sam2_image_predictor import SAM2ImagePredictor14from omegaconf import OmegaConf15from PIL import Image, ImageDraw16import numpy as np17from copy import deepcopy18import cv219 20import torch.nn.functional as F21import torchvision22from einops import rearrange23import tempfile24 25from objctrl_2_5d.utils.ui_utils import process_image, get_camera_pose, get_subject_points, get_points, undo_points, mask_image, traj2cam, get_mid_params26from ZoeDepth.zoedepth.utils.misc import colorize27 28from cameractrl.inference import get_pipeline29 30from objctrl_2_5d.utils.objmask_util import RT2Plucker, Unprojected, roll_with_ignore_multidim, dilate_mask_pytorch31from objctrl_2_5d.utils.filter_utils import get_freq_filter, freq_mix_3d32 33### Title and Description ###34#### Description ####35title = r"""<h1 align="center">ObjCtrl-2.5D: Training-free Object Control with Camera Poses</h1>"""36# subtitle = r"""<h2 align="center">Deployed on SVD Generation</h2>"""37important_link = r"""38<div align='center'>39 <a href='https://wzhouxiff.github.io/projects/ObjCtrl-2.5D/assets/ObjCtrl-2.5D.pdf'>[Paper]</a>40&ensp; <a href='https://arxiv.org/pdf/2412.07721'>[arxiv]</a>41&ensp; <a href='https://wzhouxiff.github.io/projects/ObjCtrl-2.5D/'>[Project Page]</a>42&ensp; <a href='https://github.com/wzhouxiff/ObjCtrl-2.5D'>[Code]</a>43</div>44"""45 46# authors = r"""47# <div align='center'>48#  <a href='https://wzhouxiff.github.io/'>Zhouxia Wang</a>49# &ensp; <a href='https://nirvanalan.github.io/'>Yushi Lan</a>50# &ensp; <a href='https://shangchenzhou.com/'>Shangchen Zhou</a>51# &ensp; <a href='https://www.mmlab-ntu.com/person/ccloy/index.html'>Chen Change Loy</a>52# </div>53# """54 55# affiliation = r"""56# <div align='center'>57#  <a href='https://www.mmlab-ntu.com/'>S-Lab, NTU Singapore</a>58# </div>59# """60 61description = r"""62<b>Official Gradio demo</b> for <a href='https://github.com/wzhouxiff/ObjCtrl-2.5D' target='_blank'><b>ObjCtrl-2.5D: Training-free Object Control with Camera Poses</b></a>.<br>63๐Ÿ”ฅ ObjCtrl2.5D enables object motion control in a I2V generated video via transforming 2D trajectories to 3D using depth, subsequently converting them into camera poses, 64thereby leveraging the exisitng camera motion control module for object motion control without requiring additional training.<br>65"""66 67article = r"""68If ObjCtrl2.5D is helpful, please help to โญ the <a href='https://github.com/wzhouxiff/ObjCtrl-2.5D' target='_blank'>Github Repo</a>. Thanks! 69[![GitHub Stars](https://img.shields.io/github/stars/wzhouxiff%2FObjCtrl-2.5D70)](https://github.com/wzhouxiff/ObjCtrl-2.5D)71 72---73 74๐Ÿ“ **License**75<br>76This project is licensed under <a href="https://github.com/wzhouxiff/ObjCtrl-2.5D/blob/main/LICENSE">S-Lab License 1.0</a>, 77Redistribution and use for non-commercial purposes should follow this license.78 79๐Ÿ“ **Citation**80<br>81If our work is useful for your research, please consider citing:82```bibtex83@inproceedings{objctrl2.5d,84  title={ObjCtrl-2.5D: Training-free Object Control with Camera Poses},85  author={Wang, Zhouxia and Lan, Yushi and Zhou, Shangchen and Loy, Chen Change},86  booktitle={arXiv preprint arXiv:2412.07721},87  year={2024}88}89```90 91๐Ÿ“ง **Contact**92<br>93If you have any questions, please feel free to reach me out at <b>zhouzi1212@gmail.com</b>.94 95"""96 97# pre-defined parameters98DEBUG = False99 100if DEBUG:101    cur_OUTPUT_PATH = 'outputs/tmp'102    os.makedirs(cur_OUTPUT_PATH, exist_ok=True)103 104# num_inference_steps=25105min_guidance_scale = 1.0106max_guidance_scale = 3.0107 108area_ratio = 0.3109depth_scale_ = 5.2110center_margin = 10111 112height, width = 320, 576113num_frames = 14114 115intrinsics = np.array([[float(width), float(width), float(width) / 2, float(height) / 2]])116intrinsics = np.repeat(intrinsics, num_frames, axis=0) # [n_frame, 4]117fx = intrinsics[0, 0] / width118fy = intrinsics[0, 1] / height119cx = intrinsics[0, 2] / width120cy = intrinsics[0, 3] / height121 122down_scale = 8123H, W = height // down_scale, width // down_scale124K = np.array([[width / down_scale, 0, W / 2], [0, width / down_scale, H / 2], [0, 0, 1]])125 126 127# -------------- initialization --------------128 129# CAMERA_MODE = ["Traj2Cam", "Rotate", "Clockwise", "Translate"]130CAMERA_MODE = ["None", "ZoomIn", "ZoomOut", "PanRight", "PanLeft", "TiltUp", "TiltDown", "ClockWise", "Anti-CW", "Rotate60"]131 132# select the device for computation133if torch.cuda.is_available():134    device = torch.device("cuda")135elif torch.backends.mps.is_available():136    device = torch.device("mps")137else:138    device = torch.device("cpu")139print(f"using device: {device}")140 141# # segmentation model142segmentor = SAM2ImagePredictor.from_pretrained("facebook/sam2-hiera-tiny", cache_dir="ckpt", device=device)143 144# depth model145d_model_NK = torch.hub.load('./ZoeDepth', 'ZoeD_NK', source='local', pretrained=True).to(device)146 147# cameractrl model148config = "configs/svd_320_576_cameractrl.yaml"149model_id = "stabilityai/stable-video-diffusion-img2vid"150ckpt = "checkpoints/CameraCtrl_svd.ckpt"151if not os.path.exists(ckpt):152    os.makedirs("checkpoints", exist_ok=True)153    os.system("wget -c https://huggingface.co/hehao13/CameraCtrl_SVD_ckpts/resolve/main/CameraCtrl_svd.ckpt?download=true")154    os.system("mv CameraCtrl_svd.ckpt?download=true checkpoints/CameraCtrl_svd.ckpt")155model_config = OmegaConf.load(config)156 157 158pipeline = get_pipeline(model_id, "unet", model_config['down_block_types'], model_config['up_block_types'],159                        model_config['pose_encoder_kwargs'], model_config['attention_processor_kwargs'],160                        ckpt, True, device)161 162# segmentor = None163# d_model_NK = None164# pipeline = None165 166### run the demo ##167@spaces.GPU(duration=7)168def segment(canvas, image, logits):169    if logits is not None:170        logits *=  32.0171    _, points = get_subject_points(canvas)172    image = np.array(image)173 174    with torch.inference_mode(), torch.autocast("cuda", dtype=torch.bfloat16):175        segmentor.set_image(image)176        input_points = []177        input_boxes = []178        for p in points:179            [x1, y1, _, x2, y2, _] = p180            if x2==0 and y2==0:181                input_points.append([x1, y1])182            else:183                input_boxes.append([x1, y1, x2, y2])184        if len(input_points) == 0:185            input_points = None186            input_labels = None187        else:188            input_points = np.array(input_points)189            input_labels = np.ones(len(input_points))190        if len(input_boxes) == 0:191            input_boxes = None192        else:193            input_boxes = np.array(input_boxes)194        masks, _, logits = segmentor.predict(195            point_coords=input_points,196            point_labels=input_labels,197            box=input_boxes,198            multimask_output=False,199            return_logits=True,200            mask_input=logits,201        )202        mask = masks > 0203        masked_img = mask_image(image, mask[0], color=[252, 140, 90], alpha=0.9)204        masked_img = Image.fromarray(masked_img)205        206    return mask[0], {'image': masked_img, 'points': points}, logits / 32.0207 208@spaces.GPU(duration=80)209def run_objctrl_2_5d(condition_image, 210                        mask, 211                        depth, 212                        RTs, 213                        bg_mode, 214                        shared_wapring_latents, 215                        scale_wise_masks, 216                        rescale, 217                        seed, 218                        ds, dt, 219                        num_inference_steps=25):220    seed = int(seed)221            222    center_h_margin, center_w_margin = center_margin, center_margin223    depth_center = np.mean(depth[height//2-center_h_margin:height//2+center_h_margin, width//2-center_w_margin:width//2+center_w_margin])224    225    if rescale > 0:226        depth_rescale = round(depth_scale_ * rescale / depth_center, 2)227    else:228        depth_rescale = 1.0229        230    depth = depth * depth_rescale231    232    depth_down = F.interpolate(torch.tensor(depth).unsqueeze(0).unsqueeze(0), 233                                (H, W), mode='bilinear', align_corners=False).squeeze().numpy() # [H, W]234    235    ## latent236    generator = torch.Generator()237    generator.manual_seed(seed)238    239    latents_org = pipeline.prepare_latents(240            1,241            14,242            8,243            height,244            width,245            pipeline.dtype,246            device,247            generator,248            None,249        )250    latents_org = latents_org / pipeline.scheduler.init_noise_sigma251    252    cur_plucker_embedding, _, _ = RT2Plucker(RTs, RTs.shape[0], (height, width), fx, fy, cx, cy) # 6, V, H, W253    cur_plucker_embedding = cur_plucker_embedding.to(device)254    cur_plucker_embedding = cur_plucker_embedding[None, ...] # b 6 f h w255    cur_plucker_embedding = cur_plucker_embedding.permute(0, 2, 1, 3, 4) # b f 6 h w256    cur_plucker_embedding = cur_plucker_embedding[:, :num_frames, ...]257    cur_pose_features = pipeline.pose_encoder(cur_plucker_embedding)258    259    # bg_mode = ["Fixed", "Reverse", "Free"]260    if bg_mode == "Fixed":261        fix_RTs = np.repeat(RTs[0][None, ...], num_frames, axis=0) # [n_frame, 4, 3]262        fix_plucker_embedding, _, _ = RT2Plucker(fix_RTs, num_frames, (height, width), fx, fy, cx, cy) # 6, V, H, W263        fix_plucker_embedding = fix_plucker_embedding.to(device)264        fix_plucker_embedding = fix_plucker_embedding[None, ...] # b 6 f h w265        fix_plucker_embedding = fix_plucker_embedding.permute(0, 2, 1, 3, 4) # b f 6 h w266        fix_plucker_embedding = fix_plucker_embedding[:, :num_frames, ...]267        fix_pose_features = pipeline.pose_encoder(fix_plucker_embedding)268        269    elif bg_mode == "Reverse":270        bg_plucker_embedding, _, _ = RT2Plucker(RTs[::-1], RTs.shape[0], (height, width), fx, fy, cx, cy) # 6, V, H, W271        bg_plucker_embedding = bg_plucker_embedding.to(device)272        bg_plucker_embedding = bg_plucker_embedding[None, ...] # b 6 f h w273        bg_plucker_embedding = bg_plucker_embedding.permute(0, 2, 1, 3, 4) # b f 6 h w274        bg_plucker_embedding = bg_plucker_embedding[:, :num_frames, ...]275        fix_pose_features = pipeline.pose_encoder(bg_plucker_embedding)276        277    else:278        fix_pose_features = None279        280    #### preparing mask281 282    mask = Image.fromarray(mask)283    mask = mask.resize((W, H))284    mask = np.array(mask).astype(np.float32)285    mask = np.expand_dims(mask, axis=-1)286    287    # visulize mask288    if DEBUG:289        mask_sum_vis = mask[..., 0]290        mask_sum_vis = (mask_sum_vis * 255.0).astype(np.uint8)291        mask_sum_vis = Image.fromarray(mask_sum_vis)292        293        mask_sum_vis.save(f'{cur_OUTPUT_PATH}/org_mask.png')294    295    try:296        warped_masks = Unprojected(mask, depth_down, RTs, H=H, W=W, K=K)297    298        warped_masks.insert(0, mask)299            300    except:301        # mask to bbox302        print(f'!!! Mask is too small to warp; mask to bbox') 303        mask = mask[:, :, 0]304        coords = cv2.findNonZero(mask)305        x, y, w, h = cv2.boundingRect(coords)306        # mask[y:y+h, x:x+w] = 1.0307        308        center_x, center_y = x + w // 2, y + h // 2309        center_z = depth_down[center_y, center_x]310        311        # RTs [n_frame, 3, 4] to [n_frame, 4, 4] , add [0, 0, 0, 1]312        RTs = np.concatenate([RTs, np.array([[[0, 0, 0, 1]]] * num_frames)], axis=1)313        314        # RTs: world to camera315        P0 = np.array([center_x, center_y, 1])316        Pc0 = np.linalg.inv(K) @ P0 * center_z317        pw = np.linalg.inv(RTs[0]) @ np.array([Pc0[0], Pc0[1], center_z, 1]) # [4]318        319        P = [np.array([center_x, center_y])]320        for i in range(1, num_frames):321            Pci = RTs[i] @ pw322            Pi = K @ Pci[:3] / Pci[2]323            P.append(Pi[:2])324        325        warped_masks = [mask]326        for i in range(1, num_frames):327            shift_x = int(round(P[i][0] - P[0][0]))328            shift_y = int(round(P[i][1] - P[0][1]))329 330            cur_mask = roll_with_ignore_multidim(mask, [shift_y, shift_x])331            warped_masks.append(cur_mask)332            333            334        warped_masks = [v[..., None] for v in warped_masks]335            336    warped_masks = np.stack(warped_masks, axis=0) # [f, h, w]337    warped_masks = np.repeat(warped_masks, 3, axis=-1) # [f, h, w, 3]338    339    mask_sum = np.sum(warped_masks, axis=0, keepdims=True)  # [1, H, W, 3]340    mask_sum[mask_sum > 1.0] = 1.0341    mask_sum = mask_sum[0,:,:, 0]342    343    if DEBUG:344        ## visulize warp mask    345        warp_masks_vis = torch.tensor(warped_masks)346        warp_masks_vis = (warp_masks_vis * 255.0).to(torch.uint8)347        torchvision.io.write_video(f'{cur_OUTPUT_PATH}/warped_masks.mp4', warp_masks_vis, fps=10, video_codec='h264', options={'crf': '10'})348        349        # visulize mask350        mask_sum_vis = mask_sum351        mask_sum_vis = (mask_sum_vis * 255.0).astype(np.uint8)352        mask_sum_vis = Image.fromarray(mask_sum_vis)353        354        mask_sum_vis.save(f'{cur_OUTPUT_PATH}/merged_mask.png')355        356    if scale_wise_masks:357        min_area = H * W * area_ratio # cal in downscale358        non_zero_len = mask_sum.sum() 359        360        print(f'non_zero_len: {non_zero_len}, min_area: {min_area}')361        362        if non_zero_len > min_area:363            kernel_sizes = [1, 1, 1, 3]364        elif non_zero_len > min_area * 0.5:365            kernel_sizes = [3, 1, 1, 5]366        else:367            kernel_sizes = [5, 3, 3, 7]368    else:369        kernel_sizes = [1, 1, 1, 1]370        371    mask = torch.from_numpy(mask_sum) # [h, w]372    mask = mask[None, None, ...] # [1, 1, h, w]373    mask = F.interpolate(mask, (height, width), mode='bilinear', align_corners=False) # [1, 1, H, W]374    # mask = mask.repeat(1, num_frames, 1, 1) # [1, f, H, W]375    mask = mask.to(pipeline.dtype).to(device)376    377    ##### Mask End ######378    379    ### Got blending pose features Start ###380 381    pose_features = []382    for i in range(0, len(cur_pose_features)):383        kernel_size = kernel_sizes[i]384        h, w = cur_pose_features[i].shape[-2:]385        386        if fix_pose_features is None:387            pose_features.append(torch.zeros_like(cur_pose_features[i]))388        else:389            pose_features.append(fix_pose_features[i])390            391        cur_mask = F.interpolate(mask, (h, w), mode='bilinear', align_corners=False)392        cur_mask = dilate_mask_pytorch(cur_mask, kernel_size=kernel_size) # [1, 1, H, W]393        cur_mask = cur_mask.repeat(1, num_frames, 1, 1) # [1, f, H, W]394        395        if DEBUG:396            # visulize mask397            mask_vis = cur_mask[0, 0].cpu().numpy() * 255.0398            mask_vis = Image.fromarray(mask_vis.astype(np.uint8))399            mask_vis.save(f'{cur_OUTPUT_PATH}/mask_k{kernel_size}_scale{i}.png')400            401        cur_mask = cur_mask[None, ...] # [1, 1, f, H, W]402        pose_features[-1] = cur_pose_features[i] * cur_mask + pose_features[-1] * (1 - cur_mask)403 404    ### Got blending pose features End ###405    406    ##### Warp Noise Start ######407    408    if shared_wapring_latents:409        noise = latents_org[0, 0].data.cpu().numpy().copy() #[14, 4, 40, 72]410        noise = np.transpose(noise, (1, 2, 0)) # [40, 72, 4]411 412        try:413            warp_noise = Unprojected(noise, depth_down, RTs, H=H, W=W, K=K)414            warp_noise.insert(0, noise)415        except:416            print(f'!!! Noise is too small to warp; mask to bbox')417            418            warp_noise = [noise]419            for i in range(1, num_frames):420                shift_x = int(round(P[i][0] - P[0][0]))421                shift_y = int(round(P[i][1] - P[0][1]))422                423                cur_noise= roll_with_ignore_multidim(noise, [shift_y, shift_x])424                warp_noise.append(cur_noise)425                426            warp_noise = np.stack(warp_noise, axis=0) # [f, h, w, 4]427    428        if DEBUG:429            ## visulize warp noise430            warp_noise_vis = torch.tensor(warp_noise)[..., :3] * torch.tensor(warped_masks)431            warp_noise_vis = (warp_noise_vis - warp_noise_vis.min()) / (warp_noise_vis.max() - warp_noise_vis.min())432            warp_noise_vis = (warp_noise_vis * 255.0).to(torch.uint8)433    434            torchvision.io.write_video(f'{cur_OUTPUT_PATH}/warp_noise.mp4', warp_noise_vis, fps=10, video_codec='h264', options={'crf': '10'})435    436    437        warp_latents = torch.tensor(warp_noise).permute(0, 3, 1, 2).to(latents_org.device).to(latents_org.dtype) # [frame, 4, H, W]438        warp_latents = warp_latents.unsqueeze(0) # [1, frame, 4, H, W]439        440        warped_masks = torch.tensor(warped_masks).permute(0, 3, 1, 2).unsqueeze(0) # [1, frame, 3, H, W]441        mask_extend = torch.concat([warped_masks, warped_masks[:,:,0:1]], dim=2) # [1, frame, 4, H, W]442        mask_extend = mask_extend.to(latents_org.device).to(latents_org.dtype)443        444        warp_latents = warp_latents * mask_extend + latents_org * (1 - mask_extend)445        warp_latents = warp_latents.permute(0, 2, 1, 3, 4)446        random_noise = latents_org.clone().permute(0, 2, 1, 3, 4)447            448        filter_shape = warp_latents.shape449 450        freq_filter = get_freq_filter(451            filter_shape, 452            device = device, 453            filter_type='butterworth',454            n=4,455            d_s=ds,456            d_t=dt457        )458        459        warp_latents = freq_mix_3d(warp_latents, random_noise, freq_filter)460        warp_latents = warp_latents.permute(0, 2, 1, 3, 4)461        462    else:463        warp_latents = latents_org.clone()464        465    generator.manual_seed(42)466 467    with torch.no_grad():468        result = pipeline(469            image=condition_image,470            pose_embedding=cur_plucker_embedding,471            height=height,472            width=width,473            num_frames=num_frames,474            num_inference_steps=num_inference_steps,475            min_guidance_scale=min_guidance_scale,476            max_guidance_scale=max_guidance_scale,477            do_image_process=True,478            generator=generator,479            output_type='pt',480            pose_features= pose_features,481            latents = warp_latents482        ).frames[0].cpu() #[f, c, h, w]483        484    485    result = rearrange(result, 'f c h w -> f h w c')486    result = (result * 255.0).to(torch.uint8)487 488    video_path = tempfile.NamedTemporaryFile(suffix='.mp4').name489    torchvision.io.write_video(video_path, result, fps=10, video_codec='h264', options={'crf': '8'})490    491    return video_path492 493 494# UI function495@spaces.GPU(duration=7)496def process_image(raw_image, trajectory_points):497    498    image, points = raw_image['image'], raw_image['points']499    500    print(points)501    502    try:503        assert(len(points)) == 1, "Please draw only one bbox"504        [x1, y1, _, x2, y2, _] = points[0]505        506        image = image.crop((x1, y1, x2, y2))507        image = image.resize((width, height))508    except:509        image = image.resize((width, height))510 511    depth = d_model_NK.infer_pil(image)    512    colored_depth = colorize(depth, cmap='gray_r') # [h, w, 4] 0-255513    514    depth_img = deepcopy(colored_depth[:, :, :3])515    if len(trajectory_points) > 0:516        for idx, point in enumerate(trajectory_points):517            if idx % 2 == 0:518                cv2.circle(depth_img, tuple(point), 10, (255, 0, 0), -1)519            else:520                cv2.circle(depth_img, tuple(point), 10, (0, 0, 255), -1)521            if idx > 0:522                line_length = np.sqrt((trajectory_points[idx][0] - trajectory_points[idx-1][0])**2 + (trajectory_points[idx][1] - trajectory_points[idx-1][1])**2)523                arrow_head_length = 10524                tip_length = arrow_head_length / line_length525                cv2.arrowedLine(depth_img, trajectory_points[idx-1], trajectory_points[idx], (0, 255, 0), 4, tipLength=tip_length)526    527    return image, {'image': image}, depth, depth_img, colored_depth[:, :, :3]528 529 530 531def draw_points_on_image(img, points):532    # img = Image.fromarray(np.array(image))533    draw = ImageDraw.Draw(img)534    535    for p in points:536        x1, y1, _, x2, y2, _ = p537        538        if x2 == 0 and y2 == 0:539            # Point: ้’่‰ฒ็‚นๅธฆ้ป‘่พน540            point_radius = 4541            draw.ellipse(542                (x1 - point_radius, y1 - point_radius, x1 + point_radius, y1 + point_radius),543                fill="cyan", outline="black", width=1544            )545        else:546            # Bounding Box: ้ป‘่‰ฒ็Ÿฉๅฝขๆก†547            draw.rectangle([x1, y1, x2, y2], outline="black", width=3)548    549    return img550 551@spaces.GPU(duration=15)552def from_examples(raw_input, raw_image_points, canvas, seg_image_points, selected_points_text, camera_option, mask_bk):553    raw_image_points = ast.literal_eval(raw_image_points)554    seg_image_points = ast.literal_eval(seg_image_points)555    556    selected_points = ast.literal_eval(selected_points_text)557    mask = np.array(mask_bk)558    mask = mask[:,:,0] > 0559    selected_points = ast.literal_eval(selected_points_text)560    561    image, _, depth, depth_img, colored_depth = process_image({'image': raw_input['image'], 'points': raw_image_points}, selected_points)562    563    # get camera pose564    if camera_option == "None":565        # traj2came566        rescale = 1.0567        camera_pose, camera_pose_vis, rescale, _ = traj2cam(selected_points,  depth , rescale)568    else:569        rescale = 0.0570        angle = 60571        speed = 4.0572        camera_pose, camera_pose_vis, rescale = get_camera_pose(CAMERA_MODE)(camera_option, depth, mask, rescale, angle, speed)573        574    575    raw_image = draw_points_on_image(raw_input['image'], raw_image_points)576    seg_image = draw_points_on_image(canvas['image'], seg_image_points)577        578    return image, mask, depth, depth_img, colored_depth, camera_pose, \579            camera_pose_vis, rescale, selected_points, \580            gr.update(value={'image': raw_image, 'points': raw_image_points}), \581            gr.update(value={'image': seg_image, 'points': seg_image_points}), \582 583 584# -------------- UI definition --------------585with gr.Blocks() as demo:586    # layout definition587    gr.Markdown(title)588    # gr.Markdown(authors)589    # gr.Markdown(affiliation)590    gr.Markdown(important_link)591    gr.Markdown(description)592    593    594    # with gr.Row():595    #     gr.Markdown("""# <center>Repositioning the Subject within Image </center>""")596    mask = gr.State(value=None) # store mask597    mask_bk = gr.Image(type="pil", label="Mask", show_label=True, interactive=False, visible=False)598    599    removal_mask = gr.State(value=None) # store removal mask600    selected_points = gr.State([]) # store points601    selected_points_text = gr.Textbox(label="Selected Points", visible=False)602    raw_image_points = gr.Textbox(label="Raw Image Points", visible=False)603    seg_image_points = gr.Textbox(label="Segment Image Points", visible=False)604    605    original_image = gr.State(value=None) # store original input image606    # masked_original_image = gr.State(value=None) # store masked input image607    mask_logits = gr.State(value=None) # store mask logits608    609    depth = gr.State(value=None) # store depth610    org_depth_image = gr.State(value=None) # store original depth image611    612    camera_pose = gr.State(value=None) # store camera pose613    614    rescale = gr.Slider(minimum=0.0, maximum=10, step=0.1, value=1.0, label="Rescale", interactive=True, visible=False)615    angle = gr.Slider(minimum=-360, maximum=360, step=1, value=60, label="Angle", interactive=True, visible=False)616    617    seed = gr.Textbox(value = "42", label="Seed", interactive=True, visible=False)618    scale_wise_masks = gr.Checkbox(label="Enable Scale-wise Masks", interactive=True, value=True, visible=False)619    ds = gr.Slider(minimum=0.0, maximum=1, step=0.1, value=0.25, label="ds", interactive=True, visible=False)620    dt = gr.Slider(minimum=0.0, maximum=1, step=0.1, value=0.1, label="dt", interactive=True, visible=False)621    622    with gr.Column():623        624        outlines = """625        <font size="5"><b>There are total 5 steps to complete the task.</b></font>626        - Step 1: Input an image and Crop it to a suitable size and attained depth;627        - Step 2: Attain the subject mask;628        - Step 3: Draw trajectory on depth map or skip to use camera pose;629        - Step 4: Select camera poses or skip.630        - Step 5: Generate the final video.631        """632        633        gr.Markdown(outlines)634        635        636        with gr.Row():637            with gr.Column():638                # Step 1: Input Image639                step1_dec = """640                    <font size="4"><b>Step 1: Input Image</b></font>641                    """642                step1 = gr.Markdown(step1_dec)643                raw_input = ImagePrompter(type="pil", label="Raw Image", show_label=True, interactive=True)644                645                step1_notes = """646                - Select the region using a <mark>bounding box</mark>, aiming for a ratio close to </mark>320:576</mark> (height:width).647                - If the input is in 320 x 576, press `Process` directly.648                """649                notes = gr.Markdown(step1_notes)650                651                process_button = gr.Button("Process")652                653            with gr.Column():654                # Step 2: Get Subject Mask655                step2_dec = """656                    <font size="4"><b>Step 2: Get Subject Mask</b></font>657                    """658                step2 = gr.Markdown(step2_dec)659                canvas = ImagePrompter(type="pil", label="Input Image", show_label=True, interactive=True) # for mask painting660 661                step2_notes = """662                    - Use the <mark>bounding boxes</mark> or <mark>points</mark> to select the subject.663                    - Press `Segment Subject` to get the mask. <mark>Can be refined iteratively by updating points<mark>.664                """665                notes = gr.Markdown(step2_notes)666 667                select_button = gr.Button("Segment Subject")668                669            with gr.Column():670                # Step 3: Get Depth and Draw Trajectory671                step3_dec = """672                    <font size="4"><b>Step 3: Draw Trajectory on Depth or <mark>SKIP</mark></b></font>673                    674                """675                step3 = gr.Markdown(step3_dec)676                depth_image = gr.Image(type="pil", label="Depth Image", show_label=True, interactive=False)677                678                step3_dec = """679                    - Selecting points on the depth image. <mark>No more than 14 points</mark>.680                    - Press `Undo point` to remove all points. Press `Traj2Cam` to get camera poses.681                    """682                notes = gr.Markdown(step3_dec)683                684                undo_button = gr.Button("Undo point")685                traj2cam_button = gr.Button("Traj2Cam")686                687        with gr.Row():688            689            with gr.Column():690                # Step 4: Trajectory to Camera Pose or Get Camera Pose691                step4_dec = """692                    <font size="4"><b>Step 4: Get Customized Camera Poses or <mark>SKIP</mark></b></font>693                """694                step4 = gr.Markdown(step4_dec)695                camera_pose_vis = gr.Plot(None, label='Camera Pose')696                camera_option = gr.Radio(choices = CAMERA_MODE, label='Camera Options', value=CAMERA_MODE[0], interactive=True)697                speed = gr.Slider(minimum=0.1, maximum=10, step=0.1, value=4.0, label="Speed", interactive=True, visible=True)698                        699            with gr.Column():700                # Step 5: Get the final generated video701                step5_dec = """702                    <font size="4"><b>Step 5: Get the Final Generated Video</b></font>703                """704                step5 = gr.Markdown(step5_dec)705                generated_video = gr.Video(None, label='Generated Video')706                707                # with gr.Row():708                bg_mode = gr.Radio(choices = ["Fixed", "Reverse", "Free"], label="Background Mode", value="Fixed", interactive=True)709                shared_wapring_latents = gr.Checkbox(label="Enable Shared Warping Latents", interactive=True, value=False, visible=True)710                711                generated_button = gr.Button("Generate")712 713                get_mid_params_button = gr.Button("Get Mid Params", visible=False)714                715 716    # # event definition717    process_button.click(718        fn = process_image,719        inputs = [raw_input, selected_points],720        outputs = [original_image, canvas, depth, depth_image, org_depth_image]721    )722    723    select_button.click(724        segment,725        [canvas, original_image, mask_logits],726        [mask, canvas, mask_logits]727    )728    729    depth_image.select(730        get_points,731        [depth_image, selected_points],732        [depth_image, selected_points],733    )734    undo_button.click(735        undo_points,736        [org_depth_image],737        [depth_image, selected_points]738    )739    740    traj2cam_button.click(741        traj2cam,742        [selected_points, depth, rescale],743        [camera_pose, camera_pose_vis, rescale, camera_option]744    )745    746    camera_option.change(747        get_camera_pose(CAMERA_MODE),748        [camera_option, depth, mask, rescale, angle, speed],749        [camera_pose, camera_pose_vis, rescale]750    )751    752    generated_button.click(753        run_objctrl_2_5d,754        [755         original_image,756         mask,757         depth,758         camera_pose,759         bg_mode,760         shared_wapring_latents,761         scale_wise_masks,762         rescale,763         seed,764         ds,765         dt,766        #  num_inference_steps767         ],768        [generated_video],769    )770    771    get_mid_params_button.click(772        get_mid_params,773        [raw_input, canvas, mask, selected_points, camera_option, bg_mode, shared_wapring_latents, generated_video]774    )775    776    ## Get examples777    with open('./assets/examples/examples.json', 'r') as f:778        examples = json.load(f)779        # print(examples)780    781    # examples = [examples]782    examples = [v for k, v in examples.items()]783 784    gr.Examples(785        examples=examples,786        inputs=[787            raw_input,788            raw_image_points,789            canvas,790            seg_image_points,791            mask_bk,792            selected_points_text,  # selected_points793            camera_option,794            bg_mode,795            shared_wapring_latents,796            generated_video797        ],798        examples_per_page=20799    )800    801    selected_points_text.change(802        from_examples,803        inputs=[raw_input, raw_image_points, canvas, seg_image_points, selected_points_text, camera_option, mask_bk],804        outputs=[original_image, mask, depth, depth_image, org_depth_image, camera_pose, camera_pose_vis, rescale, selected_points, raw_input, canvas]805    )806    807            808 809 810    gr.Markdown(article)811 812 813demo.queue().launch(share=True)814