Files
mudknight-comfyui-mudknight…/generation.py
T

223 lines
6.6 KiB
Python

#!/usr/bin/env python3
import nodes
import comfy.samplers
import comfy.sample
from . import common
KSAMPLER_INPUTS = {
"sampler_name": (
comfy.samplers.KSampler.SAMPLERS,
{"default": "euler_ancestral_cfg_pp"}
),
"scheduler": (
list(comfy.samplers.KSampler.SCHEDULERS) + ["align_your_steps"],
{"default": "karras"}
),
"steps": ("INT", {
"default": 20,
"min": 1,
"max": 10000
}),
"cfg": ("FLOAT", {
"default": 1.5,
"min": 0.0,
"max": 100.0,
"step": 0.1,
"round": 0.01
}),
"denoise": ("FLOAT", {
"default": 1.0,
"min": 0.0,
"max": 1.0,
"step": 0.01
}),
}
class BaseNode:
"""
Custom base generation node that creates images from either empty
latent or an optional input image. Exposes sampler, scheduler,
steps, CFG, denoise, width, and height parameters.
"""
@classmethod
def INPUT_TYPES(cls):
resolution_selector = nodes.NODE_CLASS_MAPPINGS[
"ResolutionSelector"]
return {
"required": {
"full_pipe": ("FULL_PIPE",),
**KSAMPLER_INPUTS,
"resolution": (
list(resolution_selector.RESOLUTIONS.keys()),
{"default": "832x1216 (2:3)"}
),
"portrait": ("BOOLEAN", {"default": True}),
},
"optional": {
"image": ("IMAGE",),
},
"hidden": {"extra_pnginfo": "EXTRA_PNGINFO"},
}
RETURN_TYPES = ("FULL_PIPE", "IMAGE",)
RETURN_NAMES = ("full_pipe", "image",)
OUTPUT_NODE = True
FUNCTION = "generate"
CATEGORY = "mudknight/custom"
def generate(
self, full_pipe, sampler_name, scheduler, steps, cfg,
denoise, resolution, portrait, image=None, extra_pnginfo=None):
"""Generate image from latent using sampling."""
# Unpack full_pipe
model = full_pipe.get("model")
vae = full_pipe.get("vae")
positive = full_pipe.get("positive")
negative = full_pipe.get("negative")
seed = full_pipe.get("seed")
latent = full_pipe.get("latent")
# Determine latent source
if image is not None:
# Scale image to target resolution
scale_node = common.Node("ImageScaleToTotalPixels")
scaled_image = scale_node.function(image, "lanczos", 1, 1)[0]
# Encode to latent
vae_encode = common.Node("VAEEncode")
latent = vae_encode.function(vae, scaled_image)[0]
elif latent is not None:
pass
else:
# Get resolution
resolution_selector = common.Node("ResolutionSelector")
width, height = resolution_selector.function(
resolution, portrait)
# Create empty latent
empty_latent = common.Node("EmptyLatentImage")
latent = empty_latent.function(width, height, 1)[0]
# Override denoise to 1 if no input image
denoise = 1.0
# Sample latent
sampled_latent = common.sample_latent(
model, positive, negative, seed, sampler_name,
scheduler, steps, cfg, denoise, latent
)
# Decode
vae_decode = common.Node("VAEDecode")
decoded_image = vae_decode.function(vae, sampled_latent)[0]
# Pack back into full_pipe
full_pipe_in = common.Node("FullPipeIn")
result = full_pipe_in.function(full_pipe, image=decoded_image)[0]
return common.return_preview(
(result, decoded_image),
decoded_image,
extra_pnginfo
)
class UpscaleNode:
"""
Custom upscale node that scales an image and processes it through
sampling. Exposes sampler, scheduler, steps, CFG, denoise, and
scale factor parameters.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"full_pipe": ("FULL_PIPE",),
**KSAMPLER_INPUTS,
"scheduler": (
KSAMPLER_INPUTS["scheduler"][0],
{"default": "align_your_steps"}
),
"upscale_model": (common.get_upscale_model_list(),),
"scale_by": ("FLOAT", {
"default": 2.0,
"min": 0.01,
"max": 8.0,
"step": 0.01
}),
},
"optional": {
"image": ("IMAGE",),
},
"hidden": {"extra_pnginfo": "EXTRA_PNGINFO"},
}
RETURN_TYPES = ("FULL_PIPE", "IMAGE",)
RETURN_NAMES = ("full_pipe", "image",)
FUNCTION = "upscale"
CATEGORY = "mudknight/custom"
def upscale(
self, full_pipe, sampler_name, scheduler, steps, cfg,
denoise, upscale_model, scale_by, image=None, extra_pnginfo=None):
"""Upscale and sample image."""
# Unpack full_pipe
model = full_pipe.get("model")
vae = full_pipe.get("vae")
positive = full_pipe.get("positive")
negative = full_pipe.get("negative")
seed = full_pipe.get("seed")
# Use pipe image if no input image provided
if image is None:
image = full_pipe.get("image")
# Scale image
if upscale_model != "none":
image_scale = common.Node("CombinedUpscaleNode")
scaled_image = image_scale.function(
image, upscale_model, scale_by, 'lanczos')[0]
else:
image_scale = common.Node("ImageScaleBy")
scaled_image = image_scale.function(
image, "lanczos", scale_by)[0]
# Encode to latent
vae_encode = common.Node("VAEEncode")
latent = vae_encode.function(vae, scaled_image)[0]
# Sample latent
sampled_latent = common.sample_latent(
model, positive, negative, seed, sampler_name,
scheduler, steps, cfg, denoise, latent
)
# Decode
vae_decode = common.Node("VAEDecode")
decoded_image = vae_decode.function(vae, sampled_latent)[0]
# Pack back into full_pipe
full_pipe_in = common.Node("FullPipeIn")
result = full_pipe_in.function(full_pipe, image=decoded_image)[0]
return common.return_preview(
(result, decoded_image),
decoded_image,
extra_pnginfo
)
NODE_CLASS_MAPPINGS = {
"UpscaleNode": UpscaleNode,
"BaseNode": BaseNode,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"UpscaleNode": "Upscale (full-pipe)",
"BaseNode": "Base (full-pipe)",
}