File size: 7,442 Bytes
695fbf0 dc64aaa 695fbf0 dc64aaa 695fbf0 dc64aaa 695fbf0 dc64aaa 695fbf0 dc64aaa 695fbf0 dc64aaa 695fbf0 dc64aaa 695fbf0 dc64aaa |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
from __future__ import annotations
"""MG_SuperSimple: orchestrates a 1..4 step pipeline over CF -> CADE pairs.
Step 1: CADE with the "Step 1" preset (denoise is forced to 1.0).
Steps 2..N: ControlFusion (CF) with the "Step N" preset, then CADE with the same "Step N" preset.
When custom=True: visible CADE controls (seed/steps/cfg/denoise/sampler/scheduler/clipseg_text) override the corresponding Step presets across all steps (Step 1 still uses denoise=1.0).
When custom=False: CADE values come from Step presets; node UI values are ignored. CF always uses its Step presets (kept minimal here).
Inputs:
model/vae/latent/positive/negative: standard Comfy connectors
control_net: ControlNet module for CF (required)
reference_image/clip_vision: forwarded into CADE (optional)
Outputs:
(LATENT, IMAGE) from the final executed step
"""
import torch
from .mg_cade25_easy import ComfyAdaptiveDetailEnhancer25 as _CADE
from .mg_controlfusion_easy import MG_ControlFusion as _CF
from .mg_cade25_easy import _sampler_names as _sampler_names
from .mg_cade25_easy import _scheduler_names as _scheduler_names
import comfy.model_management as model_management
from ..hard.mg_upscale_module import clear_gpu_and_ram_cache
class MG_SuperSimple:
CATEGORY = "MagicNodes/Easy"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
# High-level pipeline control
"step_count": ("INT", {"default": 4, "min": 1, "max": 4, "tooltip": "Number of steps to run (1..4)."}),
"custom": ("BOOLEAN", {"default": False, "tooltip": "When enabled, CADE UI values below override Step presets across all steps (denoise on Step 1 is still forced to 1.0)."}),
# Connectors
"model": ("MODEL", {}),
"positive": ("CONDITIONING", {}),
"negative": ("CONDITIONING", {}),
"vae": ("VAE", {}),
"latent": ("LATENT", {}),
"control_net": ("CONTROL_NET", {"tooltip": "ControlNet module used by ControlFusion."}),
# Shared CADE surface controls
"seed": ("INT", {"default": 0, "min": 0, "max": 0xFFFFFFFFFFFFFFFF, "control_after_generate": True, "tooltip": "Seed 0 = SmartSeed (Sobol + light probe). Non-zero = fixed seed (deterministic)."}),
"steps": ("INT", {"default": 25, "min": 1, "max": 10000, "tooltip": "KSampler steps for CADE (applies to all steps)."}),
"cfg": ("FLOAT", {"default": 4.5, "min": 0.0, "max": 100.0, "step": 0.1}),
# Denoise is clamped; Step 1 uses 1.0 regardless
"denoise": ("FLOAT", {"default": 0.65, "min": 0.35, "max": 0.9, "step": 0.0001}),
"sampler_name": (_sampler_names(), {"default": _sampler_names()[0]}),
"scheduler": (_scheduler_names(), {"default": "MGHybrid"}),
"clipseg_text": ("STRING", {"default": "hand, feet, face", "multiline": False, "tooltip": "Focus terms for CLIPSeg (comma-separated)."}),
},
"optional": {
"reference_image": ("IMAGE", {}),
"clip_vision": ("CLIP_VISION", {}),
},
}
RETURN_TYPES = ("LATENT", "IMAGE")
RETURN_NAMES = ("LATENT", "IMAGE")
FUNCTION = "run"
def _cade(self,
preset_step: str,
custom_override: bool,
model, vae, positive, negative, latent,
seed: int, steps: int, cfg: float, denoise: float,
sampler_name: str, scheduler: str,
clipseg_text: str,
reference_image=None, clip_vision=None):
# CADE core call mirrors CADEEasyUI -> apply_cade2
lat, img, _s, _c, _d, _mask = _CADE().apply_cade2(
model, vae, positive, negative, latent,
int(seed), int(steps), float(cfg), float(denoise),
str(sampler_name), str(scheduler), 0.0,
preset_step=str(preset_step), custom_override=bool(custom_override),
clipseg_text=str(clipseg_text),
reference_image=reference_image, clip_vision=clip_vision,
)
return lat, img
def _cf(self,
preset_step: str,
image, positive, negative, control_net, vae):
# Keep CF strictly on presets for SuperSimple (no extra UI),
# so pass custom_override=False intentionally.
pos, neg, _prev = _CF().apply(
image=image, positive=positive, negative=negative,
control_net=control_net, vae=vae,
preset_step=str(preset_step), custom_override=False,
)
return pos, neg
def run(self,
step_count, custom,
model, positive, negative, vae, latent, control_net,
seed, steps, cfg, denoise, sampler_name, scheduler, clipseg_text,
reference_image=None, clip_vision=None):
# Cooperative cancel before any heavy work
model_management.throw_exception_if_processing_interrupted()
# Clamp step_count to 1..4
n = int(max(1, min(4, step_count)))
cur_latent = latent
cur_image = None
cur_pos = positive
cur_neg = negative
try:
# Step 1: CADE with Step 1 preset, denoise forced to 1.0
denoise_step1 = 1.0
lat1, img1 = self._cade(
preset_step="Step 1",
custom_override=bool(custom),
model=model, vae=vae, positive=cur_pos, negative=cur_neg, latent=cur_latent,
seed=seed, steps=steps, cfg=cfg, denoise=denoise_step1,
sampler_name=sampler_name, scheduler=scheduler,
clipseg_text=clipseg_text,
reference_image=reference_image, clip_vision=clip_vision,
)
cur_latent, cur_image = lat1, img1
# Steps 2..n: CF -> CADE per step
for i in range(2, n + 1):
# allow user cancel between steps
model_management.throw_exception_if_processing_interrupted()
# ControlFusion on current image/conds
cur_pos, cur_neg = self._cf(
preset_step=f"Step {i}",
image=cur_image, positive=cur_pos, negative=cur_neg,
control_net=control_net, vae=vae,
)
# CADE with shared controls
# If no external reference_image is provided, use the previous step image
# so that reference_clean / CLIP-Vision gating can take effect.
ref_img = reference_image if (reference_image is not None) else cur_image
lat_i, img_i = self._cade(
preset_step=f"Step {i}",
custom_override=bool(custom),
model=model, vae=vae, positive=cur_pos, negative=cur_neg, latent=cur_latent,
seed=seed, steps=steps, cfg=cfg, denoise=denoise,
sampler_name=sampler_name, scheduler=scheduler,
clipseg_text=clipseg_text,
reference_image=ref_img, clip_vision=clip_vision,
)
cur_latent, cur_image = lat_i, img_i
return (cur_latent, cur_image)
finally:
# try to free memory on cancel or normal exit
try:
clear_gpu_and_ram_cache()
except Exception:
pass
|