Signed-off-by: vladmandic <mandic00@live.com>
This commit is contained in:
vladmandic
2026-03-13 11:42:16 +01:00
parent 1b699557b8
commit ee6cadfa9c
3 changed files with 43 additions and 121 deletions
-1
View File
@@ -691,7 +691,6 @@ def control_run(state: str = '', # pylint: disable=keyword-arg-before-vararg
and getattr(p, 'init_images', None) is None \
and getattr(p, 'image', None) is None:
if is_generator:
log.debug(f'Control args: {p.task_args}')
yield terminate(f'Mode={p.extra_generation_params.get("Control type", None)} input image is none')
return terminate(f'Mode={p.extra_generation_params.get("Control type", None)} input image is none')
if unit_type == 'lite':
+17 -17
View File
@@ -85,16 +85,16 @@ def correction(p, timestep, latent):
latent = soft_clamp_tensor(latent, threshold=p.hdr_threshold, boundary=p.hdr_boundary)
p.extra_generation_params["Latent clamp"] = f'{p.hdr_threshold}/{p.hdr_boundary}'
if 600 < timestep < 900 and p.hdr_color != 0:
n = getattr(p, '_correction_steps_mid', 1)
n = getattr(p, 'correction_steps_mid', 1)
latent[1:] = center_tensor(latent[1:], channel_shift=p.hdr_color / n, full_shift=float(p.hdr_mode))
p.extra_generation_params["Latent color"] = f'{p.hdr_color}'
if 600 < timestep < 900 and p.hdr_tint_ratio != 0:
n = getattr(p, '_correction_steps_mid', 1)
n = getattr(p, 'correction_steps_mid', 1)
latent = color_adjust(latent, p.hdr_color_picker, p.hdr_tint_ratio / n)
p.extra_generation_params["Latent tint"] = f'{p.hdr_tint_ratio}'
p.extra_generation_params["Latent tint color"] = p.hdr_color_picker
if timestep < 200 and (p.hdr_brightness != 0):
n = getattr(p, '_correction_steps_late', 1)
n = getattr(p, 'correction_steps_late', 1)
latent[0:1] = center_tensor(latent[0:1], full_shift=float(p.hdr_mode), offset=p.hdr_brightness / n)
p.extra_generation_params["Latent brightness"] = f'{p.hdr_brightness}'
if timestep < 350 and p.hdr_sharpen != 0:
@@ -179,35 +179,35 @@ def _count_steps_below(pipe, threshold):
def correction_callback(p, timestep, kwargs, pipe=None, initial: bool = False):
if initial:
if not any([p.hdr_clamp, p.hdr_mode, p.hdr_maximize, p.hdr_sharpen, p.hdr_color, p.hdr_brightness, p.hdr_tint_ratio]):
p._correction_skip = True
p.correction_skip = True
return kwargs
# always skip for detailer passes (already-corrected image, different resolution)
if getattr(p, 'recursion', False):
p._correction_skip = True
p.correction_skip = True
return kwargs
# optionally skip for hires pass
if getattr(p, 'is_hr_pass', False) and not getattr(p, 'hdr_apply_hires', True):
p._correction_skip = True
p.correction_skip = True
return kwargs
p._correction_skip = False
p._correction_warned = False
p.correction_skip = False
p.correction_warned = False
if pipe is not None:
p._correction_steps_mid = _count_steps_in_range(pipe, 600, 900)
p._correction_steps_late = _count_steps_below(pipe, 200)
elif getattr(p, '_correction_skip', False):
p.correction_steps_mid = _count_steps_in_range(pipe, 600, 900)
p.correction_steps_late = _count_steps_below(pipe, 200)
elif getattr(p, 'correction_skip', False):
return kwargs
latents = kwargs["latents"]
if len(latents.shape) <= 3: # packed latent
if pipe is None:
if not getattr(p, '_correction_warned', False):
if not getattr(p, 'correction_warned', False):
log.warning(f'Latent correction: shape={latents.shape} packed latent but no pipe reference')
p._correction_warned = True
p.correction_warned = True
return kwargs
unpacked, pack_type = _unpack_latents(latents, pipe, p)
if pack_type == 'unknown':
if not getattr(p, '_correction_warned', False):
if not getattr(p, 'correction_warned', False):
log.warning(f'Latent correction: shape={latents.shape} unknown packed format')
p._correction_warned = True
p.correction_warned = True
return kwargs
for i in range(unpacked.shape[0]):
unpacked[i] = correction(p, timestep, unpacked[i])
@@ -228,7 +228,7 @@ def correction_callback(p, timestep, kwargs, pipe=None, initial: bool = False):
latents = latents.permute(1, 0, 2, 3).unsqueeze(0)
kwargs["latents"] = latents
else:
if not getattr(p, '_correction_warned', False):
if not getattr(p, 'correction_warned', False):
log.warning(f'Latent correction: shape={latents.shape} unknown latent')
p._correction_warned = True
p.correction_warned = True
return kwargs
+26 -103
View File
@@ -10,7 +10,7 @@ from modules.sd_samplers_common import SamplerData, flow_models
debug = os.environ.get('SD_SAMPLER_DEBUG', None) is not None
debug_log = log.trace if debug else lambda *args, **kwargs: None
_scheduler_overrides = {} # set by sd_samplers.create_sampler() before constructor call
scheduler_overrides = {} # set by sd_samplers.create_sampler() before constructor call
# Diffusers schedulers
try:
@@ -345,80 +345,10 @@ samplers_data_diffusers = [
SamplerData('Same as primary', None, [], {}),
]
_sampler_cls = None
_cls_caps = None
def _get_sampler_cls():
"""Lazily build sampler name → scheduler class mapping from samplers_data_diffusers."""
global _sampler_cls # pylint: disable=global-statement
if _sampler_cls is not None:
return _sampler_cls
_sampler_cls = {}
for sd in samplers_data_diffusers:
if sd.constructor is None:
_sampler_cls[sd.name] = None
continue
try:
closure_globals = inspect.getclosurevars(sd.constructor).globals
_sampler_cls[sd.name] = next((v for v in closure_globals.values() if v is not DiffusionSampler and isinstance(v, type)), None)
except Exception:
_sampler_cls[sd.name] = None
return _sampler_cls
def _get_cls_caps():
"""Cache per-sampler capabilities (static, computed once).
Only tracks flow support and is_flow_only — these are the gates
used by get_sampler_compatibility().
"""
global _cls_caps # pylint: disable=global-statement
if _cls_caps is not None:
return _cls_caps
sampler_cls = _get_sampler_cls()
_cls_caps = {}
for name, cls in sampler_cls.items():
if cls is None:
_cls_caps[name] = {'flow': True, 'is_flow_only': False}
continue
is_flow_only = 'FlowMatch' in cls.__name__
if is_flow_only:
flow = True
else:
try:
src = inspect.getsource(cls)
flow = '"flow_prediction"' in src or "'flow_prediction'" in src
except (TypeError, OSError):
flow = False
_cls_caps[name] = {'flow': flow, 'is_flow_only': is_flow_only}
return _cls_caps
def get_sampler_compatibility(model=None):
"""Return {sampler_name: bool} compatibility for the loaded model.
Only the flow/non-flow gate is used: flow models require schedulers
that support flow_prediction, non-flow models reject flow-only schedulers.
The sigmas/scale_noise checks are NOT applied here because some schedulers
(e.g. Res4Lyf) compute flow sigmas internally rather than accepting them
as set_timesteps parameters; the runtime validation in DiffusionSampler
handles API-level mismatches with a fallback.
"""
if model is None:
return {}
default = getattr(model, 'default_scheduler', getattr(model, 'scheduler', None))
if default is None:
return {}
requires_flow = ('FlowMatch' in default.__class__.__name__) or (getattr(default.config, 'prediction_type', None) == 'flow_prediction')
caps = _get_cls_caps()
result = {}
for name, cap in caps.items():
if requires_flow:
result[name] = cap['flow']
else:
result[name] = not cap['is_flow_only']
return result
def get_override(key, default=None):
if key in scheduler_overrides:
return scheduler_overrides[key]
return getattr(shared.opts, key, default)
class DiffusionSampler:
@@ -452,16 +382,9 @@ class DiffusionSampler:
if key in self.config:
self.config[key] = value
# finally apply user preferences (with per-request override support)
overrides = _scheduler_overrides.copy()
def _opt(key, default=None):
if key in overrides:
return overrides[key]
return getattr(shared.opts, key, default)
if _opt('schedulers_prediction_type') != 'default':
self.config['prediction_type'] = _opt('schedulers_prediction_type')
sched_beta = _opt('schedulers_beta_schedule')
if get_override('schedulers_prediction_type') != 'default':
self.config['prediction_type'] = get_override('schedulers_prediction_type')
sched_beta = get_override('schedulers_beta_schedule')
if sched_beta != 'default':
if sched_beta == 'linear':
self.config['beta_schedule'] = 'linear'
@@ -472,9 +395,9 @@ class DiffusionSampler:
elif sched_beta == 'sigmoid':
self.config['beta_schedule'] = 'sigmoid'
timesteps = re.split(',| ', _opt('schedulers_timesteps'))
timesteps = re.split(',| ', get_override('schedulers_timesteps'))
timesteps = [int(x) for x in timesteps if x.isdigit()]
sched_sigma = _opt('schedulers_sigma')
sched_sigma = get_override('schedulers_sigma')
if len(timesteps) == 0:
if 'sigma_schedule' in self.config:
self.config['sigma_schedule'] = sched_sigma if sched_sigma != 'default' else None
@@ -494,37 +417,37 @@ class DiffusionSampler:
pass # timesteps are set using set_timesteps in set_pipeline_args
if 'thresholding' in self.config:
self.config['thresholding'] = _opt('schedulers_use_thresholding')
self.config['thresholding'] = get_override('schedulers_use_thresholding')
if 'lower_order_final' in self.config:
self.config['lower_order_final'] = _opt('schedulers_use_loworder')
if 'solver_order' in self.config and int(_opt('schedulers_solver_order')) > 0:
self.config['solver_order'] = int(_opt('schedulers_solver_order'))
self.config['lower_order_final'] = get_override('schedulers_use_loworder')
if 'solver_order' in self.config and int(get_override('schedulers_solver_order')) > 0:
self.config['solver_order'] = int(get_override('schedulers_solver_order'))
if 'predict_x0' in self.config:
self.config['solver_type'] = _opt('uni_pc_variant')
if 'beta_start' in self.config and _opt('schedulers_beta_start') > 0:
self.config['beta_start'] = _opt('schedulers_beta_start')
if 'beta_end' in self.config and _opt('schedulers_beta_end') > 0:
self.config['beta_end'] = _opt('schedulers_beta_end')
sched_shift = _opt('schedulers_shift')
self.config['solver_type'] = get_override('uni_pc_variant')
if 'beta_start' in self.config and get_override('schedulers_beta_start') > 0:
self.config['beta_start'] = get_override('schedulers_beta_start')
if 'beta_end' in self.config and get_override('schedulers_beta_end') > 0:
self.config['beta_end'] = get_override('schedulers_beta_end')
sched_shift = get_override('schedulers_shift')
if 'shift' in self.config:
self.config['shift'] = sched_shift if sched_shift > 0 else 3
if 'flow_shift' in self.config:
self.config['flow_shift'] = sched_shift if sched_shift > 0 else 3
if 'use_dynamic_shifting' in self.config:
self.config['use_dynamic_shifting'] = True if sched_shift == 0 else _opt('schedulers_dynamic_shift')
self.config['use_dynamic_shifting'] = True if sched_shift == 0 else get_override('schedulers_dynamic_shift')
if 'base_shift' in self.config:
self.config['base_shift'] = _opt('schedulers_base_shift')
self.config['base_shift'] = get_override('schedulers_base_shift')
if 'max_shift' in self.config:
self.config['max_shift'] = _opt('schedulers_max_shift')
self.config['max_shift'] = get_override('schedulers_max_shift')
if 'use_beta_sigmas' in self.config and 'sigma_schedule' in self.config:
self.config['use_beta_sigmas'] = 'StableDiffusion3' in model.__class__.__name__
if 'rescale_betas_zero_snr' in self.config:
self.config['rescale_betas_zero_snr'] = _opt('schedulers_rescale_betas')
sched_ts_spacing = _opt('schedulers_timestep_spacing')
self.config['rescale_betas_zero_snr'] = get_override('schedulers_rescale_betas')
sched_ts_spacing = get_override('schedulers_timestep_spacing')
if 'timestep_spacing' in self.config and sched_ts_spacing != 'default' and sched_ts_spacing is not None:
self.config['timestep_spacing'] = sched_ts_spacing
if 'num_train_timesteps' in self.config:
self.config['num_train_timesteps'] = _opt('schedulers_timesteps_range')
self.config['num_train_timesteps'] = get_override('schedulers_timesteps_range')
if 'EDM' in name:
del self.config['beta_start']
del self.config['beta_end']