diff --git a/.gitattributes b/.gitattributes index 21d222985022f0ce7ba8ba7b12dcc617df7a153f..ba80df21a68781fb307145eae1ff53af6e29a257 100644 --- a/.gitattributes +++ b/.gitattributes @@ -35,3 +35,65 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text *tfevents* filter=lfs diff=lfs merge=lfs -text wheels/diff_gaussian_rasterization-0.0.0-cp310-cp310-linux_x86_64.whl filter=lfs diff=lfs merge=lfs -text wheels/nvdiffrast-0.3.3-cp310-cp310-linux_x86_64.whl filter=lfs diff=lfs merge=lfs -text +assets/example_image/T.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_building_building.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_building_castle.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_building_colorful_cottage.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_building_maya_pyramid.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_building_mushroom.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_building_space_station.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_creature_dragon.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_creature_elephant.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_creature_furry.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_creature_quadruped.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_creature_robot_crab.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_creature_robot_dinosour.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_creature_rock_monster.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_humanoid_block_robot.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_humanoid_dragonborn.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_humanoid_dwarf.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_humanoid_goblin.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_humanoid_mech.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_misc_crate.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_misc_fireplace.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_misc_gate.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_misc_lantern.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_misc_magicbook.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_misc_mailbox.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_misc_monster_chest.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_misc_paper_machine.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_misc_phonograph.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_misc_portal2.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_misc_storage_chest.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_misc_telephone.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_misc_television.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_misc_workbench.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_vehicle_biplane.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_vehicle_bulldozer.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_vehicle_cart.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_vehicle_excavator.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_vehicle_helicopter.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_vehicle_locomotive.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/typical_vehicle_pirate_ship.png filter=lfs diff=lfs merge=lfs -text +assets/example_image/weatherworn_misc_paper_machine3.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/character_1.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/character_2.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/character_3.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/mushroom_1.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/mushroom_2.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/mushroom_3.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/orangeguy_1.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/orangeguy_2.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/orangeguy_3.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/popmart_1.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/popmart_2.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/popmart_3.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/rabbit_1.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/rabbit_2.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/rabbit_3.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/tiger_1.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/tiger_2.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/tiger_3.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/yoimiya_1.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/yoimiya_2.png filter=lfs diff=lfs merge=lfs -text +assets/example_multi_image/yoimiya_3.png filter=lfs diff=lfs merge=lfs -text diff --git a/app.py b/app.py index 0ef9be3f8c37edd918e81ffdbf8904287ecd8fde..6c33db66c458a29c2786261ba77778cb1d43d840 100644 --- a/app.py +++ b/app.py @@ -1,9 +1,8 @@ import gradio as gr -from gradio_litmodel3d import LitModel3D import spaces +from gradio_litmodel3d import LitModel3D import os - import shutil os.environ['SPCONV_ALGO'] = 'native' from typing import * @@ -12,97 +11,111 @@ import numpy as np import imageio from easydict import EasyDict as edict from PIL import Image -from Amodal3R.pipelines import Amodal3RImageTo3DPipeline from trellis.pipelines import TrellisImageTo3DPipeline -from Amodal3R.representations import Gaussian, MeshExtractResult -from Amodal3R.utils import render_utils, postprocessing_utils -from segment_anything import sam_model_registry, SamPredictor -from huggingface_hub import hf_hub_download -import cv2 +from trellis.representations import Gaussian, MeshExtractResult +from trellis.utils import render_utils, postprocessing_utils MAX_SEED = np.iinfo(np.int32).max TMP_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tmp') os.makedirs(TMP_DIR, exist_ok=True) + def start_session(req: gr.Request): user_dir = os.path.join(TMP_DIR, str(req.session_hash)) os.makedirs(user_dir, exist_ok=True) - + + def end_session(req: gr.Request): user_dir = os.path.join(TMP_DIR, str(req.session_hash)) shutil.rmtree(user_dir) -def reset_image(predictor, img): + +def preprocess_image(image: Image.Image) -> Image.Image: """ - 上传图像后调用: - - 重置 predictor, - - 设置 predictor 的输入图像, - - 返回原图 + Preprocess the input image. + Args: + image (Image.Image): The input image. + Returns: + Image.Image: The preprocessed image. """ - predictor.set_image(img) - original_img = img.copy() - # 返回predictor,visible occlusion mask初始化, 原始图像 - return predictor, original_img, "The models are ready." - -def button_clickable(selected_points): - if len(selected_points) > 0: - return gr.Button.update(interactive=True) - else: - return gr.Button.update(interactive=False) + processed_image = pipeline.preprocess_image(image) + return processed_image + -def run_sam(predictor, selected_points): +def preprocess_images(images: List[Tuple[Image.Image, str]]) -> List[Image.Image]: """ - 调用 SAM 模型进行分割。 + Preprocess a list of input images. + + Args: + images (List[Tuple[Image.Image, str]]): The input images. + + Returns: + List[Image.Image]: The preprocessed images. """ - # predictor.set_image(image) - if len(selected_points) == 0: - return [], None - input_points = [p for p in selected_points] - input_labels = [1 for _ in range(len(selected_points))] - masks, _, _ = predictor.predict( - point_coords=np.array(input_points), - point_labels=np.array(input_labels), - multimask_output=False, # 单对象输出 + images = [image[0] for image in images] + processed_images = [pipeline.preprocess_image(image) for image in images] + return processed_images + + +def pack_state(gs: Gaussian, mesh: MeshExtractResult) -> dict: + return { + 'gaussian': { + **gs.init_params, + '_xyz': gs._xyz.cpu().numpy(), + '_features_dc': gs._features_dc.cpu().numpy(), + '_scaling': gs._scaling.cpu().numpy(), + '_rotation': gs._rotation.cpu().numpy(), + '_opacity': gs._opacity.cpu().numpy(), + }, + 'mesh': { + 'vertices': mesh.vertices.cpu().numpy(), + 'faces': mesh.faces.cpu().numpy(), + }, + } + + +def unpack_state(state: dict) -> Tuple[Gaussian, edict, str]: + gs = Gaussian( + aabb=state['gaussian']['aabb'], + sh_degree=state['gaussian']['sh_degree'], + mininum_kernel_size=state['gaussian']['mininum_kernel_size'], + scaling_bias=state['gaussian']['scaling_bias'], + opacity_bias=state['gaussian']['opacity_bias'], + scaling_activation=state['gaussian']['scaling_activation'], ) - best_mask = masks[0].astype(np.uint8) - # dilate - if len(selected_points) > 1: - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) - best_mask = cv2.dilate(best_mask, kernel, iterations=1) - best_mask = cv2.erode(best_mask, kernel, iterations=1) - return best_mask - -def apply_mask_overlay(image, mask, color=(255, 0, 0)): - """ - 在原图上叠加 mask:使用红色绘制 mask 的轮廓,非 mask 区域叠加浅灰色半透明遮罩。 - """ - img_arr = image - overlay = img_arr.copy() - gray_color = np.array([200, 200, 200], dtype=np.uint8) - non_mask = mask == 0 - overlay[non_mask] = (0.5 * overlay[non_mask] + 0.5 * gray_color).astype(np.uint8) - contours, _ = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) - cv2.drawContours(overlay, contours, -1, color, 2) - return overlay - -def segment_and_overlay(image, points, sam_predictor): + gs._xyz = torch.tensor(state['gaussian']['_xyz'], device='cuda') + gs._features_dc = torch.tensor(state['gaussian']['_features_dc'], device='cuda') + gs._scaling = torch.tensor(state['gaussian']['_scaling'], device='cuda') + gs._rotation = torch.tensor(state['gaussian']['_rotation'], device='cuda') + gs._opacity = torch.tensor(state['gaussian']['_opacity'], device='cuda') + + mesh = edict( + vertices=torch.tensor(state['mesh']['vertices'], device='cuda'), + faces=torch.tensor(state['mesh']['faces'], device='cuda'), + ) + + return gs, mesh + + +def get_seed(randomize_seed: bool, seed: int) -> int: """ - 调用 run_sam 获得 mask,然后叠加显示分割结果。 + Get the random seed. """ - visible_mask = run_sam(sam_predictor, points) - overlaid = apply_mask_overlay(image, visible_mask * 255) - return overlaid, visible_mask + return np.random.randint(0, MAX_SEED) if randomize_seed else seed @spaces.GPU def image_to_3d( - images: np.ndarray, + image: Image.Image, + multiimages: List[Tuple[Image.Image, str]], + is_multiimage: bool, seed: int, ss_guidance_strength: float, ss_sampling_steps: int, slat_guidance_strength: float, slat_sampling_steps: int, + multiimage_algo: Literal["multidiffusion", "stochastic"], req: gr.Request, ) -> Tuple[dict, str]: """ @@ -122,21 +135,37 @@ def image_to_3d( str: The path to the video of the 3D model. """ user_dir = os.path.join(TMP_DIR, str(req.session_hash)) - outputs = pipeline.run_multi_image( - [Image.fromarray(images)], - seed=seed, - formats=["gaussian", "mesh"], - preprocess_image=False, - sparse_structure_sampler_params={ - "steps": ss_sampling_steps, - "cfg_strength": ss_guidance_strength, - }, - slat_sampler_params={ - "steps": slat_sampling_steps, - "cfg_strength": slat_guidance_strength, - }, - mode="stochastic", - ) + if not is_multiimage: + outputs = pipeline.run( + image, + seed=seed, + formats=["gaussian", "mesh"], + preprocess_image=False, + sparse_structure_sampler_params={ + "steps": ss_sampling_steps, + "cfg_strength": ss_guidance_strength, + }, + slat_sampler_params={ + "steps": slat_sampling_steps, + "cfg_strength": slat_guidance_strength, + }, + ) + else: + outputs = pipeline.run_multi_image( + [image[0] for image in multiimages], + seed=seed, + formats=["gaussian", "mesh"], + preprocess_image=False, + sparse_structure_sampler_params={ + "steps": ss_sampling_steps, + "cfg_strength": ss_guidance_strength, + }, + slat_sampler_params={ + "steps": slat_sampling_steps, + "cfg_strength": slat_guidance_strength, + }, + mode=multiimage_algo, + ) video = render_utils.render_video(outputs['gaussian'][0], num_frames=120)['color'] video_geo = render_utils.render_video(outputs['mesh'][0], num_frames=120)['normal'] video = [np.concatenate([video[i], video_geo[i]], axis=1) for i in range(len(video))] @@ -153,9 +182,15 @@ def extract_glb( mesh_simplify: float, texture_size: int, req: gr.Request, -) -> tuple: +) -> Tuple[str, str]: """ - 从生成的 3D 模型中提取 GLB 文件。 + Extract a GLB file from the 3D model. + Args: + state (dict): The state of the generated 3D model. + mesh_simplify (float): The mesh simplification factor. + texture_size (int): The texture resolution. + Returns: + str: The path to the extracted GLB file. """ user_dir = os.path.join(TMP_DIR, str(req.session_hash)) gs, mesh = unpack_state(state) @@ -167,9 +202,13 @@ def extract_glb( @spaces.GPU -def extract_gaussian(state: dict, req: gr.Request) -> tuple: +def extract_gaussian(state: dict, req: gr.Request) -> Tuple[str, str]: """ - 从生成的 3D 模型中提取 Gaussian 文件。 + Extract a Gaussian file from the 3D model. + Args: + state (dict): The state of the generated 3D model. + Returns: + str: The path to the extracted Gaussian file. """ user_dir = os.path.join(TMP_DIR, str(req.session_hash)) gs, _ = unpack_state(state) @@ -179,273 +218,59 @@ def extract_gaussian(state: dict, req: gr.Request) -> tuple: return gaussian_path, gaussian_path -def pack_state(gs: Gaussian, mesh: MeshExtractResult) -> dict: - return { - 'gaussian': { - **gs.init_params, - '_xyz': gs._xyz.cpu().numpy(), - '_features_dc': gs._features_dc.cpu().numpy(), - '_scaling': gs._scaling.cpu().numpy(), - '_rotation': gs._rotation.cpu().numpy(), - '_opacity': gs._opacity.cpu().numpy(), - }, - 'mesh': { - 'vertices': mesh.vertices.cpu().numpy(), - 'faces': mesh.faces.cpu().numpy(), - }, - } - - -def unpack_state(state: dict) -> tuple: - gs = Gaussian( - aabb=state['gaussian']['aabb'], - sh_degree=state['gaussian']['sh_degree'], - mininum_kernel_size=state['gaussian']['mininum_kernel_size'], - scaling_bias=state['gaussian']['scaling_bias'], - opacity_bias=state['gaussian']['opacity_bias'], - scaling_activation=state['gaussian']['scaling_activation'], - ) - gs._xyz = torch.tensor(state['gaussian']['_xyz'], device='cuda') - gs._features_dc = torch.tensor(state['gaussian']['_features_dc'], device='cuda') - gs._scaling = torch.tensor(state['gaussian']['_scaling'], device='cuda') - gs._rotation = torch.tensor(state['gaussian']['_rotation'], device='cuda') - gs._opacity = torch.tensor(state['gaussian']['_opacity'], device='cuda') - - mesh = edict( - vertices=torch.tensor(state['mesh']['vertices'], device='cuda'), - faces=torch.tensor(state['mesh']['faces'], device='cuda'), - ) - - return gs, mesh - -def get_sam_predictor(): - # sam_checkpoint = hf_hub_download("ybelkada/segment-anything", "checkpoints/sam_vit_h_4b8939.pth") - # model_type = "vit_h" - # sam = sam_model_registry[model_type](checkpoint=sam_checkpoint) - # sam_predictor = SamPredictor(sam) - # return sam_predictor - return predictor - - -def draw_points_on_image(image, point): - """在图像上绘制所有点,points 为 [(x, y, point_type), ...]""" - image_with_points = image.copy() - x, y = point - color = (255, 0, 0) - cv2.circle(image_with_points, (int(x), int(y)), radius=10, color=color, thickness=-1) - return image_with_points - - -def see_point(image, x, y): - """ - see操作:不修改 points 列表,仅在图像上临时显示这个点, - 并返回更新后的图像和当前列表(不更新)。 - """ - # 复制当前列表,并在副本中加上新点(仅用于显示) - updated_image = draw_points_on_image(image, [x,y]) - return updated_image - -def add_point(x, y, visible_points): - """ - add操作:将新点添加到 points 列表中, - 并返回更新后的图像和新的点列表。 - """ - if [x, y] not in visible_points: - visible_points.append([x, y]) - return visible_points - -def delete_point(visible_points): - """ - delete操作:删除 points 列表中的最后一个点, - 并返回更新后的图像和新的点列表。 - """ - visible_points.pop() - return visible_points - +def prepare_multi_example() -> List[Image.Image]: + multi_case = list(set([i.split('_')[0] for i in os.listdir("assets/example_multi_image")])) + images = [] + for case in multi_case: + _images = [] + for i in range(1, 4): + img = Image.open(f'assets/example_multi_image/{case}_{i}.png') + W, H = img.size + img = img.resize((int(W / H * 512), 512)) + _images.append(np.array(img)) + images.append(Image.fromarray(np.concatenate(_images, axis=1))) + return images -def clear_all_points(image): - """ - 清除所有点:返回原图、空的 visible 和 occlusion 列表, - 以及更新后的点文本信息和空下拉菜单列表。 - """ - updated_image = image.copy() - return updated_image -def see_visible_points(image, visible_points): +def split_image(image: Image.Image) -> List[Image.Image]: """ - 在图像上绘制所有 visible 点(红色)。 + Split an image into multiple views. """ - updated_image = image.copy() - for p in visible_points: - cv2.circle(updated_image, (int(p[0]), int(p[1])), radius=10, color=(255, 0, 0), thickness=-1) - return updated_image - -def update_all_points(visible_points): - text = f"Points: {visible_points}" - visible_dropdown_choices = [f"({p[0]}, {p[1]})" for p in visible_points] - # 返回更新字典来明确设置 choices 和 value - return text, gr.Dropdown(label="Select Point to Delete", choices=visible_dropdown_choices, value=None, interactive=True) - -def delete_selected_visible(image, visible_points, selected_value): - # selected_value 是类似 "(x, y)" 的字符串 - try: - selected_index = [f"({p[0]}, {p[1]})" for p in visible_points].index(selected_value) - except ValueError: - selected_index = None - if selected_index is not None and 0 <= selected_index < len(visible_points): - visible_points.pop(selected_index) - updated_image = image.copy() - # 重新绘制所有 visible 点(红色) - for p in visible_points: - cv2.circle(updated_image, (int(p[0]), int(p[1])), radius=10, color=(255, 0, 0), thickness=-1) - updated_text, vis_dropdown = update_all_points(visible_points) - return updated_image, visible_points, updated_text, vis_dropdown - -def add_mask(mask, mask_list): - # check if the mask if same as the last mask in the list - if len(mask_list) > 0: - if np.array_equal(mask, mask_list[-1]): - return mask_list - mask_list.append(mask) - return mask_list - -def vis_mask(image, mask_list): - updated_image = image.copy() - # combine all the mask: - combined_mask = np.zeros_like(updated_image[:, :, 0]) - for mask in mask_list: - combined_mask = cv2.bitwise_or(combined_mask, mask) - # overlay the mask on the image - updated_image = apply_mask_overlay(updated_image, combined_mask) - return updated_image - -def delete_mask(mask_list): - if len(mask_list) > 0: - mask_list.pop() - return mask_list - -def check_combined_mask(image, visibility_mask, mask_list, scale=0.6): - updated_image = image.copy() - # combine all the mask: - combined_mask = np.zeros_like(updated_image[:, :, 0]) - occluded_mask = np.zeros_like(updated_image[:, :, 0]) - if len(mask_list) == 0: - combined_mask = visibility_mask - else: - for mask in mask_list: - combined_mask = cv2.bitwise_or(combined_mask, mask) - - if len(mask_list) > 1: - kernel = np.ones((5, 5), np.uint8) - dilate_iterations = 1 - combined_mask = cv2.dilate(combined_mask, kernel, iterations=dilate_iterations) - combined_mask = cv2.erode(combined_mask, kernel, iterations=dilate_iterations) - - masked_img = updated_image * combined_mask[:, :, None] - occluded_mask[combined_mask == 1] = 127 - - # move the visible part to the center of the image - x, y, w, h = cv2.boundingRect(combined_mask.astype(np.uint8)) - cropped_occluded_mask = (occluded_mask[y:y+h, x:x+w]).astype(np.uint8) - cropped_img = masked_img[y:y+h, x:x+w] - - target_size = 512 - scale_factor = target_size / max(w, h) - new_w = int(round(w * scale_factor * scale)) - new_h = int(round(h * scale_factor * scale)) - - resized_occluded_mask = cv2.resize(cropped_occluded_mask.astype(np.uint8), (new_w, new_h), cv2.INTER_NEAREST) - resized_img = cv2.resize(cropped_img, (new_w, new_h), cv2.INTER_NEAREST) - - final_img = np.zeros((target_size, target_size, 3), dtype=updated_image.dtype) - final_occluded_mask = np.zeros((target_size, target_size), dtype=np.uint8) - - x_offset = (target_size - new_w) // 2 - y_offset = (target_size - new_h) // 2 - - final_img[y_offset:y_offset+new_h, x_offset:x_offset+new_w] = resized_img - final_occluded_mask[y_offset:y_offset+new_h, x_offset:x_offset+new_w] = resized_occluded_mask - - return final_img, occluded_mask - - - -def get_seed(randomize_seed: bool, seed: int) -> int: - """ - Get the random seed. - """ - return np.random.randint(0, MAX_SEED) if randomize_seed else seed + image = np.array(image) + alpha = image[..., 3] + alpha = np.any(alpha>0, axis=0) + start_pos = np.where(~alpha[:-1] & alpha[1:])[0].tolist() + end_pos = np.where(alpha[:-1] & ~alpha[1:])[0].tolist() + images = [] + for s, e in zip(start_pos, end_pos): + images.append(Image.fromarray(image[:, s:e+1])) + return [preprocess_image(image) for image in images] with gr.Blocks(delete_cache=(600, 600)) as demo: gr.Markdown(""" - ## 3D Amodal Reconstruction with [Amodal3R](https://sm0kywu.github.io/Amodal3R/) + ## Image to 3D Asset with [TRELLIS](https://trellis3d.github.io/) + * Upload an image and click "Generate" to create a 3D asset. If the image has alpha channel, it be used as the mask. Otherwise, we use `rembg` to remove the background. + * If you find the generated 3D asset satisfactory, click "Extract GLB" to extract the GLB file and download it. + + ✨New: 1) Experimental multi-image support. 2) Gaussian file extraction. """) - - # 定义各状态变量 - predictor = gr.State(value=get_sam_predictor()) - visible_points_state = gr.State(value=[]) - occlusion_points_state = gr.State(value=[]) - original_image = gr.State(value=None) - visibility_mask = gr.State(value=None) - visibility_mask_list = gr.State(value=[]) - - occluded_mask = gr.State(value=None) - output_buf = gr.State() - - - with gr.Row(): - gr.Markdown("""* Step 1 - Generate Visibility Mask and Occlusion Mask. - * Please wait for a few seconds after uploading the image. The 2D segmenter is getting ready. - * Add the point prompts to indicate the target object and occluders separately. - * "Render Point", see the position of the point to be added. - * "Add Point", the point will be added to the list. - * "Generate mask", see the segmented area corresponding to current point list. - * "Add mask", current mask will be added for 3D amodal completion. - """) - with gr.Row(): - with gr.Column(): - input_image = gr.Image(type="numpy", label='Input Occlusion Image', sources="upload", height=300) - with gr.Row(): - message = gr.Markdown("Please wait a few seconds after uploading the image.", label="Message") # 用于显示提示信息 - with gr.Row(): - x_input = gr.Number(label="X Coordinate", value=0) - y_input = gr.Number(label="Y Coordinate", value=0) - with gr.Row(): - see_button = gr.Button("Render Point") - add_button = gr.Button("Add Point") - with gr.Row(): - clear_button = gr.Button("Clear Points") - see_visible_button = gr.Button("Render Added Points") - with gr.Row(): - # 新增文本框实时显示点列表 - points_text = gr.Textbox(label="Points List", interactive=False) - with gr.Row(): - # 新增下拉菜单,用户可选择需要删除的点 - visible_points_dropdown = gr.Dropdown(label="Select Point to Delete", choices=[], value=None, interactive=True) - delete_visible_button = gr.Button("Delete Selected Visible") - with gr.Column(): - # 用于显示 SAM 分割结果 - visible_mask = gr.Image(label='Visible Mask', interactive=False, height=300) - with gr.Row(): - gen_vis_mask = gr.Button("Generate Mask") - add_vis_mask = gr.Button("Add Mask") - with gr.Row(): - render_vis_mask = gr.Button("Render Mask") - undo_vis_mask = gr.Button("Undo Last Mask") - vis_input = gr.Image(label='Visible Input', interactive=False, height=300) - with gr.Row(): - zoom_scale = gr.Slider(0.3, 1.0, label="Target Object Scale", value=0.6, step=0.1) - check_visible_input = gr.Button("Generate Occluded Input") - with gr.Row(): - gr.Markdown("""* Step 2 - 3D Amodal Completion. - * Different random seeds can be tried in "Generation Settings", if you think the results are not ideal. - * If the reconstruction 3D asset is satisfactory, you can extract the GLB file and download it. - """) + with gr.Row(): with gr.Column(): - with gr.Accordion(label="Generation Settings", open=True): - seed = gr.Slider(0, MAX_SEED, label="Seed", value=1, step=1) + with gr.Tabs() as input_tabs: + with gr.Tab(label="Single Image", id=0) as single_image_input_tab: + image_prompt = gr.Image(label="Image Prompt", format="png", image_mode="RGBA", type="pil", height=300) + with gr.Tab(label="Multiple Images", id=1) as multiimage_input_tab: + multiimage_prompt = gr.Gallery(label="Image Prompt", format="png", type="pil", height=300, columns=3) + gr.Markdown(""" + Input different views of the object in separate images. + + *NOTE: this is an experimental algorithm without training a specialized model. It may not produce the best results for all images, especially those having different poses or inconsistent details.* + """) + + with gr.Accordion(label="Generation Settings", open=False): + seed = gr.Slider(0, MAX_SEED, label="Seed", value=0, step=1) randomize_seed = gr.Checkbox(label="Randomize Seed", value=True) gr.Markdown("Stage 1: Sparse Structure Generation") with gr.Row(): @@ -455,114 +280,127 @@ with gr.Blocks(delete_cache=(600, 600)) as demo: with gr.Row(): slat_guidance_strength = gr.Slider(0.0, 10.0, label="Guidance Strength", value=3.0, step=0.1) slat_sampling_steps = gr.Slider(1, 50, label="Sampling Steps", value=12, step=1) + multiimage_algo = gr.Radio(["stochastic", "multidiffusion"], label="Multi-image Algorithm", value="stochastic") + generate_btn = gr.Button("Generate") + + with gr.Accordion(label="GLB Extraction Settings", open=False): + mesh_simplify = gr.Slider(0.9, 0.98, label="Simplify", value=0.95, step=0.01) + texture_size = gr.Slider(512, 2048, label="Texture Size", value=1024, step=512) + + with gr.Row(): + extract_glb_btn = gr.Button("Extract GLB", interactive=False) + extract_gs_btn = gr.Button("Extract Gaussian", interactive=False) + gr.Markdown(""" + *NOTE: Gaussian file can be very large (~50MB), it will take a while to display and download.* + """) + with gr.Column(): video_output = gr.Video(label="Generated 3D Asset", autoplay=True, loop=True, height=300) + model_output = LitModel3D(label="Extracted GLB/Gaussian", exposure=10.0, height=300) + + with gr.Row(): + download_glb = gr.DownloadButton(label="Download GLB", interactive=False) + download_gs = gr.DownloadButton(label="Download Gaussian", interactive=False) - # # Handlers + is_multiimage = gr.State(False) + output_buf = gr.State() + + # Example images at the bottom of the page + with gr.Row() as single_image_example: + examples = gr.Examples( + examples=[ + f'assets/example_image/{image}' + for image in os.listdir("assets/example_image") + ], + inputs=[image_prompt], + fn=preprocess_image, + outputs=[image_prompt], + run_on_click=True, + examples_per_page=64, + ) + with gr.Row(visible=False) as multiimage_example: + examples_multi = gr.Examples( + examples=prepare_multi_example(), + inputs=[image_prompt], + fn=split_image, + outputs=[multiimage_prompt], + run_on_click=True, + examples_per_page=8, + ) + + # Handlers demo.load(start_session) demo.unload(end_session) - - # --------------------------- - # 原有交互逻辑(略) - # --------------------------- - input_image.upload( - reset_image, - [predictor, input_image], - [predictor, original_image, message], - ) - see_button.click( - see_point, - inputs=[original_image, x_input, y_input], - outputs=[input_image] - ) - add_button.click( - add_point, - inputs=[x_input, y_input, visible_points_state], - outputs=[visible_points_state] - ) - # --------------------------- - # 新增的交互逻辑 - # --------------------------- - clear_button.click( - clear_all_points, - inputs=[original_image], - outputs=[input_image] + single_image_input_tab.select( + lambda: tuple([False, gr.Row.update(visible=True), gr.Row.update(visible=False)]), + outputs=[is_multiimage, single_image_example, multiimage_example] ) - see_visible_button.click( - see_visible_points, - inputs=[input_image, visible_points_state], - outputs=input_image + multiimage_input_tab.select( + lambda: tuple([True, gr.Row.update(visible=False), gr.Row.update(visible=True)]), + outputs=[is_multiimage, single_image_example, multiimage_example] ) - # 当 visible_points_state 或 occlusion_points_state 变化时,更新文本框和下拉菜单 - visible_points_state.change( - update_all_points, - inputs=[visible_points_state], - outputs=[points_text, visible_points_dropdown] + + image_prompt.upload( + preprocess_image, + inputs=[image_prompt], + outputs=[image_prompt], ) - delete_visible_button.click( - delete_selected_visible, - inputs=[input_image, visible_points_state, visible_points_dropdown], - outputs=[input_image, visible_points_state, points_text, visible_points_dropdown] + multiimage_prompt.upload( + preprocess_images, + inputs=[multiimage_prompt], + outputs=[multiimage_prompt], ) - # 生成mask的逻辑 - gen_vis_mask.click( - segment_and_overlay, - inputs=[original_image, visible_points_state, predictor], - outputs=[visible_mask, visibility_mask] - ) - add_vis_mask.click( - add_mask, - inputs=[visibility_mask, visibility_mask_list], - outputs=[visibility_mask_list] - ) - render_vis_mask.click( - vis_mask, - inputs=[original_image, visibility_mask_list], - outputs=[visible_mask] + generate_btn.click( + get_seed, + inputs=[randomize_seed, seed], + outputs=[seed], + ).then( + image_to_3d, + inputs=[image_prompt, multiimage_prompt, is_multiimage, seed, ss_guidance_strength, ss_sampling_steps, slat_guidance_strength, slat_sampling_steps, multiimage_algo], + outputs=[output_buf, video_output], + ).then( + lambda: tuple([gr.Button(interactive=True), gr.Button(interactive=True)]), + outputs=[extract_glb_btn, extract_gs_btn], ) - undo_vis_mask.click( - delete_mask, - inputs=[visibility_mask_list], - outputs=[visibility_mask_list] + + video_output.clear( + lambda: tuple([gr.Button(interactive=False), gr.Button(interactive=False)]), + outputs=[extract_glb_btn, extract_gs_btn], ) - check_visible_input.click( - check_combined_mask, - inputs=[original_image, visibility_mask, visibility_mask_list, zoom_scale], - outputs=[vis_input, occluded_mask] + extract_glb_btn.click( + extract_glb, + inputs=[output_buf, mesh_simplify, texture_size], + outputs=[model_output, download_glb], + ).then( + lambda: gr.Button(interactive=True), + outputs=[download_glb], ) + extract_gs_btn.click( + extract_gaussian, + inputs=[output_buf], + outputs=[model_output, download_gs], + ).then( + lambda: gr.Button(interactive=True), + outputs=[download_gs], + ) - # 3D Amodal Reconstruction - # generate_btn.click( - # get_seed, - # inputs=[randomize_seed, seed], - # outputs=[seed], - # ).then( - # image_to_3d, - # inputs=[vis_input, occluded_mask, seed, ss_guidance_strength, ss_sampling_steps, slat_guidance_strength, slat_sampling_steps], - # outputs=[output_buf, video_output], - # ) - - generate_btn.click( - image_to_3d, - inputs=[vis_input, seed, ss_guidance_strength, ss_sampling_steps, slat_guidance_strength, slat_sampling_steps], - outputs=[output_buf, video_output], + model_output.clear( + lambda: gr.Button(interactive=False), + outputs=[download_glb], ) -# 启动 Gradio App +# Launch the Gradio app if __name__ == "__main__": - # pipeline = Amodal3RImageTo3DPipeline.from_pretrained("Sm0kyWu/Amodal3R") pipeline = TrellisImageTo3DPipeline.from_pretrained("JeffreyXiang/TRELLIS-image-large") pipeline.cuda() - predictor = get_sam_predictor() - predictor = predictor.cuda() try: - pipeline.preprocess_image(Image.fromarray(np.zeros((512, 512, 3), dtype=np.uint8))) + pipeline.preprocess_image(Image.fromarray(np.zeros((512, 512, 3), dtype=np.uint8))) # Preload rembg except: pass - demo.launch() \ No newline at end of file + demo.launch() diff --git a/assets/example_image/T.png b/assets/example_image/T.png new file mode 100644 index 0000000000000000000000000000000000000000..861ee434cb123a74d50b843631db47d0646675ed --- /dev/null +++ b/assets/example_image/T.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e29ddc83a5bd3a05fe9b34732169bc4ea7131f7c36527fdc5f626a90a73076d2 +size 954642 diff --git a/assets/example_image/typical_building_building.png b/assets/example_image/typical_building_building.png new file mode 100644 index 0000000000000000000000000000000000000000..515be4e5bb423b92933f1dc11438b570c5f4db95 --- /dev/null +++ b/assets/example_image/typical_building_building.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8faa11d557be95c000c475247e61a773d511114c7d1e517c04f8d3d88a6049ec +size 546948 diff --git a/assets/example_image/typical_building_castle.png b/assets/example_image/typical_building_castle.png new file mode 100644 index 0000000000000000000000000000000000000000..8b4f705e8f3e37eb96cf948eb822b193e47c3bf8 --- /dev/null +++ b/assets/example_image/typical_building_castle.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:076f0554b087b921863643d2b1ab3e0572a13a347fd66bc29cd9d194034affae +size 426358 diff --git a/assets/example_image/typical_building_colorful_cottage.png b/assets/example_image/typical_building_colorful_cottage.png new file mode 100644 index 0000000000000000000000000000000000000000..d9f451150d723f39a402590c520702e4e7fd8e44 --- /dev/null +++ b/assets/example_image/typical_building_colorful_cottage.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:687305b4e35da759692be0de614d728583a2a9cd2fd3a55593fa753e567d0d47 +size 609383 diff --git a/assets/example_image/typical_building_maya_pyramid.png b/assets/example_image/typical_building_maya_pyramid.png new file mode 100644 index 0000000000000000000000000000000000000000..d5db764b32e08a93ac098c06aac9329dba743ea9 --- /dev/null +++ b/assets/example_image/typical_building_maya_pyramid.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4d514f7f4db244ee184af4ddfbc5948d417b4e5bf1c6ee5f5a592679561690df +size 231664 diff --git a/assets/example_image/typical_building_mushroom.png b/assets/example_image/typical_building_mushroom.png new file mode 100644 index 0000000000000000000000000000000000000000..fcc169f1f0eef1725af0ba60b429a5d6c550dfa5 --- /dev/null +++ b/assets/example_image/typical_building_mushroom.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:de9b72d3e13e967e70844ddc54643832a84a1b35ca043a11e7c774371d0ccdab +size 488032 diff --git a/assets/example_image/typical_building_space_station.png b/assets/example_image/typical_building_space_station.png new file mode 100644 index 0000000000000000000000000000000000000000..bffc6286658add1b747af63e29d42c2735ed37a3 --- /dev/null +++ b/assets/example_image/typical_building_space_station.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:212c7b4c27ba1e01a7908dbc7f245e7115850eadbc9974aa726327cf35062846 +size 619626 diff --git a/assets/example_image/typical_creature_dragon.png b/assets/example_image/typical_creature_dragon.png new file mode 100644 index 0000000000000000000000000000000000000000..d62c7e18b52a7f1ac603c143abfc092b321e2734 --- /dev/null +++ b/assets/example_image/typical_creature_dragon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0e8d6720dfa1e7b332b76e897e617b7f0863187f30879451b4724f482c84185a +size 563963 diff --git a/assets/example_image/typical_creature_elephant.png b/assets/example_image/typical_creature_elephant.png new file mode 100644 index 0000000000000000000000000000000000000000..d4f189675bc888238896eab93f4aefa6e2e32a9b --- /dev/null +++ b/assets/example_image/typical_creature_elephant.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:86a171e37a3d781e7215977f565cd63e813341c1f89e2c586fa61937e4ed6916 +size 481919 diff --git a/assets/example_image/typical_creature_furry.png b/assets/example_image/typical_creature_furry.png new file mode 100644 index 0000000000000000000000000000000000000000..58033811f29f4757759eb66a351bbb82228c0f07 --- /dev/null +++ b/assets/example_image/typical_creature_furry.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b5445b8f1996cf6d72497b2d7564c656f4048e6c1fa626fd7bb3ee582fee671 +size 647568 diff --git a/assets/example_image/typical_creature_quadruped.png b/assets/example_image/typical_creature_quadruped.png new file mode 100644 index 0000000000000000000000000000000000000000..503ff7d93aed42ef8b8ccaa741559007f4627e7e --- /dev/null +++ b/assets/example_image/typical_creature_quadruped.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7469f43f58389adec101e9685f60188bd4e7fbede77eef975102f6a8865bc786 +size 685321 diff --git a/assets/example_image/typical_creature_robot_crab.png b/assets/example_image/typical_creature_robot_crab.png new file mode 100644 index 0000000000000000000000000000000000000000..1546f322acc31caeb524a518979afbffe8de197f --- /dev/null +++ b/assets/example_image/typical_creature_robot_crab.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d7e716abe8f8895080f562d1dc26b14fa0e20a05aa5beb2770c6fb3b87b3476a +size 594232 diff --git a/assets/example_image/typical_creature_robot_dinosour.png b/assets/example_image/typical_creature_robot_dinosour.png new file mode 100644 index 0000000000000000000000000000000000000000..b8a802f1f64424db980dc09e35c7be98e526d9dd --- /dev/null +++ b/assets/example_image/typical_creature_robot_dinosour.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d0986f29557a6fddf9b52b5251a6b6103728c61e201b1cfad1e709b090b72f56 +size 632292 diff --git a/assets/example_image/typical_creature_rock_monster.png b/assets/example_image/typical_creature_rock_monster.png new file mode 100644 index 0000000000000000000000000000000000000000..8a6987064fddd7162ff55bd9a98c47c32a6b2397 --- /dev/null +++ b/assets/example_image/typical_creature_rock_monster.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e29458a6110bee8374c0d4d12471e7167a6c1c98c18f6e2d7ff4f5f0ca3fa01b +size 648344 diff --git a/assets/example_image/typical_humanoid_block_robot.png b/assets/example_image/typical_humanoid_block_robot.png new file mode 100644 index 0000000000000000000000000000000000000000..d509f6d257ff3851eae6f12820ec2cf605bec85f --- /dev/null +++ b/assets/example_image/typical_humanoid_block_robot.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3a0acbb532668e1bf35f3eef5bcbfdd094c22219ef2d837fa01ccf51cce75ca3 +size 441407 diff --git a/assets/example_image/typical_humanoid_dragonborn.png b/assets/example_image/typical_humanoid_dragonborn.png new file mode 100644 index 0000000000000000000000000000000000000000..88f2d9070bb76eb31cd2b9e3c9677f70998e5d3b --- /dev/null +++ b/assets/example_image/typical_humanoid_dragonborn.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5d7c547909a6c12da55dbab1c1c98181ff09e58c9ba943682ca105e71be9548e +size 481371 diff --git a/assets/example_image/typical_humanoid_dwarf.png b/assets/example_image/typical_humanoid_dwarf.png new file mode 100644 index 0000000000000000000000000000000000000000..6bcc3945be7038c12c61d72950bab8bcb8475f10 --- /dev/null +++ b/assets/example_image/typical_humanoid_dwarf.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a4a7c157d5d8071128c27594e45a7a03e5113b3333b7f1c5ff1379481e3e0264 +size 498425 diff --git a/assets/example_image/typical_humanoid_goblin.png b/assets/example_image/typical_humanoid_goblin.png new file mode 100644 index 0000000000000000000000000000000000000000..6f4a8142d9f452b233b6d5b0bd0d6dac5e41f94e --- /dev/null +++ b/assets/example_image/typical_humanoid_goblin.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2b0e9a04ae3e7bef44b7180a70306f95374b60727ffa0f6f01fd6c746595cd77 +size 496430 diff --git a/assets/example_image/typical_humanoid_mech.png b/assets/example_image/typical_humanoid_mech.png new file mode 100644 index 0000000000000000000000000000000000000000..e0e07443d760b405979253c85bd28747d686976c --- /dev/null +++ b/assets/example_image/typical_humanoid_mech.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a244ec54b7984e646e54d433de6897657081dd5b9cd5ccd3d865328d813beb49 +size 849947 diff --git a/assets/example_image/typical_misc_crate.png b/assets/example_image/typical_misc_crate.png new file mode 100644 index 0000000000000000000000000000000000000000..b66a57feffa7117f1f3c2d614d24001fb2611467 --- /dev/null +++ b/assets/example_image/typical_misc_crate.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59fd9884301faca93265166d90078e8c31e76c7f93524b1db31975df4b450748 +size 642105 diff --git a/assets/example_image/typical_misc_fireplace.png b/assets/example_image/typical_misc_fireplace.png new file mode 100644 index 0000000000000000000000000000000000000000..c8f352fbb6b256d56d43febb052b91943b6d4e3d --- /dev/null +++ b/assets/example_image/typical_misc_fireplace.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2288c034603e289192d63cbc73565107caefd99e81c4b7afa2983c8b13e34440 +size 558466 diff --git a/assets/example_image/typical_misc_gate.png b/assets/example_image/typical_misc_gate.png new file mode 100644 index 0000000000000000000000000000000000000000..58f96079232d47f7615a491b95d2fc7cd635ef1d --- /dev/null +++ b/assets/example_image/typical_misc_gate.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec8db5389b74fe56b826e3c6d860234541033387350e09268591c46d411cc8e9 +size 572414 diff --git a/assets/example_image/typical_misc_lantern.png b/assets/example_image/typical_misc_lantern.png new file mode 100644 index 0000000000000000000000000000000000000000..0917aa4be24bec12ba961475efd5bef1c6f8fd2a --- /dev/null +++ b/assets/example_image/typical_misc_lantern.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e17bd83adf433ebfca17abd220097b2b7f08affc649518bd7822e03797e83d41 +size 300270 diff --git a/assets/example_image/typical_misc_magicbook.png b/assets/example_image/typical_misc_magicbook.png new file mode 100644 index 0000000000000000000000000000000000000000..f268ca7e37851578d888cb26f8057846f9e12e78 --- /dev/null +++ b/assets/example_image/typical_misc_magicbook.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aff9c14589c340e31b61bf82e4506d77d72c511e741260fa1e600cefa4e103e6 +size 496050 diff --git a/assets/example_image/typical_misc_mailbox.png b/assets/example_image/typical_misc_mailbox.png new file mode 100644 index 0000000000000000000000000000000000000000..31a5b45556a0da3b7e36301230c7ffa1f15adad1 --- /dev/null +++ b/assets/example_image/typical_misc_mailbox.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:01e86a5d68edafb7e11d7a86f7e8081f5ed1b02578198a3271554c5fb8fb9fcf +size 631146 diff --git a/assets/example_image/typical_misc_monster_chest.png b/assets/example_image/typical_misc_monster_chest.png new file mode 100644 index 0000000000000000000000000000000000000000..660a8a9bbec77a967b0263b98900411d69588d71 --- /dev/null +++ b/assets/example_image/typical_misc_monster_chest.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c57a598e842225a31b9770bf3bbb9ae86197ec57d0c2883caf8cb5eed4908fbc +size 690259 diff --git a/assets/example_image/typical_misc_paper_machine.png b/assets/example_image/typical_misc_paper_machine.png new file mode 100644 index 0000000000000000000000000000000000000000..db27c6c49446d85e081133a195be4aff90e66f33 --- /dev/null +++ b/assets/example_image/typical_misc_paper_machine.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2d55400ae5d4df2377258400d800ece75766d5274e80ce07c3b29a4d1fd1fa36 +size 613984 diff --git a/assets/example_image/typical_misc_phonograph.png b/assets/example_image/typical_misc_phonograph.png new file mode 100644 index 0000000000000000000000000000000000000000..d2f4c143fa51c16c4a3ab4a0a625319d95d4a8be --- /dev/null +++ b/assets/example_image/typical_misc_phonograph.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:14fff9a27ea769d3ca711e9ff55ab3d9385486a5e8b99117f506df326a0a357e +size 517193 diff --git a/assets/example_image/typical_misc_portal2.png b/assets/example_image/typical_misc_portal2.png new file mode 100644 index 0000000000000000000000000000000000000000..6ab0c8b0784eaa370d566854974a7b4f1e0f4ff7 --- /dev/null +++ b/assets/example_image/typical_misc_portal2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:57aab2bba56bc946523a3fca77ca70651a4ad8c6fbf1b91a1a824418df48faae +size 386106 diff --git a/assets/example_image/typical_misc_storage_chest.png b/assets/example_image/typical_misc_storage_chest.png new file mode 100644 index 0000000000000000000000000000000000000000..2fcd82551d835903a914c8737b5650373776d363 --- /dev/null +++ b/assets/example_image/typical_misc_storage_chest.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0e4ac1c67fdda902ecb709447b8defd949c738954c844c1b8364b8e3f7d9e55a +size 632296 diff --git a/assets/example_image/typical_misc_telephone.png b/assets/example_image/typical_misc_telephone.png new file mode 100644 index 0000000000000000000000000000000000000000..58e7a3a434274b58ef0ad567a19ec46e16bd4ba4 --- /dev/null +++ b/assets/example_image/typical_misc_telephone.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:00048be46234a2709c12614b04cbad61c6e3c7e63c2a4ef33d999185f5393e36 +size 647557 diff --git a/assets/example_image/typical_misc_television.png b/assets/example_image/typical_misc_television.png new file mode 100644 index 0000000000000000000000000000000000000000..79042fd48c953ccabc2f41333dfa12942ebbb5cd --- /dev/null +++ b/assets/example_image/typical_misc_television.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6a1947b737398bf535ec212668a4d78cd38fe84cf9da1ccd6c0c0d838337755e +size 627348 diff --git a/assets/example_image/typical_misc_workbench.png b/assets/example_image/typical_misc_workbench.png new file mode 100644 index 0000000000000000000000000000000000000000..c964138ac20d2378c9ba5ecd35eca70f1c728ed2 --- /dev/null +++ b/assets/example_image/typical_misc_workbench.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6d9ed4d005a5253b8571fd976b0d102e293512d7b5a8ed5e3f7f17c5f4e19da +size 462693 diff --git a/assets/example_image/typical_vehicle_biplane.png b/assets/example_image/typical_vehicle_biplane.png new file mode 100644 index 0000000000000000000000000000000000000000..4bca4e16ec6453e008d21b5a82dd5667a6ef453a --- /dev/null +++ b/assets/example_image/typical_vehicle_biplane.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c73e98112eb603b4ba635b8965cad7807d0588f083811bc2faa0c7ab9668a65a +size 574448 diff --git a/assets/example_image/typical_vehicle_bulldozer.png b/assets/example_image/typical_vehicle_bulldozer.png new file mode 100644 index 0000000000000000000000000000000000000000..9fa27c21bb6beca459d19d2cbd536f0a41fdce0d --- /dev/null +++ b/assets/example_image/typical_vehicle_bulldozer.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:23d821b4daea61cbea28cc6ddd3ae46712514dfcdff995c2664f5a70d21f4ef3 +size 692767 diff --git a/assets/example_image/typical_vehicle_cart.png b/assets/example_image/typical_vehicle_cart.png new file mode 100644 index 0000000000000000000000000000000000000000..d8848f3473355f2040c67a803847947e086dc969 --- /dev/null +++ b/assets/example_image/typical_vehicle_cart.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b72c04a2aa5cf57717c05151a2982d6dc31afde130d5e830adf37a84a70616cb +size 693145 diff --git a/assets/example_image/typical_vehicle_excavator.png b/assets/example_image/typical_vehicle_excavator.png new file mode 100644 index 0000000000000000000000000000000000000000..3dddee1020e052155d2e8f404d982f45786c5d07 --- /dev/null +++ b/assets/example_image/typical_vehicle_excavator.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27a418853eefa197f1e10ed944a7bb071413fd2bc1681804ee773a6ce3799c52 +size 712062 diff --git a/assets/example_image/typical_vehicle_helicopter.png b/assets/example_image/typical_vehicle_helicopter.png new file mode 100644 index 0000000000000000000000000000000000000000..499cc8e87a468582d3c64b96c30ddb11dbb04f1f --- /dev/null +++ b/assets/example_image/typical_vehicle_helicopter.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f1a1b37bc52417c0e1048927a30bf3a52dde81345f90114040608186196ffe7 +size 352731 diff --git a/assets/example_image/typical_vehicle_locomotive.png b/assets/example_image/typical_vehicle_locomotive.png new file mode 100644 index 0000000000000000000000000000000000000000..658e79d82868ae6e86ac108914169deafe02a53b --- /dev/null +++ b/assets/example_image/typical_vehicle_locomotive.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:67d5124e7069b133dc0aaa16047a52c6dc1d7c2a4e4510ffd3235fe95597fbef +size 806246 diff --git a/assets/example_image/typical_vehicle_pirate_ship.png b/assets/example_image/typical_vehicle_pirate_ship.png new file mode 100644 index 0000000000000000000000000000000000000000..a3baba4b951316dc7e0e940c22ba6f537bd5db14 --- /dev/null +++ b/assets/example_image/typical_vehicle_pirate_ship.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8926ec7c9f36a52e3bf1ca4e8cfc75d297da934fe7c0e8d7a73f0d35a5ef38ad +size 611289 diff --git a/assets/example_image/weatherworn_misc_paper_machine3.png b/assets/example_image/weatherworn_misc_paper_machine3.png new file mode 100644 index 0000000000000000000000000000000000000000..253a9c5dd331fc1a3d0d66cb5e9afb0b5b82ac46 --- /dev/null +++ b/assets/example_image/weatherworn_misc_paper_machine3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3c6fbf47ed53ffad1a3027f72bf0806c238682c7bf7604b8770aef428906d33b +size 502071 diff --git a/assets/example_multi_image/character_1.png b/assets/example_multi_image/character_1.png new file mode 100644 index 0000000000000000000000000000000000000000..9f56066845cfd18504ace4924c9a94544d55280c --- /dev/null +++ b/assets/example_multi_image/character_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:729e2e0214232e1dd45c9187e339f8a2a87c6e41257ef701e578a4f0a8be7ef1 +size 171816 diff --git a/assets/example_multi_image/character_2.png b/assets/example_multi_image/character_2.png new file mode 100644 index 0000000000000000000000000000000000000000..32de7843e9458be83c0adbd5ddbd526d3db59c94 --- /dev/null +++ b/assets/example_multi_image/character_2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e8afc8af9960a5f2315d9d5b9815f29137ef9b63c4d512c451a8ba374003c3ac +size 198217 diff --git a/assets/example_multi_image/character_3.png b/assets/example_multi_image/character_3.png new file mode 100644 index 0000000000000000000000000000000000000000..036b8f12cc2e2744fa3ebf3fba41114b07b340be --- /dev/null +++ b/assets/example_multi_image/character_3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3413a2b4f67105b947a42ebbe14d3f6ab9f68a99f2258a86fd50d94342b49bdd +size 145868 diff --git a/assets/example_multi_image/mushroom_1.png b/assets/example_multi_image/mushroom_1.png new file mode 100644 index 0000000000000000000000000000000000000000..ac2f8342feadfab20e91fbbc101a2c4db48a8556 --- /dev/null +++ b/assets/example_multi_image/mushroom_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3e5fd9ee75d39c827b0c5544392c255a89b4ca62bf3cf31f702d39b150bea00c +size 434321 diff --git a/assets/example_multi_image/mushroom_2.png b/assets/example_multi_image/mushroom_2.png new file mode 100644 index 0000000000000000000000000000000000000000..c81fb2dbf291c1898afbbfb61cce8e781ac7c42e --- /dev/null +++ b/assets/example_multi_image/mushroom_2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e5709b910341b12d149632b8442aebd218dd591b238ccb7e4e8b185860aae04 +size 461694 diff --git a/assets/example_multi_image/mushroom_3.png b/assets/example_multi_image/mushroom_3.png new file mode 100644 index 0000000000000000000000000000000000000000..67c68bcfccbf331fa132c3c41d8c40b8c0548a31 --- /dev/null +++ b/assets/example_multi_image/mushroom_3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:115c9c3a11d3d08de568680468ad42ac1322c21bdad46a43e149fc97cc687e48 +size 424840 diff --git a/assets/example_multi_image/orangeguy_1.png b/assets/example_multi_image/orangeguy_1.png new file mode 100644 index 0000000000000000000000000000000000000000..a89f44e74fd8ed4d7d140b5447315d60266081be --- /dev/null +++ b/assets/example_multi_image/orangeguy_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab30ee372fc365e5d100f2e06ea7cd17b3ea3f53b1a76ebe44e69a1cf834700e +size 632098 diff --git a/assets/example_multi_image/orangeguy_2.png b/assets/example_multi_image/orangeguy_2.png new file mode 100644 index 0000000000000000000000000000000000000000..b2554f8502d33981926afcfa8456dab03571ae63 --- /dev/null +++ b/assets/example_multi_image/orangeguy_2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f7ceecb68666ce692bf0a292fe4e125e657ece2babb67a7df89ab1827ad18665 +size 447045 diff --git a/assets/example_multi_image/orangeguy_3.png b/assets/example_multi_image/orangeguy_3.png new file mode 100644 index 0000000000000000000000000000000000000000..207e70ebc17912569261d5796b942b1d64dd39d7 --- /dev/null +++ b/assets/example_multi_image/orangeguy_3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fb89267bc91e0c0d1fdb148a5a5cd14e594f58e0a3da7b48f4e4cb61a3b7dcf1 +size 545243 diff --git a/assets/example_multi_image/popmart_1.png b/assets/example_multi_image/popmart_1.png new file mode 100644 index 0000000000000000000000000000000000000000..f4437187e4b20d0d35fcee7718bdea4aaebe5aa9 --- /dev/null +++ b/assets/example_multi_image/popmart_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:90a1fff9944982d66d13abb78da87a35092c26b905efd5a58b3c077bb5fed2cd +size 311311 diff --git a/assets/example_multi_image/popmart_2.png b/assets/example_multi_image/popmart_2.png new file mode 100644 index 0000000000000000000000000000000000000000..3747f78e72c5d3199ab077511b1e1897356a7f85 --- /dev/null +++ b/assets/example_multi_image/popmart_2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c74822f7fcf7b95d230e4578342272644a328ea175a90fa492f1b74ee438bdb4 +size 288176 diff --git a/assets/example_multi_image/popmart_3.png b/assets/example_multi_image/popmart_3.png new file mode 100644 index 0000000000000000000000000000000000000000..230775ef5b47d221e106aa2395cb70af93befa49 --- /dev/null +++ b/assets/example_multi_image/popmart_3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:239b41cf5f42ddcb1d3ba3f273e4ec66f2601b3303562ee97080d1904d9707b9 +size 310746 diff --git a/assets/example_multi_image/rabbit_1.png b/assets/example_multi_image/rabbit_1.png new file mode 100644 index 0000000000000000000000000000000000000000..f8389134e05238573f19962c2051a4a287d5fe81 --- /dev/null +++ b/assets/example_multi_image/rabbit_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f64979aae844de9e04f08c3f2cb8b6c47e4681c015afc624375ed6ac6aff0cd +size 359949 diff --git a/assets/example_multi_image/rabbit_2.png b/assets/example_multi_image/rabbit_2.png new file mode 100644 index 0000000000000000000000000000000000000000..03f5d8d502fc6a10a1a915462272cc71075bc51e --- /dev/null +++ b/assets/example_multi_image/rabbit_2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:85bb8760b5fb4c2e5c36e169a3cd0f95ca3164bd4764b8a830d220cd852f60ed +size 492291 diff --git a/assets/example_multi_image/rabbit_3.png b/assets/example_multi_image/rabbit_3.png new file mode 100644 index 0000000000000000000000000000000000000000..4e0f07e8a188e456451da5961b866b74d98d104f --- /dev/null +++ b/assets/example_multi_image/rabbit_3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f1f10a6c04e10ce27a20fd583ff443238bff2b6a49c485e5cf0ed6952a20c9c5 +size 339992 diff --git a/assets/example_multi_image/tiger_1.png b/assets/example_multi_image/tiger_1.png new file mode 100644 index 0000000000000000000000000000000000000000..bfd8a2cb78ac39ff4fbd46555e0af273d2319a27 --- /dev/null +++ b/assets/example_multi_image/tiger_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f2a61f5e48bd1a68d90c3afaf9ad8e5a7d955bc31ef7032b7fd876647922be1d +size 426296 diff --git a/assets/example_multi_image/tiger_2.png b/assets/example_multi_image/tiger_2.png new file mode 100644 index 0000000000000000000000000000000000000000..23ba80ad8bdd22f161e59b6d7b0a17e2f9082168 --- /dev/null +++ b/assets/example_multi_image/tiger_2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e5cab1d833a2541ca18e56fada83f5fe6b0cfe84c2d7e72382e2c93fcabc33a0 +size 731448 diff --git a/assets/example_multi_image/tiger_3.png b/assets/example_multi_image/tiger_3.png new file mode 100644 index 0000000000000000000000000000000000000000..0e0831948ddbc2ecb108a141ad7345ea55e408b4 --- /dev/null +++ b/assets/example_multi_image/tiger_3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5614fcb8e85dba6f30af6c992d1fe63e33b95ab70b4c870de4f0761fcecd5d9c +size 646392 diff --git a/assets/example_multi_image/yoimiya_1.png b/assets/example_multi_image/yoimiya_1.png new file mode 100644 index 0000000000000000000000000000000000000000..bb6519735180acb52c952cfa99be6f57d859941c --- /dev/null +++ b/assets/example_multi_image/yoimiya_1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6d9e7e0a3c75a736a25aeca6986afaa2adcd1ce96277484cbc9c41bf63d0230 +size 245840 diff --git a/assets/example_multi_image/yoimiya_2.png b/assets/example_multi_image/yoimiya_2.png new file mode 100644 index 0000000000000000000000000000000000000000..e2f03461b9f43ab58ad7810371a256744c215d70 --- /dev/null +++ b/assets/example_multi_image/yoimiya_2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e0703e3a557949a3176f2b02c8601ff883e22ce81962a75a21dbff9d5848ccf +size 182235 diff --git a/assets/example_multi_image/yoimiya_3.png b/assets/example_multi_image/yoimiya_3.png new file mode 100644 index 0000000000000000000000000000000000000000..54b268dfe229dcebc192e458b0f5c30ef34458d1 --- /dev/null +++ b/assets/example_multi_image/yoimiya_3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:56352f95536fcfe0996c33909e39e5beb423bd22b4bc48936938717073785305 +size 239134