import os import fire import gradio as gr from PIL import Image from functools import partial import argparse import sys import torch if os.getenv('SYSTEM') == 'spaces': # os.system('pip install --global-option="--no-networks" --use-pep517 git+https://github.com/NVlabs/tiny-cuda-nn/#subdirectory=bindings/torch') os.system('pip install git+https://github.com/NVlabs/tiny-cuda-nn/#subdirectory=bindings/torch') print('tcnn done') os.system('pip install fvcore iopath') print('fvcore done') os.system('pip install git+https://github.com/facebookresearch/pytorch3d.git@v0.7.2') print('pt3d done') import cv2 import time import numpy as np import trimesh from segment_anything import build_sam, SamPredictor import random from pytorch3d import transforms import torchvision import torch.distributed as dist import nvdiffrast.torch as dr from video3d.model_ddp import Unsup3DDDP, forward_to_matrix from video3d.trainer_few_shot import Fewshot_Trainer from video3d.trainer_ddp import TrainerDDP from video3d import setup_runtime from video3d.render.mesh import make_mesh from video3d.utils.skinning_v4 import estimate_bones, skinning, euler_angles_to_matrix from video3d.utils.misc import save_obj from video3d.render import util import matplotlib.pyplot as plt from pytorch3d import utils, renderer, transforms, structures, io from video3d.render.render import render_mesh from video3d.render.material import texture as material_texture _TITLE = '''Learning the 3D Fauna of the Web''' _DESCRIPTION = '''
Reconstruct any quadruped animal from one image.
The demo only contains the 3D reconstruction part.
''' _GPU_ID = 0 if not hasattr(Image, 'Resampling'): Image.Resampling = Image def sam_init(): sam_checkpoint = os.path.join(os.path.dirname(__file__), "sam_pt", "sam_vit_h_4b8939.pth") model_type = "vit_h" # sam = sam_model_registry[model_type](checkpoint=sam_checkpoint).to(device=f"cuda:{_GPU_ID}") # predictor = SamPredictor(sam) predictor = SamPredictor(build_sam(checkpoint=sam_checkpoint).to("cuda")) return predictor def sam_segment(predictor, input_image, *bbox_coords): bbox = np.array(bbox_coords) image = np.asarray(input_image) start_time = time.time() predictor.set_image(image) masks_bbox, scores_bbox, logits_bbox = predictor.predict( box=bbox, multimask_output=True ) print(f"SAM Time: {time.time() - start_time:.3f}s") out_image = np.zeros((image.shape[0], image.shape[1], 4), dtype=np.uint8) out_image[:, :, :3] = image out_image_bbox = out_image.copy() out_image_bbox[:, :, 3] = masks_bbox[-1].astype(np.uint8) * 255 torch.cuda.empty_cache() # return Image.fromarray(out_image_bbox, mode='RGB') x_nonzero = np.nonzero(masks_bbox[-1].astype(np.uint8).sum(axis=0)) y_nonzero = np.nonzero(masks_bbox[-1].astype(np.uint8).sum(axis=1)) x_min = int(x_nonzero[0].min()) y_min = int(y_nonzero[0].min()) x_max = int(x_nonzero[0].max()) y_max = int(y_nonzero[0].max()) out_image_bbox = out_image_bbox[y_min:y_max, x_min:x_max] out_image_valid = np.concatenate([ out_image_bbox[:, :, :3], np.ones_like(out_image_bbox[:, :, 3:]) * 255 ], axis=-1) return Image.fromarray(out_image_valid, mode='RGBA') def expand2square(pil_img, background_color): width, height = pil_img.size if width == height: return pil_img elif width > height: result = Image.new(pil_img.mode, (width, width), background_color) result.paste(pil_img, (0, (width - height) // 2)) return result else: result = Image.new(pil_img.mode, (height, height), background_color) result.paste(pil_img, ((height - width) // 2, 0)) return result def preprocess(predictor, input_image, chk_group=None, segment=False): RES = 1024 input_image.thumbnail([RES, RES], Image.Resampling.LANCZOS) if chk_group is not None: segment = "Use SAM to center animal" in chk_group if segment: image_rem = input_image.convert('RGB') arr = np.asarray(image_rem)[:,:,-1] x_nonzero = np.nonzero(arr.sum(axis=0)) y_nonzero = np.nonzero(arr.sum(axis=1)) x_min = int(x_nonzero[0].min()) y_min = int(y_nonzero[0].min()) x_max = int(x_nonzero[0].max()) y_max = int(y_nonzero[0].max()) input_image = sam_segment(predictor, input_image.convert('RGB'), x_min, y_min, x_max, y_max) input_image = expand2square(input_image, (0, 0, 0, 255)) return input_image, input_image.resize((256, 256), Image.Resampling.LANCZOS) def save_images(images, mask_pred, mode="transparent"): img = images[0] mask = mask_pred[0] img = img.clamp(0, 1) if mask is not None: mask = mask.clamp(0, 1) if mode == "white": img = img * mask + 1 * (1 - mask) elif mode == "black": img = img * mask + 0 * (1 - mask) else: img = torch.cat([img, mask[0:1]], 0) img = img.permute(1, 2, 0).cpu().numpy() img = Image.fromarray(np.uint8(img * 255)) return img def get_bank_embedding(rgb, memory_bank_keys, memory_bank, model, memory_bank_topk=10, memory_bank_dim=128): images = rgb batch_size, num_frames, _, h0, w0 = images.shape images = images.reshape(batch_size*num_frames, *images.shape[2:]) # 0~1 images_in = images * 2 - 1 # rescale to (-1, 1) for DINO x = images_in with torch.no_grad(): b, c, h, w = x.shape model.netInstance.netEncoder._feats = [] model.netInstance.netEncoder._register_hooks([11], 'key') #self._register_hooks([11], 'token') x = model.netInstance.netEncoder.ViT.prepare_tokens(x) #x = self.ViT.prepare_tokens_with_masks(x) for blk in model.netInstance.netEncoder.ViT.blocks: x = blk(x) out = model.netInstance.netEncoder.ViT.norm(x) model.netInstance.netEncoder._unregister_hooks() ph, pw = h // model.netInstance.netEncoder.patch_size, w // model.netInstance.netEncoder.patch_size patch_out = out[:, 1:] # first is class token patch_out = patch_out.reshape(b, ph, pw, model.netInstance.netEncoder.vit_feat_dim).permute(0, 3, 1, 2) patch_key = model.netInstance.netEncoder._feats[0][:,:,1:] # B, num_heads, num_patches, dim patch_key = patch_key.permute(0, 1, 3, 2).reshape(b, model.netInstance.netEncoder.vit_feat_dim, ph, pw) global_feat = out[:, 0] batch_features = global_feat batch_size = batch_features.shape[0] query = torch.nn.functional.normalize(batch_features.unsqueeze(1), dim=-1) # [B, 1, d_k] key = torch.nn.functional.normalize(memory_bank_keys, dim=-1) # [size, d_k] key = key.transpose(1, 0).unsqueeze(0).repeat(batch_size, 1, 1).to(query.device) # [B, d_k, size] cos_dist = torch.bmm(query, key).squeeze(1) # [B, size], larger the more similar rank_idx = torch.sort(cos_dist, dim=-1, descending=True)[1][:, :memory_bank_topk] # [B, k] value = memory_bank.unsqueeze(0).repeat(batch_size, 1, 1).to(query.device) # [B, size, d_v] out = torch.gather(value, dim=1, index=rank_idx[..., None].repeat(1, 1, memory_bank_dim)) # [B, k, d_v] weights = torch.gather(cos_dist, dim=-1, index=rank_idx) # [B, k] weights = torch.nn.functional.normalize(weights, p=1.0, dim=-1).unsqueeze(-1).repeat(1, 1, memory_bank_dim) # [B, k, d_v] weights have been normalized out = weights * out out = torch.sum(out, dim=1) batch_mean_out = torch.mean(out, dim=0) weight_aux = { 'weights': weights[:, :, 0], # [B, k], weights from large to small 'pick_idx': rank_idx, # [B, k] } batch_embedding = batch_mean_out embeddings = out weights = weight_aux bank_embedding_model_input = [batch_embedding, embeddings, weights] return bank_embedding_model_input class FixedDirectionLight(torch.nn.Module): def __init__(self, direction, amb, diff): super(FixedDirectionLight, self).__init__() self.light_dir = direction self.amb = amb self.diff = diff self.is_hacking = not (isinstance(self.amb, float) or isinstance(self.amb, int)) def forward(self, feat): batch_size = feat.shape[0] if self.is_hacking: return torch.concat([self.light_dir, self.amb, self.diff], -1) else: return torch.concat([self.light_dir, torch.FloatTensor([self.amb, self.diff]).to(self.light_dir.device)], -1).expand(batch_size, -1) def shade(self, feat, kd, normal): light_params = self.forward(feat) light_dir = light_params[..., :3][:, None, None, :] int_amb = light_params[..., 3:4][:, None, None, :] int_diff = light_params[..., 4:5][:, None, None, :] shading = (int_amb + int_diff * torch.clamp(util.dot(light_dir, normal), min=0.0)) shaded = shading * kd return shaded, shading def render_bones(mvp, bones_pred, size=(256, 256)): bone_world4 = torch.concat([bones_pred, torch.ones_like(bones_pred[..., :1]).to(bones_pred.device)], dim=-1) b, f, num_bones = bone_world4.shape[:3] bones_clip4 = (bone_world4.view(b, f, num_bones*2, 1, 4) @ mvp.transpose(-1, -2).reshape(b, f, 1, 4, 4)).view(b, f, num_bones, 2, 4) bones_uv = bones_clip4[..., :2] / bones_clip4[..., 3:4] # b, f, num_bones, 2, 2 dpi = 32 fx, fy = size[1] // dpi, size[0] // dpi rendered = [] for b_idx in range(b): for f_idx in range(f): frame_bones_uv = bones_uv[b_idx, f_idx].cpu().numpy() fig = plt.figure(figsize=(fx, fy), dpi=dpi, frameon=False) ax = plt.Axes(fig, [0., 0., 1., 1.]) ax.set_axis_off() for bone in frame_bones_uv: ax.plot(bone[:, 0], bone[:, 1], marker='o', linewidth=8, markersize=20) ax.set_xlim(-1, 1) ax.set_ylim(-1, 1) ax.invert_yaxis() # Convert to image fig.add_axes(ax) fig.canvas.draw_idle() image = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8) w, h = fig.canvas.get_width_height() image.resize(h, w, 3) rendered += [image / 255.] return torch.from_numpy(np.stack(rendered, 0).transpose(0, 3, 1, 2)).to(bones_pred.device) def add_mesh_color(mesh, color): verts = mesh.verts_padded() color = torch.FloatTensor(color).to(verts.device).view(1,1,3) / 255 mesh.textures = renderer.TexturesVertex(verts_features=verts*0+color) return mesh def create_sphere(position, scale, device, color=[139, 149, 173]): mesh = utils.ico_sphere(2).to(device) mesh = mesh.extend(position.shape[0]) # scale and offset mesh = mesh.update_padded(mesh.verts_padded() * scale + position[:, None]) mesh = add_mesh_color(mesh, color) return mesh def estimate_bone_rotation(b): """ (0, 0, 1) = matmul(R^(-1), b) assumes x, y is a symmetry plane returns R """ b = b / torch.norm(b, dim=-1, keepdim=True) n = torch.FloatTensor([[1, 0, 0]]).to(b.device) n = n.expand_as(b) v = torch.cross(b, n, dim=-1) R = torch.stack([n, v, b], dim=-1).transpose(-2, -1) return R def estimate_vector_rotation(vector_a, vector_b): """ vector_a = matmul(R, vector_b) returns R https://math.stackexchange.com/questions/180418/calculate-rotation-matrix-to-align-vector-a-to-vector-b-in-3d """ vector_a = vector_a / torch.norm(vector_a, dim=-1, keepdim=True) vector_b = vector_b / torch.norm(vector_b, dim=-1, keepdim=True) v = torch.cross(vector_a, vector_b, dim=-1) c = torch.sum(vector_a * vector_b, dim=-1) skew = torch.stack([ torch.stack([torch.zeros_like(v[..., 0]), -v[..., 2], v[..., 1]], dim=-1), torch.stack([v[..., 2], torch.zeros_like(v[..., 0]), -v[..., 0]], dim=-1), torch.stack([-v[..., 1], v[..., 0], torch.zeros_like(v[..., 0])], dim=-1)], dim=-1) R = torch.eye(3, device=vector_a.device)[None] + skew + torch.matmul(skew, skew) / (1 + c[..., None, None]) return R def create_elipsoid(bone, scale=0.05, color=[139, 149, 173], generic_rotation_estim=True): length = torch.norm(bone[:, 0] - bone[:, 1], dim=-1) mesh = utils.ico_sphere(2).to(bone.device) mesh = mesh.extend(bone.shape[0]) # scale x, y verts = mesh.verts_padded() * torch.FloatTensor([scale, scale, 1]).to(bone.device) # stretch along z axis, set the start to origin verts[:, :, 2] = verts[:, :, 2] * length[:, None] * 0.5 + length[:, None] * 0.5 bone_vector = bone[:, 1] - bone[:, 0] z_vector = torch.FloatTensor([[0, 0, 1]]).to(bone.device) z_vector = z_vector.expand_as(bone_vector) if generic_rotation_estim: rot = estimate_vector_rotation(z_vector, bone_vector) else: rot = estimate_bone_rotation(bone_vector) tsf = transforms.Rotate(rot, device=bone.device) tsf = tsf.compose(transforms.Translate(bone[:, 0], device=bone.device)) verts = tsf.transform_points(verts) mesh = mesh.update_padded(verts) mesh = add_mesh_color(mesh, color) return mesh def convert_textures_vertex_to_textures_uv(meshes: structures.Meshes, color1, color2) -> renderer.TexturesUV: """ Convert a TexturesVertex object to a TexturesUV object. """ color1 = torch.Tensor(color1).to(meshes.device).view(1, 1, 3) / 255 color2 = torch.Tensor(color2).to(meshes.device).view(1, 1, 3) / 255 textures_vertex = meshes.textures assert isinstance(textures_vertex, renderer.TexturesVertex), "Input meshes must have TexturesVertex" verts_rgb = textures_vertex.verts_features_padded() faces_uvs = meshes.faces_padded() batch_size = verts_rgb.shape[0] maps = torch.zeros(batch_size, 128, 128, 3, device=verts_rgb.device) maps[:, :, :64, :] = color1 maps[:, :, 64:, :] = color2 is_first = (verts_rgb == color1)[..., 0] verts_uvs = torch.zeros(batch_size, verts_rgb.shape[1], 2, device=verts_rgb.device) verts_uvs[is_first] = torch.FloatTensor([0.25, 0.5]).to(verts_rgb.device) verts_uvs[~is_first] = torch.FloatTensor([0.75, 0.5]).to(verts_rgb.device) textures_uv = renderer.TexturesUV(maps=maps, faces_uvs=faces_uvs, verts_uvs=verts_uvs) meshes.textures = textures_uv return meshes def create_bones_scene(bones, joint_color=[66, 91, 140], bone_color=[119, 144, 189], show_end_point=False): meshes = [] for bone_i in range(bones.shape[1]): # points meshes += [create_sphere(bones[:, bone_i, 0], 0.1, bones.device, color=joint_color)] if show_end_point: meshes += [create_sphere(bones[:, bone_i, 1], 0.1, bones.device, color=joint_color)] # connecting ellipsoid meshes += [create_elipsoid(bones[:, bone_i], color=bone_color)] current_batch_size = bones.shape[0] meshes = [structures.join_meshes_as_scene([m[i] for m in meshes]) for i in range(current_batch_size)] mesh = structures.join_meshes_as_batch(meshes) return mesh def save_mesh(mesh, file_path): obj_file = file_path idx = 0 print("Writing mesh: ", obj_file) with open(obj_file, "w") as f: # f.write(f"mtllib {fname}.mtl\n") f.write("g default\n") v_pos = mesh.v_pos[idx].detach().cpu().numpy() if mesh.v_pos is not None else None v_nrm = mesh.v_nrm[idx].detach().cpu().numpy() if mesh.v_nrm is not None else None v_tex = mesh.v_tex[idx].detach().cpu().numpy() if mesh.v_tex is not None else None t_pos_idx = mesh.t_pos_idx[0].detach().cpu().numpy() if mesh.t_pos_idx is not None else None t_nrm_idx = mesh.t_nrm_idx[0].detach().cpu().numpy() if mesh.t_nrm_idx is not None else None t_tex_idx = mesh.t_tex_idx[0].detach().cpu().numpy() if mesh.t_tex_idx is not None else None print(" writing %d vertices" % len(v_pos)) for v in v_pos: f.write('v {} {} {} \n'.format(v[0], v[1], v[2])) if v_nrm is not None: print(" writing %d normals" % len(v_nrm)) assert(len(t_pos_idx) == len(t_nrm_idx)) for v in v_nrm: f.write('vn {} {} {}\n'.format(v[2], v[1], v[0])) # faces f.write("s 1 \n") f.write("g pMesh1\n") f.write("usemtl defaultMat\n") # Write faces print(" writing %d faces" % len(t_pos_idx)) for i in range(len(t_pos_idx)): f.write("f ") for j in range(3): f.write(' %s/%s/%s' % (str(t_pos_idx[i][j]+1), '' if v_tex is None else str(t_tex_idx[i][j]+1), '' if v_nrm is None else str(t_nrm_idx[i][j]+1))) f.write("\n") def process_mesh(shape, name): mesh = shape.clone() output_glb = f'./{name}.glb' output_obj = f'./{name}.obj' # save the obj file for download save_mesh(mesh, output_obj) # save the glb for visualize mesh_tri = trimesh.Trimesh( vertices=mesh.v_pos[0].detach().cpu().numpy(), faces=mesh.t_pos_idx[0][..., [2,1,0]].detach().cpu().numpy(), process=False, maintain_order=True ) norm_colors = (mesh.v_nrm[0][..., [2,1,0]].detach().cpu().numpy() + 1.0) * 0.5 * 1.8 * 255.0 norm_colors = np.clip(norm_colors, 0, 255) mesh_tri.visual.vertex_colors = norm_colors mesh_tri.export(file_obj=output_glb) return output_glb, output_obj def run_pipeline(model_items, cfgs, input_img): epoch = 999 total_iter = 999999 model = model_items[0] memory_bank = model_items[1] memory_bank_keys = model_items[2] device = f'cuda:{_GPU_ID}' input_image = torch.stack([torchvision.transforms.ToTensor()(input_img)], dim=0).to(device) with torch.no_grad(): model.netPrior.eval() model.netInstance.eval() input_image = torch.nn.functional.interpolate(input_image, size=(256, 256), mode='bilinear', align_corners=False) input_image = input_image[:, None, :, :] # [B=1, F=1, 3, 256, 256] bank_embedding = get_bank_embedding( input_image, memory_bank_keys, memory_bank, model, memory_bank_topk=cfgs.get("memory_bank_topk", 10), memory_bank_dim=128 ) prior_shape, dino_pred, classes_vectors = model.netPrior( category_name='tmp', perturb_sdf=False, total_iter=total_iter, is_training=False, class_embedding=bank_embedding ) Instance_out = model.netInstance( 'tmp', input_image, prior_shape, epoch, dino_features=None, dino_clusters=None, total_iter=total_iter, is_training=False ) # frame dim collapsed N=(B*F) if len(Instance_out) == 13: shape, pose_raw, pose, mvp, w2c, campos, texture_pred, im_features, dino_feat_im_calc, deform, all_arti_params, light, forward_aux = Instance_out im_features_map = None else: shape, pose_raw, pose, mvp, w2c, campos, texture_pred, im_features, dino_feat_im_calc, deform, all_arti_params, light, forward_aux, im_features_map = Instance_out class_vector = classes_vectors # the bank embeddings gray_light = FixedDirectionLight(direction=torch.FloatTensor([0, 0, 1]).to(device), amb=0.2, diff=0.7) image_pred, mask_pred, _, _, _, shading = model.render( shape, texture_pred, mvp, w2c, campos, (256, 256), background=model.background_mode, im_features=im_features, light=gray_light, prior_shape=prior_shape, render_mode='diffuse', render_flow=False, dino_pred=None, im_features_map=im_features_map ) mask_pred = mask_pred.expand_as(image_pred) shading = shading.expand_as(image_pred) # render bones in pytorch3D style posed_bones = forward_aux["posed_bones"].squeeze(1) jc, bc = [66, 91, 140], [119, 144, 189] bones_meshes = create_bones_scene(posed_bones, joint_color=jc, bone_color=bc, show_end_point=True) bones_meshes = convert_textures_vertex_to_textures_uv(bones_meshes, color1=jc, color2=bc) nv_meshes = make_mesh(verts=bones_meshes.verts_padded(), faces=bones_meshes.faces_padded()[0:1], uvs=bones_meshes.textures.verts_uvs_padded(), uv_idx=bones_meshes.textures.faces_uvs_padded()[0:1], material=material_texture.Texture2D(bones_meshes.textures.maps_padded())) # buffers = render_mesh(dr.RasterizeGLContext(), nv_meshes, mvp, w2c, campos, nv_meshes.material, lgt=gray_light, feat=im_features, dino_pred=None, resolution=(256,256), bsdf="diffuse") buffers = render_mesh(dr.RasterizeCudaContext(), nv_meshes, mvp, w2c, campos, nv_meshes.material, lgt=gray_light, feat=im_features, dino_pred=None, resolution=(256,256), bsdf="diffuse") shaded = buffers["shaded"].permute(0, 3, 1, 2) bone_image = shaded[:, :3, :, :] bone_mask = shaded[:, 3:, :, :] mask_final = mask_pred.logical_or(bone_mask) mask_final = mask_final.int() image_with_bones = bone_image * bone_mask * 0.5 + (shading * (1 - bone_mask * 0.5) + 0.5 * (mask_final.float() - mask_pred.float())) mesh_image = save_images(shading, mask_pred) mesh_bones_image = save_images(image_with_bones, mask_final) shape_glb, shape_obj = process_mesh(shape, 'reconstructed_shape') base_shape_glb, base_shape_obj = process_mesh(prior_shape, 'reconstructed_base_shape') return mesh_image, mesh_bones_image, shape_glb, shape_obj, base_shape_glb, base_shape_obj def run_demo(): parser = argparse.ArgumentParser() parser.add_argument('--gpu', default='0', type=str, help='Specify a GPU device') parser.add_argument('--num_workers', default=4, type=int, help='Specify the number of worker threads for data loaders') parser.add_argument('--seed', default=0, type=int, help='Specify a random seed') parser.add_argument('--config', default='./ckpts/configs.yml', type=str) # Model config path parser.add_argument('--checkpoint_path', default='./ckpts/iter0800000.pth', type=str) args = parser.parse_args() torch.manual_seed(args.seed) os.environ['MASTER_ADDR'] = 'localhost' os.environ['MASTER_PORT'] = '8088' dist.init_process_group("gloo", rank=_GPU_ID, world_size=1) torch.cuda.set_device(_GPU_ID) args.rank = _GPU_ID args.world_size = 1 args.gpu = f'{_GPU_ID}' device = f'cuda:{_GPU_ID}' resolution = (256, 256) batch_size = 1 model_cfgs = setup_runtime(args) bone_y_thresh = 0.4 body_bone_idx_preset = [3, 6, 6, 3] model_cfgs['body_bone_idx_preset'] = body_bone_idx_preset model = Unsup3DDDP(model_cfgs) # a hack attempt model.netPrior.classes_vectors = torch.nn.Parameter(torch.nn.init.uniform_(torch.empty(123, 128), a=-0.05, b=0.05)) cp = torch.load(args.checkpoint_path, map_location=device) model.load_model_state(cp) memory_bank_keys = cp['memory_bank_keys'] memory_bank = cp['memory_bank'] model.to(device) memory_bank.to(device) memory_bank_keys.to(device) model_items = [ model, memory_bank, memory_bank_keys ] predictor = sam_init() custom_theme = gr.themes.Soft(primary_hue="blue").set( button_secondary_background_fill="*neutral_100", button_secondary_background_fill_hover="*neutral_200") custom_css = '''#disp_image { text-align: center; /* Horizontally center the content */ }''' with gr.Blocks(title=_TITLE, theme=custom_theme, css=custom_css) as demo: with gr.Row(): with gr.Column(scale=1): gr.Markdown('# ' + _TITLE) gr.Markdown(_DESCRIPTION) with gr.Row(variant='panel'): with gr.Column(scale=1): input_image = gr.Image(type='pil', image_mode='RGBA', height=256, label='Input image', tool=None) example_folder = os.path.join(os.path.dirname(__file__), "./example_images") example_fns = [os.path.join(example_folder, example) for example in os.listdir(example_folder)] gr.Examples( examples=example_fns, inputs=[input_image], # outputs=[input_image], cache_examples=False, label='Examples (click one of the images below to start)', examples_per_page=30 ) with gr.Column(scale=1): processed_image = gr.Image(type='pil', label="Processed Image", interactive=False, height=256, tool=None, image_mode='RGB', elem_id="disp_image") processed_image_highres = gr.Image(type='pil', image_mode='RGB', visible=False, tool=None) with gr.Accordion('Advanced options', open=True): with gr.Row(): with gr.Column(): input_processing = gr.CheckboxGroup(['Use SAM to center animal'], label='Input Image Preprocessing', info='untick this, if animal is already centered, e.g. in example images') # with gr.Column(): # output_processing = gr.CheckboxGroup(['Background Removal'], label='Output Image Postprocessing', value=[]) # with gr.Row(): # with gr.Column(): # scale_slider = gr.Slider(1, 5, value=3, step=1, # label='Classifier Free Guidance Scale') # with gr.Column(): # steps_slider = gr.Slider(15, 100, value=50, step=1, # label='Number of Diffusion Inference Steps') # with gr.Row(): # with gr.Column(): # seed = gr.Number(42, label='Seed') # with gr.Column(): # crop_size = gr.Number(192, label='Crop size') # crop_size = 192 run_btn = gr.Button('Reconstruct', variant='primary', interactive=True) with gr.Row(): view_1 = gr.Image(label="Input View Reconstruction", interactive=False, height=256, show_label=True) view_2 = gr.Image(label="Input View Reconstruction with Skeleton", interactive=False, height=256, show_label=True) with gr.Row(): shape_1 = gr.Model3D(clear_color=[0.0, 0.0, 0.0, 0.0], height=512, label="Reconstructed Shape") shape_2 = gr.Model3D(clear_color=[0.0, 0.0, 0.0, 0.0], height=512, label="Reconstructed Base Shape") #shape_1_download = gr.File(label="Download Full Reconstructed Model") with gr.Row(): # shape_2 = gr.Model3D(clear_color=[0.0, 0.0, 0.0, 0.0], height=512, label="Bank Base Shape Model") shape_1_download = gr.File(label="Download Reconstructed Shape") shape_2_download = gr.File(label="Download Reconstructed Base Shape") run_btn.click(fn=partial(preprocess, predictor), inputs=[input_image, input_processing], outputs=[processed_image_highres, processed_image], queue=True ).success(fn=partial(run_pipeline, model_items, model_cfgs), inputs=[processed_image], outputs=[view_1, view_2, shape_1, shape_1_download, shape_2, shape_2_download] ) demo.queue().launch(share=True, max_threads=80) # _, local_url, share_url = demo.queue().launch(share=True, server_name="0.0.0.0", server_port=23425) # print('local_url: ', local_url) if __name__ == '__main__': fire.Fire(run_demo)