Files
yuvraj108c-ComfyUI_InvSR/node.py
T

188 lines
6.2 KiB
Python

from .comfyui_invsr_trimmed import get_configs, InvSamplerSR, BaseSampler, Namespace
import torch
from comfy.utils import ProgressBar
from folder_paths import get_full_path, get_folder_paths, models_dir
import os
import torch.nn.functional as F
def split_tensor_into_batches(tensor, batch_size):
"""
Split a tensor into smaller batches of specified size
Args:
tensor (torch.Tensor): Input tensor of shape (N, C, H, W)
batch_size (int): Desired batch size for splitting
Returns:
list: List of tensors, each with batch_size (except possibly the last one)
"""
# Get original batch size
original_batch_size = tensor.size(0)
# Calculate number of full batches and remaining samples
num_full_batches = original_batch_size // batch_size
remaining_samples = original_batch_size % batch_size
# Split tensor into chunks
batches = []
# Handle full batches
for i in range(num_full_batches):
start_idx = i * batch_size
end_idx = start_idx + batch_size
batch = tensor[start_idx:end_idx]
batches.append(batch)
# Handle remaining samples if any
if remaining_samples > 0:
last_batch = tensor[-remaining_samples:]
batches.append(last_batch)
return batches
class LoadInvSRModels:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"sd_model": (['stabilityai/sd-turbo'],),
"invsr_model": (['noise_predictor_sd_turbo_v5.pth', 'noise_predictor_sd_turbo_v5_diftune.pth'],),
"dtype": (['fp16', 'fp32', 'bf16'], {"default": "fp16"}),
"tiled_vae": ("BOOLEAN", {"default": True}),
},
}
RETURN_TYPES = ("INVSR_PIPE",)
RETURN_NAMES = ("invsr_pipe",)
FUNCTION = "loadmodel"
CATEGORY = "INVSR"
def loadmodel(self, sd_model, invsr_model, dtype, tiled_vae):
match dtype:
case "fp16":
dtype = "torch.float16"
case "fp32":
dtype = "torch.float32"
case "bf16":
dtype = "torch.bfloat16"
cfg_path = os.path.join(
os.path.dirname(__file__), "configs", "sample-sd-turbo.yaml"
)
sd_path = get_folder_paths("diffusers")[0]
try:
ckpt_dir = get_folder_paths("invsr")[0]
except:
ckpt_dir = os.path.join(models_dir, "invsr")
args = Namespace(
bs=1,
chopping_bs=8,
timesteps=None,
num_steps=1,
cfg_path=cfg_path,
sd_path=sd_path,
started_ckpt_dir=ckpt_dir,
tiled_vae=tiled_vae,
color_fix="",
chopping_size=128,
invsr_model=invsr_model
)
configs = get_configs(args)
configs["sd_pipe"]["params"]["torch_dtype"] = dtype
base_sampler = BaseSampler(configs)
return ((base_sampler, invsr_model),)
class InvSRSampler:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"invsr_pipe": ("INVSR_PIPE",),
"images": ("IMAGE",),
"num_steps": ("INT",{"default": 1, "min": 1, "max": 5}),
"cfg": ("FLOAT",{"default": 1.0, "step":0.1}),
# "scale_factor": ("INT",{"default": 4}),
"batch_size": ("INT",{"default": 1}),
"chopping_batch_size": ("INT",{"default": 8}),
"chopping_size": ([128, 256, 512],{"default": 128}),
"color_fix": (['none', 'wavelet', 'ycbcr'], {"default": "none"}),
"seed": ("INT", {"default": 123, "min": 0, "max": 2**32 - 1, "step": 1}),
},
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("image",)
FUNCTION = "process"
CATEGORY = "INVSR"
def process(self, invsr_pipe, images, num_steps, cfg, batch_size, chopping_batch_size, chopping_size, color_fix, seed):
base_sampler, invsr_model = invsr_pipe
if color_fix == "none":
color_fix = ""
cfg_path = os.path.join(
os.path.dirname(__file__), "configs", "sample-sd-turbo.yaml"
)
sd_path = get_folder_paths("diffusers")[0]
try:
ckpt_dir = get_folder_paths("invsr")[0]
except:
ckpt_dir = os.path.join(models_dir, "invsr")
args = Namespace(
bs=batch_size,
chopping_bs=chopping_batch_size,
timesteps=None,
num_steps=num_steps,
cfg_path=cfg_path,
sd_path=sd_path,
started_ckpt_dir=ckpt_dir,
tiled_vae=base_sampler.configs.tiled_vae,
color_fix=color_fix,
chopping_size=chopping_size,
invsr_model=invsr_model
)
configs = get_configs(args, log=True)
configs["cfg_scale"] = cfg
# configs["basesr"]["sf"] = scale_factor
base_sampler.configs = configs
base_sampler.setup_seed(seed)
sampler = InvSamplerSR(base_sampler)
images_bchw = images.permute(0,3,1,2)
og_h, og_w = images_bchw.shape[2:]
# Calculate new dimensions divisible by 16
new_height = ((og_h + 15) // 16) * 16 # Round up to nearest multiple of 16
new_width = ((og_w + 15) // 16) * 16
resized = False
if og_h != new_height or og_w != new_width:
resized = True
print(f"[InvSR] - Image not divisible by 16. Resizing to {new_height} (h) x {new_width} (w)")
images_bchw = F.interpolate(images_bchw, size=(new_height, new_width), mode='bicubic', align_corners=False)
batches = split_tensor_into_batches(images_bchw, batch_size)
results = []
pbar = ProgressBar(len(batches))
for batch in batches:
result = sampler.inference(image_bchw=batch)
results.append(torch.from_numpy(result))
pbar.update(1)
result_t = torch.cat(results, dim=0)
# Resize to original dimensions * 4
if resized:
result_t = F.interpolate(result_t, size=(og_h * 4, og_w * 4), mode='bicubic', align_corners=False)
return (result_t.permute(0,2,3,1),)