Relit-LIVE-ZeroGPU-demo / relit_inference.py
fffiloni's picture
Migrated files batch 1
465cfae verified
Raw
History Blame Contribute Delete
35.7 kB
from diffsynth import ModelManager, WanVideoRelitlivePipeline
from torchvision.transforms import v2
from einops import rearrange
from PIL import Image
from tqdm import tqdm
from natsort import natsorted
from typing import List
import torch, os, imageio, argparse, torchvision, pdb, cv2, pathlib, glob, pyexr
import torch.nn as nn
import numpy as np
def stitch_frames(video_frames, rows=2, cols=5):
num_frames = len(video_frames[0])
assert all(len(frames) == num_frames for frames in video_frames), "All video frames must have the same number of frames."
frame_width, frame_height = video_frames[0][0].size
stitched_images = []
for frame_idx in range(num_frames):
stitched_image = Image.new('RGB', (cols * frame_width, rows * frame_height))
for i in range(rows):
for j in range(cols):
video_idx = i * cols + j
if video_idx < len(video_frames):
img = video_frames[video_idx][frame_idx]
x = j * frame_width
y = i * frame_height
stitched_image.paste(img, (x, y))
stitched_images.append(stitched_image)
return stitched_images
def save_video(frames, save_path, fps, quality=9, ffmpeg_params=None):
writer = imageio.get_writer(save_path, fps=fps, quality=quality, ffmpeg_params=ffmpeg_params)
for frame in tqdm(frames, desc="Saving video"):
frame = np.array(frame)
writer.append_data(frame)
writer.close()
def save_frames(frames, save_path):
os.makedirs(save_path, exist_ok=True)
for i, frame in enumerate(tqdm(frames, desc="Saving images")):
frame.save(os.path.join(save_path, f"{i}.png"))
def rotate_panorama_around_horizontal_axis(
panorama_img,
pitch_angle=10
):
if isinstance(panorama_img, Image.Image):
panorama = np.array(panorama_img.convert('RGB'))
elif isinstance(panorama_img, np.ndarray):
panorama = panorama_img.copy()
if len(panorama.shape) != 3 or panorama.shape[2] != 3:
raise ValueError("The NumPy array for the panoramic image must be in RGB format with dimensions (H, W, 3).")
else:
raise TypeError("The panoramic image must be a PIL.Image or a numpy.ndarray.")
pan_h, pan_w = panorama.shape[:2]
pitch_rad = np.radians(pitch_angle)
lon = np.linspace(0, 2 * np.pi, pan_w)
lat = np.linspace(-np.pi/2, np.pi/2, pan_h)
lon_grid, lat_grid = np.meshgrid(lon, lat)
x = np.cos(lat_grid) * np.sin(lon_grid)
y = np.sin(lat_grid)
z = np.cos(lat_grid) * np.cos(lon_grid)
coords = np.stack([x, y, z], axis=-1)
R_pitch = np.array([
[1, 0, 0],
[0, np.cos(pitch_rad), -np.sin(pitch_rad)],
[0, np.sin(pitch_rad), np.cos(pitch_rad)]
])
coords_rot = np.dot(coords, R_pitch.T)
lon_new = np.arctan2(coords_rot[..., 0], coords_rot[..., 2])
lat_new = np.arcsin(np.clip(coords_rot[..., 1], -1, 1))
u_new = (lon_new / (2 * np.pi) + 0.5) * pan_w
v_new = (lat_new / np.pi + 0.5) * pan_h
u_new = u_new.astype(np.float32)
v_new = v_new.astype(np.float32)
new_panorama = cv2.remap(
panorama,
u_new, v_new,
interpolation=cv2.INTER_CUBIC,
borderMode=cv2.BORDER_WRAP
)
new_panorama = np.hstack([new_panorama[:, pan_w//2:, ...], new_panorama[:, :pan_w//2, ...]])
return new_panorama
class PBRVideo_img_Dataset(torch.utils.data.Dataset):
def __init__(self, base_path, max_num_frames=81, frame_interval=1, num_frames=81, height=480, width=832, env_map_path=None, dataset_type='relit-live', \
use_ref_image=False, full_resolution=False, padding_resolution=False, drop_mr=False, args=None):
self.dataset_type = dataset_type
self.base_path = base_path
self.args = args
self.num_frames = num_frames
p = pathlib.Path(base_path)
if not p.is_dir():
raise NotADirectoryError(f"{base_path}' is not a valid dir")
if self.dataset_type == "relit-live":
self.path: List[pathlib.Path] = natsorted([item for item in p.iterdir() if item.is_dir()])
self.frame_interval = frame_interval if self.num_frames!=1 else 0
print(f'============= Load {len(self.path)}seqs from {base_path} =============')
self.max_num_frames = max_num_frames
self.num_frames = num_frames
self.height = height
self.width = width
self.frame_process = v2.Compose([
v2.CenterCrop(size=(height, width)),
v2.ToTensor(),
v2.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]),
])
self.env_map_path = env_map_path
self.use_ref_image = use_ref_image
self.full_resolution = full_resolution
self.padding_resolution = padding_resolution
self.drop_mr = drop_mr
def crop_and_resize(self, image, std_shape=None, env_reshape=False, align=None):
if env_reshape:
image = torchvision.transforms.functional.resize(
image,
(self.height, self.width),
interpolation=torchvision.transforms.InterpolationMode.BILINEAR
)
else:
if std_shape is not None:
height, width = std_shape
else:
width, height = image.size
if self.padding_resolution:
scale = min(self.width / width, self.height / height)
else:
scale = max(self.width / width, self.height / height)
if align is not None:
if align == 'width':
scale = self.width / width
elif align == 'height':
scale = self.height / height
image = torchvision.transforms.functional.resize(
image,
(round(height*scale), round(width*scale)),
interpolation=torchvision.transforms.InterpolationMode.BILINEAR
)
return image
def load_frames_using_imageio(self, file_path, start_frame_id, interval, num_frames, frame_process, std_shape=None, env_reshape=False, return_shape=False):
reader = imageio.get_reader(file_path)
image_num = reader.count_frames()
frames = []
for frame_id in range(num_frames):
sample_list = list(range(image_num)) + list(range(image_num-2, 0, -1))
sample_id = sample_list[((start_frame_id + frame_id * interval) % len(sample_list))]
frame = reader.get_data(sample_id)
img_shape = frame.shape[:2]
frame = Image.fromarray(frame)
frame = self.crop_and_resize(frame, std_shape, env_reshape)
frame = frame_process(frame)
frames.append(frame)
reader.close()
frames = torch.stack(frames, dim=0)
frames = rearrange(frames, "T C H W -> C T H W")
if return_shape:
return frames, img_shape
return frames
def load_frames_using_imageio_from_imgdir(self, file_path, start_frame_id, interval, num_frames, frame_process, divided_max=False, std_shape=None, env_reshape=False, return_shape=False):
image_paths = glob.glob(os.path.join(file_path, '*.png')) + glob.glob(os.path.join(file_path, '*.jpg')) + glob.glob(os.path.join(file_path, '*.exr'))
if len(image_paths) == 0:
return None
sorted_image_paths = natsorted(image_paths)
image_num = len(sorted_image_paths)
frames = []
for frame_id in range(num_frames):
sample_list = list(range(image_num)) + list(range(image_num-2, 0, -1))
sample_id = sample_list[((start_frame_id + frame_id * interval) % len(sample_list))]
if ".exr" in sorted_image_paths[sample_id]:
frame = pyexr.open(sorted_image_paths[sample_id]).get()
else:
frame = imageio.imread(sorted_image_paths[sample_id])
if frame.shape[-1] == 4:
frame = frame[:,:,:3]
elif frame.shape[-1] == 1:
frame = np.repeat(frame, 3, axis=-1)
img_shape = frame.shape[:2]
if divided_max:
bg_mask = (frame > 1000.0)
if len(frame[bg_mask]) > 0:
frame_max = float(np.percentile(frame, 99))
frame = np.clip(frame / frame_max, 0, 1)
else:
frame = frame / frame.max()
if frame.min() < 0:
frame = frame * 0.5 + 0.5
if frame.dtype != 'uint8':
frame = (frame * 255).astype(np.uint8)
frame = Image.fromarray(frame)
frame = self.crop_and_resize(frame, std_shape, env_reshape)
frame = frame_process(frame)
frames.append(frame)
frames = torch.stack(frames, dim=0)
frames = rearrange(frames, "T C H W -> C T H W")
if return_shape:
return frames, img_shape
return frames
def load_frames_using_imageio_from_imgpath(self, file_path, start_frame_id, interval, num_frames, frame_process, divided_max=False, std_shape=None, env_reshape=False, return_shape=False, align=None):
image_paths = [file_path]
if len(image_paths) == 0:
return None
sorted_image_paths = natsorted(image_paths)
image_num = len(sorted_image_paths)
frames = []
for frame_id in range(num_frames):
sample_list = list(range(image_num)) + list(range(image_num-2, 0, -1))
sample_id = sample_list[((start_frame_id + frame_id * interval) % len(sample_list))]
if ".exr" in sorted_image_paths[sample_id]:
frame = pyexr.open(sorted_image_paths[sample_id]).get()
else:
frame = imageio.imread(sorted_image_paths[sample_id])
if frame.shape[-1] == 4:
frame = frame[:,:,:3]
elif frame.shape[-1] == 1:
frame = np.repeat(frame, 3, axis=-1)
img_shape = frame.shape[:2]
if divided_max:
bg_mask = (frame > 1000.0)
if len(frame[bg_mask]) > 0:
frame_max = float(np.percentile(frame, 99))
frame = np.clip(frame / frame_max, 0, 1)
else:
frame = frame / frame.max()
if frame.min() < 0:
frame = frame * 0.5 + 0.5
if frame.dtype != 'uint8':
frame = (frame * 255).astype(np.uint8)
frame = Image.fromarray(frame)
frame = self.crop_and_resize(frame, std_shape, env_reshape, align=align)
frame = frame_process(frame)
frames.append(frame)
frames = torch.stack(frames, dim=0)
frames = rearrange(frames, "T C H W -> C T H W")
if return_shape:
return frames, img_shape
return frames
def __getitem__(self, index):
data_id = index % len(self.path)
dir_path = self.path[data_id]
RGB_path = os.path.join(dir_path, "images_4")
basecolor_path = os.path.join(dir_path, "Base Color")
depth_path = os.path.join(dir_path, "depth")
metallic_path = os.path.join(dir_path, "Metallic")
normal_path = os.path.join(dir_path, "normal")
roughness_path = os.path.join(dir_path, "Roughness")
fixed_env = True
if self.env_map_path is not None:
ldr_path = os.path.join(self.env_map_path, "ldr_video_fix_first_frame.mp4")
hdr_log_path = os.path.join(self.env_map_path, "hdr_log_video_fix_first_frame.mp4")
env_dir_path = os.path.join(self.env_map_path, "env_dir_video_fix_first_frame.mp4")
else:
ldr_path = os.path.join(dir_path, "env", "ldr_video_fix_first_frame.mp4")
hdr_log_path = os.path.join(dir_path, "env", "hdr_log_video_fix_first_frame.mp4")
env_dir_path = os.path.join(dir_path, "env", "env_dir_video_fix_first_frame.mp4")
fixed_env = False
start_frame_id = 0
if self.full_resolution:
source = self.load_frames_using_imageio_from_imgdir(RGB_path, start_frame_id, self.frame_interval, self.num_frames, self.frame_process, env_reshape=True)
basecolor= self.load_frames_using_imageio_from_imgdir(basecolor_path, start_frame_id, self.frame_interval, self.num_frames, self.frame_process, env_reshape=True)
depth= self.load_frames_using_imageio_from_imgdir(depth_path, start_frame_id, self.frame_interval, self.num_frames, self.frame_process, divided_max=True, env_reshape=True)
metallic= self.load_frames_using_imageio_from_imgdir(metallic_path, start_frame_id, self.frame_interval, self.num_frames, self.frame_process, env_reshape=True)
normal= self.load_frames_using_imageio_from_imgdir(normal_path, start_frame_id, self.frame_interval, self.num_frames, self.frame_process, env_reshape=True)
roughness = self.load_frames_using_imageio_from_imgdir(roughness_path, start_frame_id, self.frame_interval, self.num_frames, self.frame_process, env_reshape=True)
else:
source, std_shape = self.load_frames_using_imageio_from_imgdir(RGB_path, start_frame_id, self.frame_interval, self.num_frames, self.frame_process, return_shape=True)
basecolor= self.load_frames_using_imageio_from_imgdir(basecolor_path, start_frame_id, self.frame_interval, self.num_frames, self.frame_process, std_shape=std_shape)
depth= self.load_frames_using_imageio_from_imgdir(depth_path, start_frame_id, self.frame_interval, self.num_frames, self.frame_process, divided_max=True, std_shape=std_shape)
metallic= self.load_frames_using_imageio_from_imgdir(metallic_path, start_frame_id, self.frame_interval, self.num_frames, self.frame_process, std_shape=std_shape)
normal= self.load_frames_using_imageio_from_imgdir(normal_path, start_frame_id, self.frame_interval, self.num_frames, self.frame_process, std_shape=std_shape)
roughness = self.load_frames_using_imageio_from_imgdir(roughness_path, start_frame_id, self.frame_interval, self.num_frames, self.frame_process, std_shape=std_shape)
if metallic is None:
metallic = source * 0.0
if roughness is None:
roughness = source * 0.0
ldr = self.load_frames_using_imageio(ldr_path, start_frame_id, self.frame_interval, self.num_frames, self.frame_process, env_reshape=True)
hdr_log = self.load_frames_using_imageio(hdr_log_path, start_frame_id, self.frame_interval, self.num_frames, self.frame_process, env_reshape=True)
env_dir = self.load_frames_using_imageio(env_dir_path, start_frame_id, self.frame_interval, self.num_frames, self.frame_process, env_reshape=True)
if fixed_env:
ldr = ldr[:,0:1].repeat(1, self.num_frames, 1, 1)
hdr_log = hdr_log[:,0:1].repeat(1, self.num_frames, 1, 1)
if self.drop_mr:
metallic_weight = torch.tensor(0.0)
roughness_weight = torch.tensor(0.0)
else:
metallic_weight = torch.tensor(1.0)
roughness_weight = torch.tensor(1.0)
env_self_weight = torch.tensor(1.0)
env_cross_weight = torch.tensor(1.0)
# print(f"haven't use prompt for {RGB_path}")
prompt = ""
if self.use_ref_image:
image_paths = glob.glob(os.path.join(basecolor_path, '*.png')) + glob.glob(os.path.join(basecolor_path, '*.jpg')) + glob.glob(os.path.join(basecolor_path, '*.exr'))
max_idx = (len(image_paths)-1) - (self.num_frames-1) * self.frame_interval
if max_idx == 0:
start_frame_id_ref = 0
else:
start_frame_id_ref = start_frame_id
ref_RGB_path = RGB_path
if self.full_resolution:
ref_source = self.load_frames_using_imageio_from_imgdir(ref_RGB_path, start_frame_id_ref, self.frame_interval, self.num_frames, self.frame_process, env_reshape=True)
else:
ref_source = self.load_frames_using_imageio_from_imgdir(ref_RGB_path, start_frame_id_ref, self.frame_interval, self.num_frames, self.frame_process, std_shape=std_shape)
if self.args.use_fixed_frame_and_w_rotate_light or self.args.use_fixed_frame_and_h_rotate_light or self.num_frames == 1:
ref_idx = 0
else:
ref_idx = ref_source.shape[1] // 2 # Use the mid frame as the reference image
ref_image = ref_source[:, ref_idx:ref_idx+1, :, :]
if self.args.ref_image_path_with_idddx is not None:
ref_image_path_with_idx = self.args.ref_image_path_with_idddx.replace("idddx", f"{index}")
if self.full_resolution:
ref_image = self.load_frames_using_imageio_from_imgpath(ref_image_path_with_idx, self.max_num_frames, start_frame_id_ref, self.frame_interval, self.num_frames, self.frame_process, env_reshape=True)
else:
ref_image = self.load_frames_using_imageio_from_imgpath(ref_image_path_with_idx, self.max_num_frames, start_frame_id_ref, self.frame_interval, self.num_frames, self.frame_process, std_shape=None, align='width')
ref_image = ref_image[:, -1:, :, :]
data = {
"source_video": source,
"basecolor": basecolor,
"depth": depth,
"metallic":metallic,
"normal": normal,
"roughness":roughness,
'metallic_weight': metallic_weight,
'roughness_weight': roughness_weight,
'env_self_weight': env_self_weight,
'env_cross_weight': env_cross_weight,
"ldr": ldr,
"hdr_log": hdr_log,
"env_dir": env_dir,
"ref_image": ref_image,
"ref_video": ref_source,
"path": str(dir_path),
"prompt": prompt,
}
else:
data = {
"source_video": source,
"basecolor": basecolor,
"depth": depth,
"metallic":metallic,
"normal": normal,
"roughness":roughness,
'metallic_weight': metallic_weight,
'roughness_weight': roughness_weight,
'env_self_weight': env_self_weight,
'env_cross_weight': env_cross_weight,
"ldr": ldr,
"hdr_log": hdr_log,
"env_dir": env_dir,
"path": str(dir_path),
"prompt": prompt,
}
return data
def __len__(self):
return len(self.path)
def parse_args():
parser = argparse.ArgumentParser(description="diffusion renderer Inference")
parser.add_argument(
"--dataset_path",
type=str,
default="./example_test_data",
help="The path of the Dataset.",
)
parser.add_argument(
"--env_map_path",
type=str,
default=None,
help="env_map_path.",
)
parser.add_argument(
"--use_ref_image",
default=False,
action="store_true",
help="Whether to use reference image.",
)
parser.add_argument(
"--use_muti_ref_image",
default=False,
action="store_true",
help="Whether to use multiple reference images.",
)
parser.add_argument(
"--ref_image_path_with_idddx",
type=str,
default=None,
help="Path to the reference image with index.",
)
parser.add_argument(
"--full_resolution",
default=False,
action="store_true",
help="Whether to use full_resolution.",
)
parser.add_argument(
"--padding_resolution",
default=False,
action="store_true",
help="Whether to use padding_resolution.",
)
parser.add_argument(
"--dataset_type",
type=str,
default="relit-live",
help="Dataset format to load. Use 'relit-live' for the default Relit-LiVE directory layout.",
)
parser.add_argument(
"--drop_mr",
default=False,
action="store_true",
help="Ignore metallic and roughness inputs by setting their conditioning weights to zero.",
)
parser.add_argument(
"--use_rotate_light",
default=False,
action="store_true",
help="Enable light rotation mode during inference.",
)
parser.add_argument(
"--use_fixed_frame_and_w_rotate_light",
default=False,
action="store_true",
help="Repeat the first frame and rotate the environment map along the width axis across frames.",
)
parser.add_argument(
"--use_fixed_frame_and_h_rotate_light",
default=False,
action="store_true",
help="Repeat the first frame and rotate the environment map along the height axis across frames.",
)
parser.add_argument(
"--h_rotate_light",
type=int,
default=0,
help="Rotate the environment map vertically by this many degrees for every frame.",
)
parser.add_argument(
"--w_rotate_light",
type=int,
default=0,
help="Rotate the environment map horizontally by this many pixels for every frame.",
)
parser.add_argument(
"--num_frames",
type=int,
default=81,
help="Number of frames.",
)
parser.add_argument(
"--num_inference_steps",
type=int,
default=50,
help="Number of denoising steps used during inference.",
)
parser.add_argument(
"--frame_interval",
type=int,
default=1,
help="Sampling interval between frames read from the input sequence.",
)
parser.add_argument(
"--height",
type=int,
default=480,
help="Image height.",
)
parser.add_argument(
"--width",
type=int,
default=832,
help="Image width.",
)
parser.add_argument(
"--ckpt_path",
type=str,
default=None,
help="Path to the fine-tuned checkpoint to load into the pipeline.",
)
parser.add_argument(
"--output_dir",
type=str,
default="./results",
help="Path to save the results.",
)
parser.add_argument(
"--output_path",
type=str,
default=None,
help="Optional explicit output file path. Supports .mp4 and .png.",
)
parser.add_argument(
"--dataloader_num_workers",
type=int,
default=1,
help="Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process.",
)
parser.add_argument(
"--cfg_scale",
type=float,
default=5.0,
help="Classifier-free guidance scale.",
)
parser.add_argument(
"--wo_ref_weight",
type=float,
default=0.0,
help="Weight applied to the branch without reference-image conditioning.",
)
parser.add_argument(
"--quality",
type=int,
default=5,
help="Video encoding quality passed to imageio when saving mp4 outputs.",
)
args = parser.parse_args()
return args
if __name__ == '__main__':
args = parse_args()
# Load Wan2.1 pre-trained models
model_manager = ModelManager(torch_dtype=torch.bfloat16, device="cpu")
model_manager.load_models([
"models/Wan-AI/Wan2.1-T2V-1.3B/diffusion_pytorch_model.safetensors",
"models/Wan-AI/Wan2.1-T2V-1.3B/models_t5_umt5-xxl-enc-bf16.pth",
"models/Wan-AI/Wan2.1-T2V-1.3B/Wan2.1_VAE.pth",
])
pipe = WanVideoRelitlivePipeline.from_model_manager(model_manager, device="cuda")
# Initialize additional modules introduced in relit live
dim=pipe.dit.blocks[0].self_attn.q.weight.shape[0]
for block in pipe.dit.blocks:
block.env_encoder = nn.Sequential(
nn.Conv3d(
48, dim, kernel_size=(1,2,2), stride=(1,2,2)),
nn.SiLU()
)
block.env_encoder[0].weight.data.zero_()
block.env_encoder[0].bias.data.zero_()
block.projector = nn.Linear(dim, dim)
block.projector.weight = nn.Parameter(torch.eye(dim))
block.projector.bias = nn.Parameter(torch.zeros(dim))
# if args.use_ref_image:
dim = pipe.dit.patch_embedding.out_channels
pipe.dit.ref_conv = nn.Conv2d(16, dim, kernel_size=(2,2), stride=(2,2))
# Load checkpoint
state_dict = torch.load(args.ckpt_path, map_location="cpu")
pipe.dit.load_state_dict(state_dict, strict=False) # True
pipe.to("cuda")
pipe.to(dtype=torch.bfloat16)
output_dir = os.path.join(args.output_dir)
if not os.path.exists(output_dir):
os.makedirs(output_dir)
dataset = PBRVideo_img_Dataset(
base_path=args.dataset_path,
max_num_frames=args.num_frames,
frame_interval=args.frame_interval,
num_frames=args.num_frames,
height=args.height,
width=args.width,
env_map_path=args.env_map_path,
dataset_type=args.dataset_type,
use_ref_image=args.use_ref_image,
full_resolution=args.full_resolution,
padding_resolution=args.padding_resolution,
drop_mr=args.drop_mr,
args=args,
)
dataloader = torch.utils.data.DataLoader(
dataset,
shuffle=False,
batch_size=1,
num_workers=args.dataloader_num_workers
)
# Inference
ldr_img, log_img, ref_img = None, None, None
for batch_idx, batch in enumerate(dataloader):
input_rgb = batch["source_video"] # b c t h w
basecolor = batch["basecolor"]
depth = batch["depth"]
metallic = batch["metallic"]
normal = batch["normal"]
roughness = batch["roughness"]
ldr = batch["ldr"]
hdr_log = batch["hdr_log"]
env_dir = batch["env_dir"]
dir_path = batch["path"][0]
prompt = batch["prompt"]
print(f'processing {dir_path}, prompt: {prompt}.')
if args.h_rotate_light != 0:
for frame_idx in range(args.num_frames):
ldr_now = np.array(ldr[0,:,frame_idx,...].permute(1, 2, 0)) # c h w -> h w c
hdr_log_now = np.array(hdr_log[0,:,frame_idx,...].permute(1, 2, 0))
ldr_now = rotate_panorama_around_horizontal_axis(ldr_now, args.h_rotate_light)
hdr_log_now = rotate_panorama_around_horizontal_axis(hdr_log_now, args.h_rotate_light)
ldr[:,:,frame_idx:frame_idx+1,...] = torch.tensor(ldr_now).permute(2, 0, 1).unsqueeze(0).unsqueeze(2)
hdr_log[:,:,frame_idx:frame_idx+1,...] = torch.tensor(hdr_log_now).permute(2, 0, 1).unsqueeze(0).unsqueeze(2)
batch["ldr"] = ldr
batch["hdr_log"] = hdr_log
elif args.w_rotate_light != 0:
for frame_idx in range(args.num_frames):
ldr_now = np.array(ldr[0,:,frame_idx,...].permute(1, 2, 0)) # c h w -> h w c
hdr_log_now = np.array(hdr_log[0,:,frame_idx,...].permute(1, 2, 0))
ldr_rotate = np.concatenate((ldr_now[:,args.w_rotate_light:], ldr_now[:,:args.w_rotate_light]), axis=1)
hdr_log_rotate = np.concatenate((hdr_log_now[:,args.w_rotate_light:], hdr_log_now[:,:args.w_rotate_light]), axis=1)
ldr[:,:,frame_idx:frame_idx+1,...] = torch.tensor(ldr_rotate).permute(2, 0, 1).unsqueeze(0).unsqueeze(2)
hdr_log[:,:,frame_idx:frame_idx+1,...] = torch.tensor(hdr_log_rotate).permute(2, 0, 1).unsqueeze(0).unsqueeze(2)
batch["ldr"] = ldr
batch["hdr_log"] = hdr_log
if (args.use_fixed_frame_and_w_rotate_light or args.use_fixed_frame_and_h_rotate_light or args.use_rotate_light) and args.num_frames == 1:
continue
if (args.use_fixed_frame_and_w_rotate_light or args.use_fixed_frame_and_h_rotate_light or args.use_rotate_light) and args.num_frames != 1:
# Repeat the first frame for num_frames times
if not args.use_rotate_light:
input_rgb = input_rgb[:, :, :1, :, :].repeat(1, 1, args.num_frames, 1, 1)
batch["source_video"] = input_rgb
basecolor = basecolor[:, :, :1, :, :].repeat(1, 1, args.num_frames, 1, 1)
batch["basecolor"] = basecolor
depth = depth[:, :, :1, :, :].repeat(1, 1, args.num_frames, 1, 1)
batch["depth"] = depth
metallic = metallic[:, :, :1, :, :].repeat(1, 1, args.num_frames, 1, 1)
batch["metallic"] = metallic
normal = normal[:, :, :1, :, :].repeat(1, 1, args.num_frames, 1, 1)
batch["normal"] = normal
roughness = roughness[:, :, :1, :, :].repeat(1, 1, args.num_frames, 1, 1)
batch["roughness"] = roughness
if args.use_fixed_frame_and_h_rotate_light:
ldr_first = np.array(ldr[0,:,0,...].permute(1, 2, 0)) # c h w -> h w c
hdr_log_first = np.array(hdr_log[0,:,0,...].permute(1, 2, 0))
x_rotate_list = [int(360 / (args.num_frames-1) * (i+1)) for i in range(args.num_frames-1)]
for frame_idx, x_rotate in enumerate(x_rotate_list):
ldr_now = rotate_panorama_around_horizontal_axis(ldr_first, x_rotate)
hdr_log_now = rotate_panorama_around_horizontal_axis(hdr_log_first, x_rotate)
ldr[:,:,frame_idx+1:frame_idx+2,...] = torch.tensor(ldr_now).permute(2, 0, 1).unsqueeze(0).unsqueeze(2)
hdr_log[:,:,frame_idx+1:frame_idx+2,...] = torch.tensor(hdr_log_now).permute(2, 0, 1).unsqueeze(0).unsqueeze(2)
batch["ldr"] = ldr
batch["hdr_log"] = hdr_log
else:
ldr_first = ldr[:,:,:1,...]
hdr_log_first = hdr_log[:,:,:1,...]
_, c, _, h, w = ldr_first.shape
y_rotate_list = [int(w / (args.num_frames-1) * (i+1)) for i in range(args.num_frames-1)]
for frame_idx, y_rotate in enumerate(y_rotate_list):
try:
ldr_now = torch.zeros_like(ldr_first)
ldr_now[:, :, :, :, -y_rotate:] = ldr_first[:, :, :, :, :y_rotate]
ldr_now[:, :, :, :, :-y_rotate] = ldr_first[:, :, :, :, y_rotate:]
hdr_log_now = torch.zeros_like(hdr_log_first)
hdr_log_now[:, :, :, :, -y_rotate:] = hdr_log_first[:, :, :, :, :y_rotate]
hdr_log_now[:, :, :, :, :-y_rotate] = hdr_log_first[:, :, :, :, y_rotate:]
ldr[:,:,frame_idx+1:frame_idx+2,...] = ldr_now
hdr_log[:,:,frame_idx+1:frame_idx+2,...] = hdr_log_now
except:
pdb.set_trace()
batch["ldr"] = ldr
batch["hdr_log"] = hdr_log
seq_name = os.path.basename(dir_path)
if args.use_rotate_light:
seq_name += "_w_rotate_light"
elif args.use_fixed_frame_and_w_rotate_light:
seq_name += "_fixed_frame_and_w_rotate_light"
elif args.use_fixed_frame_and_h_rotate_light:
seq_name += "_fixed_frame_and_h_rotate_light"
model_name = os.path.basename(args.ckpt_path).replace("ckpt", f'{seq_name}_{args.height}_{args.width}')
if not args.use_ref_image:
model_name += f'_no_ref_image'
elif args.use_muti_ref_image:
model_name += f'_ref_muti_image'
else:
model_name += f'_ref_mid_image'
if args.env_map_path is not None:
env_str = os.path.basename(args.env_map_path)
model_name += f'_envdir_{env_str}'
if args.drop_mr:
model_name += f'_drop_mr'
if args.padding_resolution:
model_name += f'_padding'
if args.full_resolution:
model_name += f'_full'
if args.num_inference_steps != 50:
model_name += f'_steps{args.num_inference_steps}'
if args.wo_ref_weight != 0.0:
args.wo_ref_weight = round(args.wo_ref_weight, 2)
model_name += f'_worw{args.wo_ref_weight}'
if args.h_rotate_light != 0:
model_name += f'_h-rotation-{args.h_rotate_light}'
if args.w_rotate_light != 0:
model_name += f'_w-rotation-{args.w_rotate_light}'
if args.ref_image_path_with_idddx is not None:
model_name += f'_ref_hr_{batch_idx}'
model_name += f'_frames{args.num_frames}'
if args.num_frames in [1]:
save_path = os.path.join(output_dir, f"{model_name}_cfg{args.cfg_scale}_render.png")
else:
save_path = os.path.join(output_dir, f"{model_name}_cfg{args.cfg_scale}_video.mp4")
video, envs = pipe(
prompt=prompt,
negative_prompt="",
batch=batch,
height=args.height,
width=args.width,
num_frames=args.num_frames,
cfg_scale=args.cfg_scale,
num_inference_steps=args.num_inference_steps,
seed=0, tiled=True, wo_ref_weight=args.wo_ref_weight, use_muti_ref_image=args.use_muti_ref_image
)
input_rgb = pipe.tensor2video(input_rgb[0])
basecolor = pipe.tensor2video(basecolor[0])
depth = pipe.tensor2video(depth[0])
metallic = pipe.tensor2video(metallic[0])
normal = pipe.tensor2video(normal[0])
roughness = pipe.tensor2video(roughness[0])
ldr = pipe.tensor2video(ldr[0])
hdr_log = pipe.tensor2video(hdr_log[0])
env_dir = pipe.tensor2video(env_dir[0])
if args.use_ref_image:
ref_image = batch["ref_image"].repeat(1, 1, args.num_frames, 1, 1) # b c t h w
ref_image = pipe.tensor2video(ref_image[0])
stitched_results = stitch_frames([basecolor, metallic, roughness, depth, normal, ldr, ref_image, video, input_rgb, env_dir, envs, env_dir], rows=4, cols=3)
else:
stitched_results = stitch_frames([basecolor, metallic, roughness, depth, normal, ldr, env_dir, video, input_rgb, env_dir, envs, env_dir], rows=4, cols=3)
print(f'Finish the inference of {model_name}.')
if args.num_frames in [1]:
output = video[0]
output_all = stitched_results[0]
output.save(os.path.join(output_dir, f"{model_name}_cfg{args.cfg_scale}_render.png"))
output_all.save(os.path.join(output_dir, f"{model_name}_cfg{args.cfg_scale}.png"))
else:
save_video(video, os.path.join(output_dir, f"{model_name}_cfg{args.cfg_scale}_video.mp4"), fps=30, quality=args.quality)
save_video(stitched_results, os.path.join(output_dir, f"{model_name}_cfg{args.cfg_scale}.mp4"), fps=30, quality=5)
if args.output_path is not None:
if '.mp4' in args.output_path:
save_video(video, args.output_path, fps=30, quality=args.quality)
elif '.png' in args.output_path:
output = video[0]
output.save(args.output_path)
else:
print(f'Error output_path: {args.output_path}')