Files
silveroxides-ComfyUI_Sigmoi…/__init__.py
T

93 lines
3.6 KiB
Python

import torch
import math
import logging
import numpy
from comfy.samplers import SchedulerHandler, SCHEDULER_HANDLERS, SCHEDULER_NAMES
def sigmoid_offset_scheduler(model_sampling, steps: int, square_k: float = 1.2, base_c: float = 0.6) -> torch.Tensor:
if not hasattr(model_sampling, 'sigmas') or len(model_sampling.sigmas) < 2:
logging.error("model_sampling object must have a 'sigmas' attribute with at least two values.")
return torch.FloatTensor([1.0, 0.0])
if steps <= 0:
logging.warning(f"Sigmoid scheduler called with steps={steps}. Returning minimal schedule.")
sigma_max_val = float(model_sampling.sigmas[-1])
return torch.FloatTensor([sigma_max_val, 0.0])
total_timesteps = (len(model_sampling.sigmas) -1)
# Generate normalized sequence from 0 to 1 (exclusive)
ts = numpy.linspace(0, 1, steps, endpoint=False)
# Apply sigmoid transformation
# Normalize base_c to center around 0 for shifting
shift = 2.0 * (base_c - 0.5) # Maps 0-1 to -1 to 1
def sigmoid(x, k=square_k, shift=shift):
# Convert from [0,1] to [-4,4] range for good sigmoid coverage
x = 8.0 * x - 4.0
# Apply shift from base_c
x = x + (shift * 4.0) # Scale shift by width of range
# Apply sigmoid with steepness k
if k * x > 700:
return 1.0
elif k * x < -700:
return 0.0
else:
return 1.0 / (1.0 + numpy.exp(-k * x))
# Transform using sigmoid and flip (1-x) for correct mapping
transformed_ts = 1.0 - numpy.array([sigmoid(t) for t in ts])
# Map to timesteps in the model_sampling.sigmas range
mapped_ts = numpy.rint(transformed_ts * total_timesteps).astype(int)
# Collect sigma values
sigs = []
last_t = -1
for t in mapped_ts:
if t != last_t:
if isinstance(model_sampling.sigmas, torch.Tensor):
sigs.append(float(model_sampling.sigmas[t].item()))
else: # Assume list or numpy array
sigs.append(float(model_sampling.sigmas[t]))
last_t = t
# Add final 0.0
# sigs.append(0.0)
return torch.FloatTensor(sigs)
new_scheduler_name = "sigmoid_offset"
if new_scheduler_name not in SCHEDULER_HANDLERS:
sigmoid_offset_handler = SchedulerHandler(handler=sigmoid_offset_scheduler, use_ms=True)
SCHEDULER_HANDLERS[new_scheduler_name] = sigmoid_offset_handler
SCHEDULER_NAMES.append(new_scheduler_name)
class SigmoidOffsetScheduler:
@classmethod
def INPUT_TYPES(s):
return {"required":
{"model": ("MODEL",),
"steps": ("INT", {"default": 30, "min": 1, "max": 10000}),
"square_k": ("FLOAT", {"default": 1.2, "min": 0.0, "max": 5.0, "step":0.01, "round": False, "tooltip": "Sigmoid steepness factor. Higher values = steeper transition."}),
"base_c": ("FLOAT", {"default": 0.6, "min": -5.0, "max": 5.0, "step":0.01, "round": False, "tooltip": "Control parameter (0.0-1.0) that shifts sigmoid curve. '< 0.5: More time at low sigmas (early denoising)' and '> 0.5: More time at high sigmas (late denoising)'."}),
}
}
RETURN_TYPES = ("SIGMAS",)
CATEGORY = "sampling/custom_sampling/schedulers"
FUNCTION = "get_sigmas"
def get_sigmas(self, model, steps, square_k, base_c):
sigmas = sigmoid_offset_scheduler(model.get_model_object("model_sampling"), steps, square_k=square_k, base_c=base_c)
return (sigmas, )
NODE_CLASS_MAPPINGS = {
"SigmoidOffsetScheduler": SigmoidOffsetScheduler,
}