Support texture generation

This commit is contained in:
Justin John
2024-04-03 07:09:56 +05:30
parent b9b059f508
commit cad40c6741
20 changed files with 171 additions and 116 deletions
+87 -67
View File
@@ -3,100 +3,120 @@ import logging
import os
import sys
import time
from PIL import Image
import numpy as np
import rembg
import torch
from PIL import Image
import rembg
from tsr.system import TSR
from tsr.utils import remove_background, resize_foreground, save_video
from tsr.utils import remove_background, resize_foreground, save_video, to_gradio_3d_orientation
# Setup logging configuration
logging.basicConfig(format="%(asctime)s - %(levelname)s - %(message)s", level=logging.INFO)
# Print environment and GPU information
def print_environment_info():
logging.info("Environment variables:")
for key, value in os.environ.items():
logging.info(f"{key}: {value}")
logging.info('========================================')
logging.info(f'Python interpreter: {sys.executable}')
logging.info(f'PyTorch version: {torch.__version__}')
logging.info(f'CUDA Available: {torch.cuda.is_available()}')
logging.info(f'CUDA Device count: {torch.cuda.device_count()}')
logging.info(f'CUDA threads for parallelizing CPU operations: {torch.get_num_threads()}')
logging.info(f'CUDA architectures library was compiled for: {torch.cuda.get_arch_list()}')
logging.info('========================================')
print_environment_info()
def print_gpu_info():
print('========================================')
print('Python interpreter:', sys.executable)
print('PyTorch version:', torch.__version__)
print('CUDA Available:', torch.cuda.is_available())
print('CUDA Device count:', torch.cuda.device_count())
print('CUDA threads for parallelizing CPU operations:', torch.get_num_threads())
print('CUDA architectures library was compiled for:', torch.cuda.get_arch_list())
print('========================================', flush=True)
class Timer:
def __init__(self):
self.start_times = {}
self.items = {}
self.time_scale = 1000.0 # ms
self.time_unit = "ms"
def start(self, name: str):
self.start_times[name] = time.time()
def start(self, name: str) -> None:
if torch.cuda.is_available():
torch.cuda.synchronize()
self.items[name] = time.time()
logging.info(f"{name} ...")
def end(self, name: str):
if name in self.start_times:
elapsed_time = time.time() - self.start_times[name]
logging.info(f"{name} finished in {elapsed_time * 1000:.2f}ms.")
del self.start_times[name]
def end(self, name: str) -> None:
if name not in self.items:
return
if torch.cuda.is_available():
torch.cuda.synchronize()
elapsed = (time.time() - self.items.pop(name)) * self.time_scale
logging.info(f"{name} finished in {elapsed:.2f}{self.time_unit}.")
timer = Timer()
def main():
logging.basicConfig(format="%(asctime)s - %(levelname)s - %(message)s", level=logging.INFO)
def process_image(image_path, remove_bg=True, foreground_ratio=0.85):
image = Image.open(image_path).convert("RGB")
if remove_bg:
rembg_session = rembg.new_session()
image = remove_background(image, rembg_session)
image = resize_foreground(image, foreground_ratio)
image = np.array(image).astype(np.float32) / 255.0
image = image[:, :, :3] * image[:, :, 3:4] + (1 - image[:, :, 3:4]) * 0.5 # Blend with grey background
return Image.fromarray((image * 255.0).astype(np.uint8))
print_gpu_info()
parser = argparse.ArgumentParser()
parser.add_argument("--input", type=str, required=True, nargs="+", help="Path to input image(s).")
parser.add_argument("--device", default="cuda:0", type=str, help="Device to use. Defaults to 'cuda:0' if available.")
parser.add_argument("--pretrained-model-name-or-path", default="stabilityai/TripoSR", type=str, help="Pretrained model path or Hugging Face model ID.")
parser.add_argument("--chunk-size", default=8192, type=int, help="Chunk size for processing. Smaller values use less VRAM.")
parser.add_argument("--mc-resolution", default=256, type=int, help="Resolution for the marching cubes algorithm.")
parser.add_argument("--no-remove-bg", action="store_true", help="Disable automatic background removal.")
parser.add_argument("--foreground-ratio", default=0.85, type=float, help="Foreground to image size ratio.")
parser.add_argument("--output-dir", default="output/", type=str, help="Directory to save the output.")
parser.add_argument("--model-save-format", default="glb", choices=["obj", "glb"], help="Mesh save format.")
parser.add_argument("--render", action="store_true", help="Render a video from the model output.")
args = parser.parse_args()
def main(args):
output_dir = args.output_dir
os.makedirs(output_dir, exist_ok=True)
device = args.device if torch.cuda.is_available() else "cpu"
timer = Timer()
timer.start("Initializing model")
model = TSR.from_pretrained(args.pretrained_model_name_or_path, config_name="config.yaml", weight_name="model.ckpt")
model.renderer.set_chunk_size(args.chunk_size)
model.to(device)
timer.end("Initializing model")
for i, image_path in enumerate(args.image):
timer.start(f"Processing image {i+1}")
processed_image = process_image(image_path, not args.no_remove_bg, args.foreground_ratio)
processed_image_path = os.path.join(output_dir, f"processed_{i}.png")
processed_image.save(processed_image_path)
timer.end(f"Processing image {i+1}")
timer.start("Processing images")
images = []
if args.no_remove_bg:
rembg_session = None
else:
rembg_session = rembg.new_session()
timer.start(f"Running model on image {i+1}")
for i, image_path in enumerate(args.input):
if args.no_remove_bg:
image = np.array(Image.open(image_path).convert("RGB"))
else:
image = remove_background(Image.open(image_path), rembg_session)
image = resize_foreground(image, args.foreground_ratio)
image = np.array(image).astype(np.float32) / 255.0
image = image[:, :, :3] * image[:, :, 3:4] + (1 - image[:, :, 3:4]) * 0.5
image = Image.fromarray((image * 255.0).astype(np.uint8))
if not os.path.exists(os.path.join(output_dir, str(i))):
os.makedirs(os.path.join(output_dir, str(i)))
image.save(os.path.join(output_dir, str(i), "input.png"))
images.append(image)
timer.end("Processing images")
for i, image in enumerate(images):
logging.info(f"Running image {i + 1}/{len(images)} ...")
timer.start("Running model")
with torch.no_grad():
scene_codes = model([np.array(processed_image).astype(np.float32) / 255.0], device=device)
timer.end(f"Running model on image {i+1}")
scene_codes = model([image], device=device)
timer.end("Running model")
timer.start(f"Exporting mesh for image {i+1}")
if args.render:
timer.start("Rendering")
render_images = model.render(scene_codes, n_views=30, return_type="pil")
for ri, render_image in enumerate(render_images[0]):
render_image.save(os.path.join(output_dir, str(i), f"render_{ri:03d}.png"))
save_video(render_images[0], os.path.join(output_dir, str(i), "render.mp4"), fps=30)
timer.end("Rendering")
timer.start("Exporting mesh")
meshes = model.extract_mesh(scene_codes, resolution=args.mc_resolution)
mesh_export_path = os.path.join(output_dir, f"mesh_{i}.{args.model_save_format}")
meshes[0].export(mesh_export_path, file_type=args.model_save_format)
timer.end(f"Exporting mesh for image {i+1}")
meshes[0] = to_gradio_3d_orientation(meshes[0])
mesh_path = os.path.join(output_dir, str(i), f"mesh.{args.model_save_format}")
meshes[0].export(mesh_path)
logging.info(f"Mesh saved to {mesh_path}")
timer.end("Exporting mesh")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Run TSR model on input images.")
parser.add_argument("image", type=str, nargs="+", help="Path to input image(s).")
parser.add_argument("--device", default="cuda:0", type=str, help="Device to use. Defaults to 'cuda:0', falls back to 'cpu' if CUDA is not available.")
parser.add_argument("--pretrained-model-name-or-path", default="stabilityai/TripoSR", type=str, help="Pretrained model identifier or file path.")
parser.add_argument("--chunk-size", default=8192, type=int, help="Evaluation chunk size for rendering. Defaults to 8192.")
parser.add_argument("--mc-resolution", default=256, type=int, help="Resolution for marching cubes algorithm. Defaults to 256.")
parser.add_argument("--no-remove-bg", action="store_true", help="Don't remove background from input images.")
parser.add_argument("--foreground-ratio", default=0.85, type=float, help="Foreground ratio for resizing foreground. Defaults to 0.85.")
parser.add_argument("--output-dir", default="output/", type=str, help="Directory to save the output. Defaults to 'output/'.")
parser.add_argument("--model-save-format", default="obj", choices=["obj", "glb"], type=str, help="Format for saving the model. Supports 'obj' and 'glb'. Defaults to 'obj'.")
args = parser.parse_args()
main(args)
main()