Migrated all nodes in "TenserTensor/Postproduction" category to API V3 w/docs
This commit is contained in:
+15
-1
@@ -64,13 +64,13 @@ except ImportError:
|
||||
print(f"{YELLOW}TenserTensor: {RED}ERROR: Loader nodes unavailable{RESET}")
|
||||
|
||||
try:
|
||||
before = set(dir())
|
||||
from .nodes.postproduction.add_film_grain import TT_AddFilmGrain
|
||||
from .nodes.postproduction.apply_lut import TT_ApplyLut
|
||||
from .nodes.postproduction.image_enhancer import TT_ImageEnhancer
|
||||
from .nodes.postproduction.postproduction import TT_Postproduction
|
||||
from .nodes.postproduction.postproduction_advanced import TT_PostproductionAdvanced
|
||||
from .nodes.postproduction.quick_image_upscaler import TT_QuickImageUpscaler
|
||||
from .nodes_postproduction import *
|
||||
except ImportError:
|
||||
print(f"{YELLOW}TenserTensor: {RED}ERROR: Postproduction nodes unavailable{RESET}")
|
||||
|
||||
@@ -192,6 +192,13 @@ NODE_CLASS_MAPPINGS = {
|
||||
"TT_ImageEnhancer": TT_ImageEnhancer,
|
||||
"TT_Postproduction": TT_Postproduction,
|
||||
"TT_PostproductionAdvanced": TT_PostproductionAdvanced,
|
||||
# Postproduction V3 Nodes
|
||||
"TT_AddFilmGrainNode": TT_AddFilmGrainNode,
|
||||
"TT_ApplyLutNode": TT_ApplyLutNode,
|
||||
"TT_ImageEnhancerNode": TT_ImageEnhancerNode,
|
||||
"TT_QuickImageUpscalerNode": TT_QuickImageUpscalerNode,
|
||||
"TT_PostproductionNode": TT_PostproductionNode,
|
||||
"TT_PostproductionAdvancedNode": TT_PostproductionAdvancedNode,
|
||||
# Samplers
|
||||
"TT_KSampler": TT_KSampler,
|
||||
"TT_KSamplerAdvanced": TT_KSamplerAdvanced,
|
||||
@@ -313,6 +320,13 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"TT_ImageEnhancer": "TT Image Enhancer",
|
||||
"TT_Postproduction": "TT Postproduction",
|
||||
"TT_PostproductionAdvanced": "TT Postproduction (Advanced)",
|
||||
# Postproduction V3 Nodes
|
||||
"TT_AddFilmGrainNode": "TT Add Film Grain",
|
||||
"TT_ApplyLutNode": "TT Apply LUT",
|
||||
"TT_ImageEnhancerNode": "TT Image Enhancer",
|
||||
"TT_QuickImageUpscalerNode": "TT Quick Image Upscaler",
|
||||
"TT_PostproductionNode": "TT Postproduction",
|
||||
"TT_PostproductionAdvancedNode": "TT Postproduction (Advanced)",
|
||||
# Samplers
|
||||
"TT_KSampler": "TT KSampler",
|
||||
"TT_KSamplerAdvanced": "TT KSampler (Advanced)",
|
||||
|
||||
@@ -24,7 +24,7 @@ class TT_AddFilmGrain():
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("IMAGE",)
|
||||
FUNCTION = "apply_film_grain"
|
||||
CATEGORY = "TenserTensor/Postproduction"
|
||||
CATEGORY = "TenserTensor/Deprecated/Postproduction"
|
||||
|
||||
def apply_film_grain(self, **kwargs):
|
||||
return (apply_film_grain(
|
||||
|
||||
@@ -23,7 +23,7 @@ class TT_ApplyLut:
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("IMAGE",)
|
||||
FUNCTION = "apply_lut"
|
||||
CATEGORY = "TenserTensor/Postproduction"
|
||||
CATEGORY = "TenserTensor/Deprecated/Postproduction"
|
||||
|
||||
def apply_lut(self, image, lut_file, strength, colorspace):
|
||||
return (
|
||||
|
||||
@@ -24,7 +24,7 @@ class TT_ImageEnhancer():
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("IMAGE",)
|
||||
FUNCTION = "enhance_image"
|
||||
CATEGORY = "TenserTensor/Postproduction"
|
||||
CATEGORY = "TenserTensor/Deprecated/Postproduction"
|
||||
|
||||
def enhance_image(
|
||||
self,
|
||||
|
||||
@@ -45,7 +45,7 @@ class TT_Postproduction():
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("IMAGE",)
|
||||
FUNCTION = "apply_postprocessing"
|
||||
CATEGORY = "TenserTensor/Postproduction"
|
||||
CATEGORY = "TenserTensor/Deprecated/Postproduction"
|
||||
|
||||
def apply_postprocessing(self, **kwargs):
|
||||
image = kwargs.get("image").clone()
|
||||
|
||||
@@ -55,7 +55,7 @@ class TT_PostproductionAdvanced():
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("IMAGE",)
|
||||
FUNCTION = "apply_postprocessing"
|
||||
CATEGORY = "TenserTensor/Postproduction"
|
||||
CATEGORY = "TenserTensor/Deprecated/Postproduction"
|
||||
|
||||
def apply_postprocessing(self, **kwargs):
|
||||
image = kwargs.get("image").clone()
|
||||
|
||||
@@ -24,7 +24,7 @@ class TT_QuickImageUpscaler():
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("IMAGE",)
|
||||
FUNCTION = "upscale_image"
|
||||
CATEGORY = "TenserTensor/Postproduction"
|
||||
CATEGORY = "TenserTensor/Deprecated/Postproduction"
|
||||
|
||||
def upscale_image(self, image, device, upscale_model_name, tile, overlap):
|
||||
timage = image.clone()
|
||||
|
||||
+9
-5
@@ -330,12 +330,15 @@ def load_upscale_model(upscale_model) -> ImageModelDescriptor:
|
||||
BUFFER_FACTOR = 384.0
|
||||
|
||||
|
||||
def upscale(timage, **kwargs):
|
||||
device_name, upscale_tile, upscale_overlap = (
|
||||
kwargs.get("upscaler_device", None),
|
||||
def upscale(**kwargs):
|
||||
image, device_name, upscale_tile, upscale_overlap = (
|
||||
kwargs.get("image"),
|
||||
kwargs.get("upscaler_device"),
|
||||
kwargs.get("upscale_tile"),
|
||||
kwargs.get("upscale_overlap")
|
||||
)
|
||||
|
||||
timage = image.clone()
|
||||
device = get_torch_device(device_name)
|
||||
model = load_upscale_model(kwargs.get("upscale_model"))
|
||||
i_image = timage.movedim(-1, 1).to(device)
|
||||
@@ -408,10 +411,11 @@ class TT_ImagePreviewUpscaleSaveNode(io.ComfyNode):
|
||||
|
||||
@classmethod
|
||||
def execute(cls, **kwargs) -> io.NodeOutput:
|
||||
timage = kwargs.get("image")
|
||||
image = kwargs.get("image")
|
||||
timage = image.clone()
|
||||
|
||||
if kwargs.get("upscale_image"):
|
||||
timage = upscale(timage, **kwargs)
|
||||
timage = upscale(**kwargs)
|
||||
|
||||
if kwargs.get("save_image"):
|
||||
kwargs["image"] = timage
|
||||
|
||||
+353
-20
@@ -1,11 +1,77 @@
|
||||
# (c) TenserTensor <tenser.tensor@proton.me> || Apache-2.0 (apache.org/licenses/LICENSE-2.0)
|
||||
|
||||
from pathlib import Path
|
||||
from typing import override
|
||||
|
||||
import colour
|
||||
import kornia
|
||||
import torch
|
||||
from torch.nn import functional
|
||||
|
||||
import folder_paths
|
||||
from comfy_api.latest import ComfyExtension, io
|
||||
from .nodes_image import DEVICES, RESIZE_METHODS, get_upscale_models, upscale
|
||||
|
||||
CATEGORY = "TenserTensor/Postproduction"
|
||||
|
||||
COLORSPACES = ["linear", "logarithmic"]
|
||||
|
||||
|
||||
def apply_film_grain(**kwargs):
|
||||
image, seed, scale, strength, saturation, mode, antialias, toe = (
|
||||
kwargs.get("image"),
|
||||
kwargs.get("seed"),
|
||||
kwargs.get("scale"),
|
||||
kwargs.get("strength"),
|
||||
kwargs.get("saturation"),
|
||||
kwargs.get("upscale_mode"),
|
||||
kwargs.get("antialias"),
|
||||
kwargs.get("toe"),
|
||||
)
|
||||
timage = image.copy()
|
||||
batch, height, width, channels = timage.shape
|
||||
|
||||
if seed is not None:
|
||||
torch.manual_seed(seed)
|
||||
|
||||
grain = torch.rand(batch, int(height // scale), int(width // scale), 3)
|
||||
ycbcr = kornia.color.rgb_to_ycbcr(grain.movedim(-1, 1)).movedim(1, -1)
|
||||
|
||||
for idx, ksize in enumerate([3, 15, 11]):
|
||||
sigma = (ksize / 3.0,) * 2
|
||||
kernel_size = (ksize,) * 2
|
||||
channel = ycbcr[..., idx:idx + 1].movedim(-1, 1)
|
||||
ycbcr[..., idx] = kornia.filters.gaussian_blur2d(
|
||||
channel,
|
||||
kernel_size=kernel_size,
|
||||
sigma=sigma,
|
||||
border_type='reflect'
|
||||
).movedim(1, -1).squeeze(-1)
|
||||
|
||||
grain = (kornia.color.ycbcr_to_rgb(ycbcr.movedim(-1, 1)).movedim(1, -1) - 0.5) * strength
|
||||
grain[..., 0] *= 2
|
||||
grain[..., 2] *= 3
|
||||
grain += 1
|
||||
|
||||
mono = grain[..., 1:2].expand(-1, -1, -1, 3)
|
||||
grain = grain * saturation + mono * (1 - saturation)
|
||||
|
||||
grain = functional.interpolate(
|
||||
grain.movedim(-1, 1),
|
||||
size=(height, width),
|
||||
mode=mode,
|
||||
antialias=antialias if mode in ['bilinear', 'bicubic'] else False
|
||||
).movedim(1, -1)
|
||||
|
||||
base = image[..., :3]
|
||||
shadow_lift = 1 - toe
|
||||
image[..., :3] = torch.clip(
|
||||
(1 - (1 - base) * grain) * shadow_lift + toe,
|
||||
0.0, 1.0
|
||||
)
|
||||
|
||||
return image
|
||||
|
||||
|
||||
class TT_AddFilmGrainNode(io.ComfyNode):
|
||||
@classmethod
|
||||
@@ -16,37 +82,183 @@ class TT_AddFilmGrainNode(io.ComfyNode):
|
||||
category=CATEGORY,
|
||||
description="",
|
||||
inputs=[
|
||||
|
||||
io.Image.Input("image"),
|
||||
io.Int.Input("seed", default=0, min=0, max=0xffffffffffffffff),
|
||||
io.Float.Input("scale", default=0.25, min=0.05, max=2.0, step=0.05),
|
||||
io.Float.Input("strength", default=0.15, min=0.0, max=10.0, step=0.01),
|
||||
io.Float.Input("saturation", default=0.2, min=0.0, max=2.0, step=0.01),
|
||||
io.Float.Input("toe", default=0.0, min=0.0, max=0.5, step=0.001),
|
||||
io.Combo.Input("upscale_mode", options=RESIZE_METHODS, advanced=True),
|
||||
io.Boolean.Input("antialias", default=True, label_on="Antialiased", label_off="Raw", advanced=True),
|
||||
],
|
||||
outputs=[
|
||||
|
||||
io.Image.Output("IMAGE"),
|
||||
]
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def execute(cls, **kwargs) -> io.NodeOutput:
|
||||
raise NotImplementedError
|
||||
image = apply_film_grain(**kwargs)
|
||||
|
||||
return io.NodeOutput(image)
|
||||
|
||||
|
||||
def get_lut_files_list():
|
||||
base = folder_paths.get_folder_paths("custom_nodes")[0]
|
||||
lut_dir = Path(base) / "ComfyUI-TenserTensor" / "lut"
|
||||
|
||||
if not lut_dir.is_dir():
|
||||
raise FileNotFoundError(f"lut dir not found: {lut_dir}")
|
||||
|
||||
files = [file.name for file in lut_dir.glob("*.cube")]
|
||||
|
||||
return lut_dir, sorted(files)
|
||||
|
||||
|
||||
def color_correction(**kwargs):
|
||||
image, is_log, lut_dir, lut_file, lut_strength = (
|
||||
kwargs.get("image"),
|
||||
kwargs.get("colorspace") == "logarithmic",
|
||||
kwargs.get("lut_dir"),
|
||||
kwargs.get("lut_file"),
|
||||
kwargs.get("lut_strength"),
|
||||
)
|
||||
|
||||
timage = image.clone()
|
||||
orig_np_image = timage.detach().cpu().numpy()
|
||||
np_image = orig_np_image.copy()
|
||||
device, dtype = timage.device, timage.dtype
|
||||
|
||||
if is_log:
|
||||
np_image **= 1 / 2.2
|
||||
|
||||
np_image = colour.read_LUT(str(lut_dir / lut_file)).apply(np_image)
|
||||
|
||||
if is_log:
|
||||
np_image **= 2.2
|
||||
|
||||
if lut_strength < 1.0:
|
||||
np_image = lut_strength * np_image + (1 - lut_strength) * orig_np_image
|
||||
|
||||
return torch.from_numpy(np_image).to(device, dtype)
|
||||
|
||||
|
||||
class TT_ApplyLutNode(io.ComfyNode):
|
||||
LUT_DIR = None
|
||||
|
||||
@classmethod
|
||||
def define_schema(cls) -> io.Schema:
|
||||
cls.LUT_DIR, LUT_FILES = get_lut_files_list()
|
||||
|
||||
return io.Schema(
|
||||
node_id="TT_ApplyLutNode",
|
||||
display_name="TT Apply LUT",
|
||||
category=CATEGORY,
|
||||
description="",
|
||||
inputs=[
|
||||
|
||||
io.Image.Input("image"),
|
||||
io.Combo.Input("lut_file", options=LUT_FILES),
|
||||
io.Float.Input("strength", default=1.0, min=0.0, max=1.0, step=0.01),
|
||||
io.Combo.Input("colorspace", options=COLORSPACES)
|
||||
],
|
||||
outputs=[
|
||||
|
||||
io.Image.Output("IMAGE"),
|
||||
]
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def execute(cls, **kwargs) -> io.NodeOutput:
|
||||
raise NotImplementedError
|
||||
kwargs["lut_dir"] = cls.LUT_DIR
|
||||
image = color_correction(**kwargs)
|
||||
|
||||
return io.NodeOutput(image)
|
||||
|
||||
|
||||
def adjust_brightness(image, factor, clip_output=True):
|
||||
timage = image.movedim(-1, 1)
|
||||
|
||||
return kornia.enhance.adjust_brightness(timage, factor, clip_output).movedim(1, -1)
|
||||
|
||||
|
||||
def adjust_contrast(image, factor, clip_output=True):
|
||||
timage = image.movedim(-1, 1)
|
||||
|
||||
return kornia.enhance.adjust_contrast(timage, factor, clip_output).movedim(1, -1)
|
||||
|
||||
|
||||
def adjust_gamma(image, gamma, gain=1.0):
|
||||
timage = image.movedim(-1, 1)
|
||||
|
||||
return kornia.enhance.adjust_gamma(timage, gamma, gain).movedim(1, -1)
|
||||
|
||||
|
||||
def adjust_hue(image, factor):
|
||||
timage = image.movedim(-1, 1)
|
||||
|
||||
return kornia.enhance.adjust_hue(timage, factor).movedim(1, -1)
|
||||
|
||||
|
||||
def adjust_saturation(image, factor):
|
||||
timage = image.movedim(-1, 1)
|
||||
|
||||
return kornia.enhance.adjust_saturation(timage, factor).movedim(1, -1)
|
||||
|
||||
|
||||
def sharpness(image, factor):
|
||||
timage = image.movedim(-1, 1)
|
||||
|
||||
return kornia.enhance.sharpness(timage, factor).movedim(1, -1)
|
||||
|
||||
|
||||
def posterize(image, bits):
|
||||
timage = image.movedim(-1, 1)
|
||||
|
||||
return kornia.enhance.posterize(timage, bits).movedim(1, -1)
|
||||
|
||||
|
||||
def solarize(image, thresholds=0.5, additions=None):
|
||||
timage = image.movedim(-1, 1)
|
||||
|
||||
return kornia.enhance.solarize(timage, thresholds, additions).movedim(1, -1)
|
||||
|
||||
|
||||
def enhance_image(**kwargs):
|
||||
image = kwargs.get("image")
|
||||
timage = image.clone()
|
||||
|
||||
brightness_factor = kwargs.get("brightness_factor")
|
||||
if brightness_factor != 0.0:
|
||||
timage = adjust_brightness(timage, brightness_factor)
|
||||
|
||||
contrast_factor = kwargs.get("contrast_factor")
|
||||
if contrast_factor != 1.0:
|
||||
timage = adjust_contrast(timage, contrast_factor)
|
||||
|
||||
gamma_factor, gamma_gain = kwargs.get("gamma_factor"), kwargs.get("gamma_gain")
|
||||
if gamma_factor != 1.0:
|
||||
timage = adjust_gamma(timage, gamma_factor, gamma_gain)
|
||||
|
||||
hue_factor = kwargs.get("hue_factor")
|
||||
if hue_factor != 0.0:
|
||||
timage = adjust_hue(timage, hue_factor)
|
||||
|
||||
saturation_factor = kwargs.get("saturation_factor")
|
||||
if saturation_factor != 1.0:
|
||||
timage = adjust_saturation(timage, saturation_factor)
|
||||
|
||||
sharpness_factor = kwargs.get("sharpness_factor")
|
||||
if sharpness_factor != 0.0:
|
||||
timage = sharpness(timage, sharpness_factor)
|
||||
|
||||
posterize_bits = kwargs.get("posterize_bits")
|
||||
if posterize_bits != 8:
|
||||
timage = posterize(timage, posterize_bits)
|
||||
|
||||
solarize_thresholds = kwargs.get("solarize_thresholds")
|
||||
if solarize_thresholds != 1.0:
|
||||
timage = solarize(timage, solarize_thresholds)
|
||||
|
||||
return timage
|
||||
|
||||
|
||||
class TT_ImageEnhancerNode(io.ComfyNode):
|
||||
@@ -58,16 +270,27 @@ class TT_ImageEnhancerNode(io.ComfyNode):
|
||||
category=CATEGORY,
|
||||
description="",
|
||||
inputs=[
|
||||
|
||||
io.Image.Input("image"),
|
||||
io.Float.Input("brightness_factor", default=0.0, min=-1.0, max=1.0, step=0.01),
|
||||
io.Float.Input("contrast_factor", default=1.0, min=0.0, max=5.0, step=0.01),
|
||||
io.Float.Input("gamma_factor", default=1.0, min=0.0, max=3.0, step=0.01),
|
||||
io.Float.Input("gamma_gain", default=1.0, min=0.0, max=3.0, step=0.01),
|
||||
io.Float.Input("hue_factor", default=0.0, min=-torch.pi, max=torch.pi, step=0.01),
|
||||
io.Float.Input("saturation_factor", default=1.0, min=0.0, max=3.0, step=0.01),
|
||||
io.Float.Input("sharpness_factor", default=0.0, min=0.0, max=3.0, step=0.01),
|
||||
io.Int.Input("posterize_bits", default=8, min=-0, max=8),
|
||||
io.Float.Input("solarize_thresholds", default=1.0, min=0.0, max=1.0, step=0.01),
|
||||
],
|
||||
outputs=[
|
||||
|
||||
io.Image.Output("IMAGE"),
|
||||
]
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def execute(cls, **kwargs) -> io.NodeOutput:
|
||||
raise NotImplementedError
|
||||
image = enhance_image(**kwargs)
|
||||
|
||||
return io.NodeOutput(image)
|
||||
|
||||
|
||||
class TT_QuickImageUpscalerNode(io.ComfyNode):
|
||||
@@ -79,58 +302,158 @@ class TT_QuickImageUpscalerNode(io.ComfyNode):
|
||||
category=CATEGORY,
|
||||
description="",
|
||||
inputs=[
|
||||
|
||||
io.Image.Input("image"),
|
||||
io.Combo.Input("upscaler_device", options=DEVICES, advanced=True),
|
||||
io.Combo.Input("upscale_model", options=get_upscale_models(), advanced=True),
|
||||
io.Int.Input("upscale_tile", default=512, min=128, max=4096, step=64, advanced=True),
|
||||
io.Int.Input("upscale_overlap", default=64, min=8, max=256, step=8, advanced=True),
|
||||
],
|
||||
outputs=[
|
||||
|
||||
io.Image.Output("IMAGE"),
|
||||
]
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def execute(cls, **kwargs) -> io.NodeOutput:
|
||||
raise NotImplementedError
|
||||
image = upscale(**kwargs)
|
||||
|
||||
return io.NodeOutput(image)
|
||||
|
||||
|
||||
class TT_PostproductionNode(io.ComfyNode):
|
||||
LUT_DIR = None
|
||||
|
||||
@classmethod
|
||||
def define_schema(cls) -> io.Schema:
|
||||
cls.LUT_DIR, LUT_FILES = get_lut_files_list()
|
||||
|
||||
return io.Schema(
|
||||
node_id="TT_PostproductionNode",
|
||||
display_name="TT Postproduction",
|
||||
category=CATEGORY,
|
||||
description="",
|
||||
inputs=[
|
||||
|
||||
io.Image.Input("image"),
|
||||
io.Boolean.Input("apply_lut", default=True, label_on="Apply LUT", label_off="Skip LUT"),
|
||||
io.Combo.Input("lut_file", options=LUT_FILES),
|
||||
io.Float.Input("lut_strength", default=1.0, min=0.0, max=1.0, step=0.01),
|
||||
io.Combo.Input("colorspace", options=COLORSPACES),
|
||||
io.Boolean.Input("add_film_grain", default=True, label_on="With Grain", label_off="Clean"),
|
||||
io.Int.Input("seed", display_name="grain_seed", default=0, min=0, max=0xffffffffffffffff),
|
||||
io.Float.Input("scale", display_name="grain_scale", default=0.25, min=0.05, max=2.0, step=0.05),
|
||||
io.Float.Input("strength", display_name="grain_strength", default=0.15, min=0.0, max=10.0, step=0.01),
|
||||
io.Float.Input("saturation", display_name="grain_saturation", default=0.2, min=0.0, max=2.0, step=0.01),
|
||||
io.Float.Input("toe", display_name="grain_toe", default=0.0, min=0.0, max=0.5, step=0.001),
|
||||
io.Combo.Input("upscale_mode", display_name="grain_upscale_mode", options=RESIZE_METHODS, advanced=True),
|
||||
io.Boolean.Input("antialias", display_name="grain_antialias", default=True, label_on="Antialiased", label_off="Raw", advanced=True),
|
||||
io.Boolean.Input("upscale_image", default=True, label_on="Upscale image", label_off="Keep size"),
|
||||
io.Combo.Input("upscaler_device", options=DEVICES, advanced=True),
|
||||
io.Combo.Input("upscale_model", options=get_upscale_models(), advanced=True),
|
||||
io.Int.Input("upscale_tile", default=512, min=128, max=4096, step=64, advanced=True),
|
||||
io.Int.Input("upscale_overlap", default=64, min=8, max=256, step=8, advanced=True),
|
||||
],
|
||||
outputs=[
|
||||
|
||||
io.Image.Output("IMAGE"),
|
||||
]
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def execute(cls, **kwargs) -> io.NodeOutput:
|
||||
raise NotImplementedError
|
||||
image = kwargs.get("image")
|
||||
kwargs["image"] = image.clone()
|
||||
|
||||
apply_lut, add_film_grain, upscale_image = (
|
||||
kwargs.get("apply_lut"),
|
||||
kwargs.get("add_film_grain"),
|
||||
kwargs.get("upscale_image"),
|
||||
)
|
||||
|
||||
if apply_lut:
|
||||
kwargs["lut_dir"] = cls.LUT_DIR
|
||||
kwargs["image"] = color_correction(**kwargs)
|
||||
|
||||
if add_film_grain:
|
||||
kwargs["image"] = apply_film_grain(**kwargs)
|
||||
|
||||
if upscale_image:
|
||||
kwargs["image"] = upscale(**kwargs)
|
||||
|
||||
return io.NodeOutput(kwargs.get("image"))
|
||||
|
||||
|
||||
class TT_PostproductionAdvancedNode(io.ComfyNode):
|
||||
LUT_DIR = None
|
||||
|
||||
@classmethod
|
||||
def define_schema(cls) -> io.Schema:
|
||||
cls.LUT_DIR, LUT_FILES = get_lut_files_list()
|
||||
|
||||
return io.Schema(
|
||||
node_id="TT_PostproductionAdvancedNode",
|
||||
display_name="TT Postproduction (Advanced)",
|
||||
category=CATEGORY,
|
||||
description="",
|
||||
inputs=[
|
||||
|
||||
io.Image.Input("image"),
|
||||
io.Boolean.Input("image_enhancer", default=True, label_on="Enhance", label_off="Bypass"),
|
||||
io.Float.Input("brightness_factor", default=0.0, min=-1.0, max=1.0, step=0.01),
|
||||
io.Float.Input("contrast_factor", default=1.0, min=0.0, max=5.0, step=0.01),
|
||||
io.Float.Input("gamma_factor", default=1.0, min=0.0, max=3.0, step=0.01),
|
||||
io.Float.Input("gamma_gain", default=1.0, min=0.0, max=3.0, step=0.01),
|
||||
io.Float.Input("hue_factor", default=0.0, min=-torch.pi, max=torch.pi, step=0.01),
|
||||
io.Float.Input("saturation_factor", default=1.0, min=0.0, max=3.0, step=0.01),
|
||||
io.Float.Input("sharpness_factor", default=0.0, min=0.0, max=3.0, step=0.01),
|
||||
io.Int.Input("posterize_bits", default=8, min=-0, max=8),
|
||||
io.Float.Input("solarize_thresholds", default=1.0, min=0.0, max=1.0, step=0.01),
|
||||
io.Boolean.Input("apply_lut", default=True, label_on="Apply LUT", label_off="Bypass"),
|
||||
io.Combo.Input("lut_file", options=LUT_FILES),
|
||||
io.Float.Input("lut_strength", default=1.0, min=0.0, max=1.0, step=0.01),
|
||||
io.Combo.Input("colorspace", options=COLORSPACES),
|
||||
io.Boolean.Input("add_film_grain", default=True, label_on="Add Grain", label_off="Bypass"),
|
||||
io.Int.Input("seed", display_name="grain_seed", default=0, min=0, max=0xffffffffffffffff),
|
||||
io.Float.Input("scale", display_name="grain_scale", default=0.25, min=0.05, max=2.0, step=0.05),
|
||||
io.Float.Input("strength", display_name="grain_strength", default=0.15, min=0.0, max=10.0, step=0.01),
|
||||
io.Float.Input("saturation", display_name="grain_saturation", default=0.2, min=0.0, max=2.0, step=0.01),
|
||||
io.Float.Input("toe", display_name="grain_toe", default=0.0, min=0.0, max=0.5, step=0.001),
|
||||
io.Combo.Input("upscale_mode", display_name="grain_upscale_mode", options=RESIZE_METHODS, advanced=True),
|
||||
io.Boolean.Input("antialias", display_name="grain_antialias", default=True, label_on="Antialiased", label_off="Raw", advanced=True),
|
||||
io.Boolean.Input("upscale_image", default=True, label_on="Upscale image", label_off="Bypass"),
|
||||
io.Combo.Input("upscaler_device", options=DEVICES, advanced=True),
|
||||
io.Combo.Input("upscale_model", options=get_upscale_models(), advanced=True),
|
||||
io.Int.Input("upscale_tile", default=512, min=128, max=4096, step=64, advanced=True),
|
||||
io.Int.Input("upscale_overlap", default=64, min=8, max=256, step=8, advanced=True),
|
||||
],
|
||||
outputs=[
|
||||
|
||||
io.Image.Output("IMAGE"),
|
||||
]
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def execute(cls, **kwargs) -> io.NodeOutput:
|
||||
raise NotImplementedError
|
||||
image = kwargs.get("image")
|
||||
kwargs["image"] = image.clone()
|
||||
|
||||
image_enhancer, apply_lut, add_film_grain, upscale_image = (
|
||||
kwargs.get("image_enhancer"),
|
||||
kwargs.get("apply_lut"),
|
||||
kwargs.get("add_film_grain"),
|
||||
kwargs.get("upscale_image"),
|
||||
)
|
||||
|
||||
if image_enhancer:
|
||||
kwargs["image"] = enhance_image(**kwargs)
|
||||
|
||||
if apply_lut:
|
||||
kwargs["lut_dir"] = cls.LUT_DIR
|
||||
kwargs["image"] = color_correction(**kwargs)
|
||||
|
||||
if add_film_grain:
|
||||
kwargs["image"] = apply_film_grain(**kwargs)
|
||||
|
||||
if upscale_image:
|
||||
kwargs["image"] = upscale(**kwargs)
|
||||
|
||||
return io.NodeOutput(kwargs.get("image"))
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
@@ -141,7 +464,12 @@ class PostproductionNodesExtension(ComfyExtension):
|
||||
@override
|
||||
async def get_node_list(self) -> list[type[io.ComfyNode]]:
|
||||
return [
|
||||
# TT_Node,
|
||||
TT_AddFilmGrainNode,
|
||||
TT_ApplyLutNode,
|
||||
TT_ImageEnhancerNode,
|
||||
TT_QuickImageUpscalerNode,
|
||||
TT_PostproductionNode,
|
||||
TT_PostproductionAdvancedNode,
|
||||
]
|
||||
|
||||
|
||||
@@ -154,5 +482,10 @@ async def comfy_entrypoint() -> PostproductionNodesExtension:
|
||||
# ==============================================================================
|
||||
|
||||
__all__ = [
|
||||
# "TT_Node",
|
||||
"TT_AddFilmGrainNode",
|
||||
'TT_ApplyLutNode',
|
||||
'TT_ImageEnhancerNode',
|
||||
'TT_QuickImageUpscalerNode',
|
||||
'TT_PostproductionNode',
|
||||
'TT_PostproductionAdvancedNode',
|
||||
]
|
||||
|
||||
+2
-2
@@ -50,7 +50,7 @@ class TT_VaeDecodeTiledNode(io.ComfyNode):
|
||||
io.Latent.Input("latent"),
|
||||
io.Int.Input("tile_width", default=512, min=64, max=4096, step=64),
|
||||
io.Int.Input("tile_height", default=512, min=64, max=4096, step=64),
|
||||
io.Int.Input("overlap", default=512, min=64, max=4096, step=64),
|
||||
io.Int.Input("overlap", default=64, min=8, max=256, step=8),
|
||||
],
|
||||
outputs=[
|
||||
io.Image.Output("IMAGE"),
|
||||
@@ -109,7 +109,7 @@ class TT_VaeEncodeTiledNode(io.ComfyNode):
|
||||
io.Vae.Input("vae"),
|
||||
io.Int.Input("tile_width", default=512, min=64, max=4096, step=64),
|
||||
io.Int.Input("tile_height", default=512, min=64, max=4096, step=64),
|
||||
io.Int.Input("overlap", default=512, min=64, max=4096, step=64),
|
||||
io.Int.Input("overlap", default=64, min=8, max=256, step=8),
|
||||
],
|
||||
outputs=[
|
||||
io.Latent.Output("LATENT"),
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
## TT Add Film Grain *TT_AddFilmGrainNode*
|
||||
|
||||
*This description was generated with AI assistance. If you spot any errors, please create an issue on GitHub.*
|
||||
|
||||
Adds procedural film grain to an image. Grain is generated from a seed for reproducibility and can be tuned for scale, intensity, color, and shadow detail.
|
||||
|
||||
### Inputs
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|----------------|---------|----------|----------------------------------------------------------|
|
||||
| `image` | IMAGE | Yes | Input image |
|
||||
| `seed` | INT | Yes | Grain generation seed |
|
||||
| `scale` | FLOAT | No | Grain particle size, default 0.25 |
|
||||
| `strength` | FLOAT | No | Grain intensity, default 0.15 |
|
||||
| `saturation` | FLOAT | No | Grain color saturation, default 0.2 |
|
||||
| `toe` | FLOAT | No | Shadow grain lift, default 0.0 |
|
||||
| `upscale_mode` | COMBO | No | Interpolation method for grain upscaling (advanced) |
|
||||
| `antialias` | BOOLEAN | No | Antialiased — smooth grain edges; Raw — sharp (advanced) |
|
||||
|
||||
### Outputs
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|-------|----------------------|
|
||||
| `IMAGE` | IMAGE | Image with film grain|
|
||||
|
||||
### Usage
|
||||
|
||||
Lower `scale` for fine grain, higher for coarse. Increase `saturation` for colored grain typical of film stocks. Use `toe` to add grain in shadow areas only.
|
||||
|
||||
---
|
||||
|
||||
[View on GitHub](https://github.com/tenser-tensor/ComfyUI-TenserTensor)
|
||||
@@ -0,0 +1,29 @@
|
||||
## TT Apply LUT *TT_ApplyLutNode*
|
||||
|
||||
*This description was generated with AI assistance. If you spot any errors, please create an issue on GitHub.*
|
||||
|
||||
Applies a .cube LUT file to an image for color grading. Supports linear and logarithmic colorspace workflows.
|
||||
|
||||
### Inputs
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|--------------|-------|----------|---------------------------------------------------------------|
|
||||
| `image` | IMAGE | Yes | Input image |
|
||||
| `lut_file` | COMBO | Yes | LUT file from the node pack's `lut/` folder |
|
||||
| `strength` | FLOAT | No | Blend strength between original and graded image, default 1.0 |
|
||||
| `colorspace` | COMBO | No | Input colorspace — linear or logarithmic |
|
||||
|
||||
### Outputs
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|-------|--------------------|
|
||||
| `IMAGE` | IMAGE | Color graded image |
|
||||
|
||||
### Usage
|
||||
|
||||
Select a `.cube` LUT file and set colorspace to match your image. Use `strength` below 1.0 to blend the grade with the original. Choose `colorspace`
|
||||
based on your LUT file's intended working space.
|
||||
|
||||
---
|
||||
|
||||
[View on GitHub](https://github.com/tenser-tensor/ComfyUI-TenserTensor)
|
||||
@@ -0,0 +1,34 @@
|
||||
## TT Image Enhancer *TT_ImageEnhancerNode*
|
||||
|
||||
*This description was generated with AI assistance. If you spot any errors, please create an issue on GitHub.*
|
||||
|
||||
Applies a range of image enhancement operations in a single node. Each parameter has a neutral default — only active adjustments are applied.
|
||||
|
||||
### Inputs
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------------------|-------|----------|------------------------------------------------------------|
|
||||
| `image` | IMAGE | Yes | Input image |
|
||||
| `brightness_factor` | FLOAT | No | Brightness adjustment, default 0.0 (neutral) |
|
||||
| `contrast_factor` | FLOAT | No | Contrast multiplier, default 1.0 (neutral) |
|
||||
| `gamma_factor` | FLOAT | No | Gamma correction, default 1.0 (neutral) |
|
||||
| `gamma_gain` | FLOAT | No | Gamma gain, default 1.0 (neutral) |
|
||||
| `hue_factor` | FLOAT | No | Hue rotation in radians, default 0.0 (neutral) |
|
||||
| `saturation_factor` | FLOAT | No | Saturation multiplier, default 1.0 (neutral) |
|
||||
| `sharpness_factor` | FLOAT | No | Sharpness intensity, default 0.0 (neutral) |
|
||||
| `posterize_bits` | INT | No | Color depth in bits, default 8 (neutral, no posterization) |
|
||||
| `solarize_thresholds` | FLOAT | No | Solarize threshold, default 1.0 (neutral, no solarization) |
|
||||
|
||||
### Outputs
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|-------|----------------|
|
||||
| `IMAGE` | IMAGE | Enhanced image |
|
||||
|
||||
### Usage
|
||||
|
||||
All parameters default to neutral values — adjustments are only applied when values differ from defaults. Powered by Kornia.
|
||||
|
||||
---
|
||||
|
||||
[View on GitHub](https://github.com/tenser-tensor/ComfyUI-TenserTensor)
|
||||
@@ -0,0 +1,54 @@
|
||||
## TT Postproduction (Advanced) *TT_PostproductionAdvancedNode*
|
||||
|
||||
*This description was generated with AI assistance. If you spot any errors, please create an issue on GitHub.*
|
||||
|
||||
All-in-one postproduction node combining image enhancement, LUT color grading, film grain, and upscaling in a single node. Each stage can be
|
||||
independently enabled or bypassed.
|
||||
|
||||
### Inputs
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------------------|---------|----------|-------------------------------------------------------|
|
||||
| `image` | IMAGE | Yes | Input image |
|
||||
| `image_enhancer` | BOOLEAN | No | Enable image enhancement stage |
|
||||
| `brightness_factor` | FLOAT | No | Brightness adjustment, default 0.0 (neutral) |
|
||||
| `contrast_factor` | FLOAT | No | Contrast multiplier, default 1.0 (neutral) |
|
||||
| `gamma_factor` | FLOAT | No | Gamma correction, default 1.0 (neutral) |
|
||||
| `gamma_gain` | FLOAT | No | Gamma gain, default 1.0 (neutral) |
|
||||
| `hue_factor` | FLOAT | No | Hue rotation in radians, default 0.0 (neutral) |
|
||||
| `saturation_factor` | FLOAT | No | Saturation multiplier, default 1.0 (neutral) |
|
||||
| `sharpness_factor` | FLOAT | No | Sharpness intensity, default 0.0 (neutral) |
|
||||
| `posterize_bits` | INT | No | Color depth in bits, default 8 (neutral) |
|
||||
| `solarize_thresholds` | FLOAT | No | Solarize threshold, default 1.0 (neutral) |
|
||||
| `apply_lut` | BOOLEAN | No | Enable LUT color grading stage |
|
||||
| `lut_file` | COMBO | No | LUT file from the node pack's `lut/` folder |
|
||||
| `lut_strength` | FLOAT | No | LUT blend strength, default 1.0 |
|
||||
| `colorspace` | COMBO | No | Input colorspace — linear or logarithmic |
|
||||
| `add_film_grain` | BOOLEAN | No | Enable film grain stage |
|
||||
| `grain_seed` | INT | No | Grain generation seed |
|
||||
| `grain_scale` | FLOAT | No | Grain particle size, default 0.25 |
|
||||
| `grain_strength` | FLOAT | No | Grain intensity, default 0.15 |
|
||||
| `grain_saturation` | FLOAT | No | Grain color saturation, default 0.2 |
|
||||
| `grain_toe` | FLOAT | No | Shadow grain lift, default 0.0 |
|
||||
| `grain_upscale_mode` | COMBO | No | Grain interpolation method (advanced) |
|
||||
| `grain_antialias` | BOOLEAN | No | Antialiased — smooth grain; Raw — sharp (advanced) |
|
||||
| `upscale_image` | BOOLEAN | No | Enable upscaling stage |
|
||||
| `upscaler_device` | COMBO | No | Device for upscaling inference (advanced) |
|
||||
| `upscale_model` | COMBO | No | Upscale model file (advanced) |
|
||||
| `upscale_tile` | INT | No | Upscale tile size in pixels, default 512 (advanced) |
|
||||
| `upscale_overlap` | INT | No | Upscale tile overlap in pixels, default 64 (advanced) |
|
||||
|
||||
### Outputs
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|-------|----------------------|
|
||||
| `IMAGE` | IMAGE | Post-processed image |
|
||||
|
||||
### Usage
|
||||
|
||||
Stages run in order: enhancement → LUT → grain → upscale. Bypass any stage with its toggle. All enhancement parameters default to neutral values —
|
||||
only active adjustments are applied.
|
||||
|
||||
---
|
||||
|
||||
[View on GitHub](https://github.com/tenser-tensor/ComfyUI-TenserTensor)
|
||||
@@ -0,0 +1,42 @@
|
||||
## TT Postproduction *TT_PostproductionNode*
|
||||
|
||||
*This description was generated with AI assistance. If you spot any errors, please create an issue on GitHub.*
|
||||
|
||||
Streamlined postproduction node combining LUT color grading, film grain, and upscaling. Each stage can be independently enabled or bypassed.
|
||||
|
||||
### Inputs
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|----------------------|---------|----------|-------------------------------------------------------|
|
||||
| `image` | IMAGE | Yes | Input image |
|
||||
| `apply_lut` | BOOLEAN | No | Enable LUT color grading stage |
|
||||
| `lut_file` | COMBO | No | LUT file from the node pack's `lut/` folder |
|
||||
| `lut_strength` | FLOAT | No | LUT blend strength, default 1.0 |
|
||||
| `colorspace` | COMBO | No | Input colorspace — linear or logarithmic |
|
||||
| `add_film_grain` | BOOLEAN | No | Enable film grain stage |
|
||||
| `grain_seed` | INT | No | Grain generation seed |
|
||||
| `grain_scale` | FLOAT | No | Grain particle size, default 0.25 |
|
||||
| `grain_strength` | FLOAT | No | Grain intensity, default 0.15 |
|
||||
| `grain_saturation` | FLOAT | No | Grain color saturation, default 0.2 |
|
||||
| `grain_toe` | FLOAT | No | Shadow grain lift, default 0.0 |
|
||||
| `grain_upscale_mode` | COMBO | No | Grain interpolation method (advanced) |
|
||||
| `grain_antialias` | BOOLEAN | No | Antialiased — smooth grain; Raw — sharp (advanced) |
|
||||
| `upscale_image` | BOOLEAN | No | Enable upscaling stage |
|
||||
| `upscaler_device` | COMBO | No | Device for upscaling inference (advanced) |
|
||||
| `upscale_model` | COMBO | No | Upscale model file (advanced) |
|
||||
| `upscale_tile` | INT | No | Upscale tile size in pixels, default 512 (advanced) |
|
||||
| `upscale_overlap` | INT | No | Upscale tile overlap in pixels, default 64 (advanced) |
|
||||
|
||||
### Outputs
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|-------|----------------------|
|
||||
| `IMAGE` | IMAGE | Post-processed image |
|
||||
|
||||
### Usage
|
||||
|
||||
Stages run in order: LUT → grain → upscale. Bypass any stage with its toggle. Use TT Postproduction (Advanced) if you need image enhancement controls.
|
||||
|
||||
---
|
||||
|
||||
[View on GitHub](https://github.com/tenser-tensor/ComfyUI-TenserTensor)
|
||||
@@ -0,0 +1,30 @@
|
||||
## TT Quick Image Upscaler *TT_QuickImageUpscalerNode*
|
||||
|
||||
*This description was generated with AI assistance. If you spot any errors, please create an issue on GitHub.*
|
||||
|
||||
Upscales an image using a model-based upscaler. All configuration is in advanced inputs — connect an image and run.
|
||||
|
||||
### Inputs
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-------------------|-------|----------|-----------------------------------------------|
|
||||
| `image` | IMAGE | Yes | Input image |
|
||||
| `upscaler_device` | COMBO | No | Device for upscaling inference (advanced) |
|
||||
| `upscale_model` | COMBO | No | Upscale model file (advanced) |
|
||||
| `upscale_tile` | INT | No | Tile size in pixels, default 512 (advanced) |
|
||||
| `upscale_overlap` | INT | No | Tile overlap in pixels, default 64 (advanced) |
|
||||
|
||||
### Outputs
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|-------|----------------|
|
||||
| `IMAGE` | IMAGE | Upscaled image |
|
||||
|
||||
### Usage
|
||||
|
||||
Place upscale model files in `models/upscale_models`. Increase tile size for better quality at the cost of VRAM. Increase overlap to reduce visible
|
||||
tile seams.
|
||||
|
||||
---
|
||||
|
||||
[View on GitHub](https://github.com/tenser-tensor/ComfyUI-TenserTensor)
|
||||
@@ -24,4 +24,4 @@ for img2img or inpainting workflows.
|
||||
|
||||
---
|
||||
|
||||
[View on GitHub](https://github.com/tenser-tensor/ComfyUI-TenserTensor)
|
||||
[View on GitHub](https://github.com/tenser-tensor/ComfyUI-TenserTensor)
|
||||
|
||||
Reference in New Issue
Block a user