From cfa8a8efb32c9a6fcb0897562c248198baf0daef Mon Sep 17 00:00:00 2001 From: DuckFace <149963087+DevDuckFace@users.noreply.github.com> Date: Mon, 26 Jan 2026 04:23:24 -0300 Subject: [PATCH] 1.0 --- README.md | 92 ++++++++++++++++++++++++++++++++++++ __init__.py | 7 +++ nodes.py | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 232 insertions(+) create mode 100644 README.md create mode 100644 __init__.py create mode 100644 nodes.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..e1736cc --- /dev/null +++ b/README.md @@ -0,0 +1,92 @@ +# KSampler DF (with Refiner) + +A custom ComfyUI node that splits sampling steps between a main sampler and a refiner sampler, with independent denoise control for each phase. + +## Features + +- **Split Steps**: Divide total steps between main sampler and refiner sampler +- **Dual Sampler Support**: Use different samplers/schedulers for main and refiner phases +- **Independent Denoise Control**: Separate denoise parameters for main and refiner phases +- **Refiner Intensity**: Control how much the refiner affects the final result (0.0 to 1.0) + +## Installation + +### Method 1: Git Clone (Recommended) + +1. Open a terminal/command prompt +2. Navigate to your ComfyUI custom nodes folder: + ```bash + cd ComfyUI/custom_nodes + ``` +3. Clone this repository: + ```bash + git clone https://github.com/YOUR_USERNAME/ksamplerwithrefiner.git + ``` +4. Restart ComfyUI + +### Method 2: Manual Installation + +1. Download this repository as a ZIP file +2. Extract the contents to `ComfyUI/custom_nodes/ksamplerwithrefiner/` +3. Restart ComfyUI + +## Usage + +Find the node in the **Add Node** menu under: `sampling` → `KSampler DF (with Refiner)` + +### Parameters + +| Parameter | Description | +|-----------|-------------| +| **model** | The model used for denoising | +| **positive** | Positive conditioning (prompt) | +| **negative** | Negative conditioning (negative prompt) | +| **latent_image** | The latent image to denoise | +| **seed** | Random seed for noise generation | +| **steps** | Total number of steps (split between main and refiner) | +| **cfg** | Classifier-Free Guidance scale | +| **sampler_name** | Sampler algorithm for the main phase | +| **scheduler** | Scheduler for the main phase | +| **denoise** | Denoise amount for the main phase (0.0 to 1.0) | +| **refiner_at_step** | Step at which to switch from main to refiner | +| **sampler_refiner** | Sampler algorithm for the refiner phase | +| **scheduler_refiner** | Scheduler for the refiner phase | +| **denoise_refiner** | Denoise amount for the refiner phase (0.0 to 1.0) | + +### Example + +With these settings: +- **steps**: 20 +- **refiner_at_step**: 12 + +The node will: +1. Run **12 steps** with the main sampler/scheduler +2. Run **8 steps** with the refiner sampler/scheduler + +### Refiner Denoise Control + +The `denoise_refiner` parameter controls how much the refiner phase affects the image: + +| Value | Effect | +|-------|--------| +| `0.0` | Refiner is skipped (only main sampler runs) | +| `0.5` | Refiner applies 50% denoising | +| `1.0` | Refiner applies full denoising | + +## File Structure + +``` +ksamplerwithrefiner/ +├── __init__.py +├── nodes.py +└── README.md +``` + +## Requirements + +- ComfyUI (latest version recommended) +- No additional dependencies required + +## License + +MIT License - Feel free to use and modify as needed. \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..3bab699 --- /dev/null +++ b/__init__.py @@ -0,0 +1,7 @@ +# KSampler with Refiner Custom Node for ComfyUI +# Custom node that implements a KSampler with refiner functionality +# Allows using different samplers for main and refiner phases + +from .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS + +__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS'] diff --git a/nodes.py b/nodes.py new file mode 100644 index 0000000..700afe1 --- /dev/null +++ b/nodes.py @@ -0,0 +1,133 @@ +from __future__ import annotations +import torch +import comfy.samplers +import comfy.sample +import comfy.utils +import comfy.model_management +from comfy.comfy_types import IO, ComfyNodeABC, InputTypeDict + +class KsamplerDF(ComfyNodeABC): + """ + Custom KSampler with Refiner functionality for ComfyUI. + Splits steps between main sampler and refiner sampler. + Example: 12 total steps with refiner at step 6 = 6 main + 6 refiner steps. + """ + + @classmethod + def INPUT_TYPES(cls) -> InputTypeDict: + return { + "required": { + "model": (IO.MODEL, {"tooltip": "The model used for denoising the input latent."}), + "positive": (IO.CONDITIONING, {"tooltip": "The conditioning describing the attributes to include."}), + "negative": (IO.CONDITIONING, {"tooltip": "The conditioning describing the attributes to exclude."}), + "latent_image": (IO.LATENT, {"tooltip": "The latent image to denoise."}), + "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "control_after_generate": True, "tooltip": "The random seed used for creating the noise."}), + "steps": ("INT", {"default": 20, "min": 1, "max": 10000, "tooltip": "Total number of steps (main + refiner)."}), + "cfg": ("FLOAT", {"default": 7.0, "min": 0.0, "max": 100.0, "step": 0.1, "round": 0.01, "tooltip": "The Classifier-Free Guidance scale."}), + "sampler_name": (comfy.samplers.KSampler.SAMPLERS, {"default": "euler", "tooltip": "The algorithm used for the main sampling phase."}), + "scheduler": (comfy.samplers.KSampler.SCHEDULERS, {"default": "normal", "tooltip": "The scheduler for the main sampling phase."}), + "denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01, "tooltip": "Main denoise amount."}), + "refiner_at_step": ("INT", {"default": 10, "min": 0, "max": 10000, "tooltip": "Step at which to switch to refiner sampler."}), + "sampler_refiner": (comfy.samplers.KSampler.SAMPLERS, {"default": "euler", "tooltip": "The algorithm used for the refiner phase."}), + "scheduler_refiner": (comfy.samplers.KSampler.SCHEDULERS, {"default": "normal", "tooltip": "The scheduler for the refiner phase."}), + "denoise_refiner": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01, "tooltip": "Refiner denoise - controls how much the refiner interferes (0.0 = no effect, 1.0 = full effect)."}), + }, + } + + RETURN_TYPES = (IO.LATENT,) + OUTPUT_TOOLTIPS = ("The denoised latent image.",) + FUNCTION = "sample" + CATEGORY = "sampling" + DESCRIPTION = "KSampler with Refiner - Splits steps between main sampler and refiner sampler with independent denoise control." + + def sample(self, model, positive, negative, latent_image, seed, steps, cfg, + sampler_name, scheduler, denoise, refiner_at_step, sampler_refiner, + scheduler_refiner, denoise_refiner): + + import nodes + + # Clamp refiner_at_step to valid range + refiner_at_step = max(0, min(refiner_at_step, steps)) + + # Calculate steps for each phase + main_steps = refiner_at_step + refiner_steps = steps - refiner_at_step + + current_latent = latent_image + + # Phase 1: Main sampling (if main_steps > 0) + if main_steps > 0: + print(f"[KSampler DF] Main phase: {main_steps} steps with {sampler_name}/{scheduler}, denoise={denoise}") + result = nodes.common_ksampler( + model=model, + seed=seed, + steps=main_steps, + cfg=cfg, + sampler_name=sampler_name, + scheduler=scheduler, + positive=positive, + negative=negative, + latent=current_latent, + denoise=denoise, + disable_noise=False, + start_step=None, + last_step=None, + force_full_denoise=False # Don't fully denoise, refiner continues + ) + current_latent = result[0] + + # Phase 2: Refiner sampling (if refiner_steps > 0 and denoise_refiner > 0) + if refiner_steps > 0 and denoise_refiner > 0: + print(f"[KSampler DF] Refiner phase: {refiner_steps} steps with {sampler_refiner}/{scheduler_refiner}, denoise={denoise_refiner}") + result = nodes.common_ksampler( + model=model, + seed=seed, + steps=refiner_steps, + cfg=cfg, + sampler_name=sampler_refiner, + scheduler=scheduler_refiner, + positive=positive, + negative=negative, + latent=current_latent, + denoise=denoise_refiner, + disable_noise=False, + start_step=None, + last_step=None, + force_full_denoise=True # Final phase, full denoise + ) + current_latent = result[0] + elif main_steps > 0: + # If no refiner, we need to do a final denoise on main result + # Run one more step to finalize + print(f"[KSampler DF] Finalizing without refiner...") + result = nodes.common_ksampler( + model=model, + seed=seed, + steps=1, + cfg=cfg, + sampler_name=sampler_name, + scheduler=scheduler, + positive=positive, + negative=negative, + latent=current_latent, + denoise=0.0, + disable_noise=True, + start_step=None, + last_step=None, + force_full_denoise=True + ) + current_latent = result[0] + + print(f"[KSampler DF] Complete! Total: {steps} steps ({main_steps} main + {refiner_steps} refiner)") + + return (current_latent,) + + +# Register the node +NODE_CLASS_MAPPINGS = { + "KsamplerDF": KsamplerDF, +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "KsamplerDF": "KSampler DF (with Refiner)", +}