Migrated all nodes in "TenserTensor/Postproduction" category to API V3 w/docs

This commit is contained in:
TenserTensor
2026-03-13 00:04:33 +03:00
parent eda49e9576
commit dc6d5e2082
17 changed files with 607 additions and 35 deletions
+15 -1
View File
@@ -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)",
+1 -1
View File
@@ -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(
+1 -1
View File
@@ -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 (
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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()
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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"),
+32
View File
@@ -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)
+29
View File
@@ -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)
+34
View File
@@ -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)
+54
View File
@@ -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)
+42
View File
@@ -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)
+30
View File
@@ -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)
+1 -1
View File
@@ -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)