From cd84cfec11b6703a110c8dea1f20ab83ba44ae2c Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Tue, 15 Jul 2025 14:35:27 -0400 Subject: [PATCH] xyzgrid with control tab Signed-off-by: Vladimir Mandic --- CHANGELOG.md | 9 +++++ modules/control/processors.py | 2 + modules/control/processors_list.py | 23 +++++++++++ modules/control/run.py | 12 +++--- modules/control/unit.py | 4 +- modules/control/units/controlnet.py | 29 +++++++------- modules/control/units/detect.py | 58 ++++++++++++++++++--------- modules/processing.py | 8 ++-- modules/processing_class.py | 1 + modules/scripts_manager.py | 12 +++++- scripts/apg.py | 20 ++++----- scripts/pulid_ext.py | 20 ++++----- scripts/skip_layer_guidance.py | 16 ++++---- scripts/{ => xyz}/xyz_grid_classes.py | 51 ++++++++++++++++++++++- scripts/{ => xyz}/xyz_grid_draw.py | 0 scripts/{ => xyz}/xyz_grid_shared.py | 45 +++++++++++++++++++++ scripts/xyz_grid.py | 8 ++-- scripts/xyz_grid_on.py | 6 +-- 18 files changed, 243 insertions(+), 81 deletions(-) create mode 100644 modules/control/processors_list.py rename scripts/{ => xyz}/xyz_grid_classes.py (90%) rename scripts/{ => xyz}/xyz_grid_draw.py (100%) rename scripts/{ => xyz}/xyz_grid_shared.py (81%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c9c9fce7..b71dcba9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -90,6 +90,14 @@ Although upgrades and existing installations are tested and should work fine! - SD.Next now starts with *locked* state preventing model loading until startup is complete - warn when modifying legacy settings that are no longer supported, but available for compatibilty - warn on incompatible sampler and automatically restore default sampler + - **XYZ grid** can now work with control tab: + if controlnet/processor are selected in control tab main interface, they will remain active for duration of xyz grid generation + if controlnet/processor are not selected, you can select them in xyz grid + note that controlnet/processor must be selected as pair, so either both selected in main controls or xyz grid or none selected, + you cannot have processor from control tab and controlnet from xyz grid + when using controlnet/processor selected in xyz grid, behavior is forced as control-only + when using controlnet/processor selected in xyz grid, you can only have one controlnet/processor active at the time + also freely selectable are control strength, start and end values - **API** - add `/sdapi/v1/lock-checkpoint` endpoint that can be used to lock/unlock model changes if model is locked, it cannot be changed using normal load or unload methods @@ -108,6 +116,7 @@ Although upgrades and existing installations are tested and should work fine! - allow upscaling with models that have implicit VAE processing - sdnq use inference context during quantization - framepack improve offloading + - improve scripts error handling - improve infotext param parsing - improve extensions ui search - improve model type autodetection diff --git a/modules/control/processors.py b/modules/control/processors.py index 31e325abf..cef834bac 100644 --- a/modules/control/processors.py +++ b/modules/control/processors.py @@ -235,6 +235,8 @@ class Processor(): if image_input is None: # log.error('Control Processor: no input') return image_process + if isinstance(image_input, list): + image_input = image_input[0] if self.processor_id not in config: return image_process if config[self.processor_id].get('dirty', False): diff --git a/modules/control/processors_list.py b/modules/control/processors_list.py new file mode 100644 index 000000000..b9315d2d2 --- /dev/null +++ b/modules/control/processors_list.py @@ -0,0 +1,23 @@ +processors = [ + 'None', + 'OpenPose', + 'DWPose', + 'MediaPipe Face', + 'Canny', + 'Edge', + 'LineArt Realistic', + 'LineArt Anime', + 'HED', + 'PidiNet', + 'Midas Depth Hybrid', + 'Leres Depth', + 'Zoe Depth', + 'Marigold Depth', + 'Normal Bae', + 'SegmentAnything', + 'MLSD', + 'Shuffle', + 'DPT Depth Hybrid', + 'GLPN Depth', + 'Depth Anything', +] diff --git a/modules/control/run.py b/modules/control/run.py index 704a1889d..d2bf931bd 100644 --- a/modules/control/run.py +++ b/modules/control/run.py @@ -51,8 +51,7 @@ def is_unified_model(): return shared.sd_model.__class__.__name__ in unified_models -def set_pipe(p, has_models, unit_type, selected_models, active_model, active_strength, control_conditioning, control_guidance_start, control_guidance_end, inits): - print('HERE SET PIPE') +def set_pipe(p, has_models, unit_type, selected_models, active_model, active_strength, control_conditioning, control_guidance_start, control_guidance_end, inits=None): global pipe, instance # pylint: disable=global-statement pipe = None if has_models: @@ -119,12 +118,13 @@ def set_pipe(p, has_models, unit_type, selected_models, active_model, active_str p.strength = active_strength[0] pipe = shared.sd_model instance = None + if (pipe is not None) and (pipe.__class__.__name__ != shared.sd_model.__class__.__name__): + sd_models.copy_diffuser_options(pipe, shared.sd_model) # copy options from original pipeline debug_log(f'Control: run type={unit_type} models={has_models} pipe={pipe.__class__.__name__ if pipe is not None else None}') return pipe def check_active(p, unit_type, units): - print('HERE CHECK ACTIVE') active_process: List[processors.Processor] = [] # all active preprocessors active_model: List[Union[controlnet.ControlNet, xs.ControlNetXS, t2iadapter.Adapter]] = [] # all active models active_strength: List[float] = [] # strength factors for all active models @@ -194,7 +194,6 @@ def check_active(p, unit_type, units): def check_enabled(p, unit_type, units, active_model, active_strength, active_start, active_end): - print('HERE CHECK ENABLED') has_models = False selected_models: List[Union[controlnet.ControlNetModel, xs.ControlNetXSModel, t2iadapter.AdapterModel]] = None control_conditioning = None @@ -233,7 +232,6 @@ def control_set(kwargs): def init_units(units: List[unit.Unit]): - print('HERE INIT UNITS') for u in units: if not u.enabled: continue @@ -397,8 +395,6 @@ def control_run(state: str = '', # pylint: disable=keyword-arg-before-vararg elif p.enable_hr and (p.hr_upscale_to_x == 0 or p.hr_upscale_to_y == 0): p.hr_upscale_to_x, p.hr_upscale_to_y = 8 * int(p.hr_resize_x / 8), 8 * int(hr_resize_y / 8) - if is_unified_model(): - p.init_images = inputs global p_extra_args # pylint: disable=global-statement for k, v in p_extra_args.items(): @@ -420,6 +416,8 @@ def control_run(state: str = '', # pylint: disable=keyword-arg-before-vararg info_txt = [] p.is_tile = p.is_tile and has_models + if is_unified_model(): + p.init_images = inputs pipe = set_pipe(p, has_models, unit_type, selected_models, active_model, active_strength, control_conditioning, control_guidance_start, control_guidance_end, inits) debug_log(f'Control pipeline: class={pipe.__class__.__name__} args={vars(p)}') diff --git a/modules/control/unit.py b/modules/control/unit.py index 4af0fdd22..ead89a647 100644 --- a/modules/control/unit.py +++ b/modules/control/unit.py @@ -18,9 +18,9 @@ unit_types = ['t2i adapter', 'controlnet', 'xs', 'lite', 'reference', 'ip'] class Unit(): # mashup of gradio controls and mapping to actual implementation classes def update_choices(self, model_id=None): name = model_id or self.model_name - if name == 'InstantX Union': + if name == 'InstantX Union F1': self.choices = ['canny', 'tile', 'depth', 'blur', 'pose', 'gray', 'lq'] - elif name == 'Shakker-Labs Union': + elif name == 'Shakker-Labs Union F1': self.choices = ['canny', 'tile', 'depth', 'blur', 'pose', 'gray', 'lq'] elif name == 'Xinsir Union XL': self.choices = ['openpose', 'depth', 'scribble', 'canny', 'normal'] diff --git a/modules/control/units/controlnet.py b/modules/control/units/controlnet.py index c7285b0a3..8a5a46aee 100644 --- a/modules/control/units/controlnet.py +++ b/modules/control/units/controlnet.py @@ -77,19 +77,19 @@ predefined_sdxl = { # 'StabilityAI Sketch R256': 'stabilityai/control-lora/control-LoRAs-rank256/control-lora-sketch-rank256.safetensors', } predefined_f1 = { - "InstantX Union": 'InstantX/FLUX.1-dev-Controlnet-Union', - "InstantX Canny": 'InstantX/FLUX.1-dev-Controlnet-Canny', - "JasperAI Depth": 'jasperai/Flux.1-dev-Controlnet-Depth', - "BlackForrestLabs Canny LoRA": '/huggingface.co/black-forest-labs/FLUX.1-Canny-dev-lora/flux1-canny-dev-lora.safetensors', - "BlackForrestLabs Depth LoRA": '/huggingface.co/black-forest-labs/FLUX.1-Depth-dev-lora/flux1-depth-dev-lora.safetensors', - "JasperAI Surface Normals": 'jasperai/Flux.1-dev-Controlnet-Surface-Normals', - "JasperAI Upscaler": 'jasperai/Flux.1-dev-Controlnet-Upscaler', - "Shakker-Labs Union": 'Shakker-Labs/FLUX.1-dev-ControlNet-Union-Pro', - "Shakker-Labs Pose": 'Shakker-Labs/FLUX.1-dev-ControlNet-Pose', - "Shakker-Labs Depth": 'Shakker-Labs/FLUX.1-dev-ControlNet-Depth', - "XLabs-AI Canny": 'XLabs-AI/flux-controlnet-canny-diffusers', - "XLabs-AI Depth": 'XLabs-AI/flux-controlnet-depth-diffusers', - "XLabs-AI HED": 'XLabs-AI/flux-controlnet-hed-diffusers' + "InstantX Union F1": 'InstantX/FLUX.1-dev-Controlnet-Union', + "InstantX Canny F1": 'InstantX/FLUX.1-dev-Controlnet-Canny', + "JasperAI Depth F1": 'jasperai/Flux.1-dev-Controlnet-Depth', + "BlackForrestLabs Canny LoRA F1": '/huggingface.co/black-forest-labs/FLUX.1-Canny-dev-lora/flux1-canny-dev-lora.safetensors', + "BlackForrestLabs Depth LoRA F1": '/huggingface.co/black-forest-labs/FLUX.1-Depth-dev-lora/flux1-depth-dev-lora.safetensors', + "JasperAI Surface Normals F1": 'jasperai/Flux.1-dev-Controlnet-Surface-Normals', + "JasperAI Upscaler F1": 'jasperai/Flux.1-dev-Controlnet-Upscaler', + "Shakker-Labs Union F1": 'Shakker-Labs/FLUX.1-dev-ControlNet-Union-Pro', + "Shakker-Labs Pose F1": 'Shakker-Labs/FLUX.1-dev-ControlNet-Pose', + "Shakker-Labs Depth F1": 'Shakker-Labs/FLUX.1-dev-ControlNet-Depth', + "XLabs-AI Canny F1": 'XLabs-AI/flux-controlnet-canny-diffusers', + "XLabs-AI Depth F1": 'XLabs-AI/flux-controlnet-depth-diffusers', + "XLabs-AI HED F1": 'XLabs-AI/flux-controlnet-hed-diffusers' } predefined_sd3 = { "StabilityAI Canny SD35": 'diffusers-internal-dev/sd35-controlnet-canny-8b', @@ -452,6 +452,7 @@ class ControlNetPipeline(): debug_log(f'Control {what} pipeline: class={self.pipeline.__class__.__name__} time={t1-t0:.2f}') def restore(self): - self.pipeline.unload_lora_weights() + if self.pipeline is not None: + self.pipeline.unload_lora_weights() self.pipeline = None return self.orig_pipeline diff --git a/modules/control/units/detect.py b/modules/control/units/detect.py index 7cc8d144d..1d95efb41 100644 --- a/modules/control/units/detect.py +++ b/modules/control/units/detect.py @@ -1,31 +1,51 @@ import diffusers.pipelines as p -def is_sd15(model): +def is_compatible(model, compatible): if model is None: return False - if hasattr(model, '__name__'): - return model.__name__ == p.StableDiffusionPipeline.__name__ or model.__name__ == p.StableDiffusionImg2ImgPipeline.__name__ or model.__name__ == p.StableDiffusionInpaintPipeline.__name__ - return isinstance(model, p.StableDiffusionPipeline) or isinstance(model, p.StableDiffusionImg2ImgPipeline) or isinstance(model, p.StableDiffusionInpaintPipeline) + if hasattr(model, '__class__'): + return any(model.__class__.__name__ == c.__name__ for c in compatible) + return any(isinstance(model, c) for c in compatible) + + +def is_sd15(model): + compatible = [ + p.StableDiffusionPipeline, + p.StableDiffusionImg2ImgPipeline, + p.StableDiffusionInpaintPipeline, + p.StableDiffusionControlNetPipeline, + ] + return is_compatible(model, compatible) def is_sdxl(model): - if model is None: - return False - if hasattr(model, '__name__'): - return model.__name__ == p.StableDiffusionXLPipeline.__name__ or model.__name__ == p.StableDiffusionXLImg2ImgPipeline.__name__ or model.__name__ == p.StableDiffusionXLInpaintPipeline.__name__ - return isinstance(model, p.StableDiffusionXLPipeline) or isinstance(model, p.StableDiffusionXLImg2ImgPipeline) or isinstance(model, p.StableDiffusionXLInpaintPipeline) + compatible = [ + p.StableDiffusionXLPipeline, + p.StableDiffusionXLImg2ImgPipeline, + p.StableDiffusionXLInpaintPipeline, + p.StableDiffusionXLControlNetPipeline, + p.StableDiffusionXLControlNetImg2ImgPipeline, + p.StableDiffusionXLControlNetUnionPipeline, + ] + return is_compatible(model, compatible) + def is_f1(model): - if model is None: - return False - if hasattr(model, '__name__'): - return model.__name__ == p.FluxPipeline.__name__ or model.__name__ == p.FluxImg2ImgPipeline.__name__ or model.__name__ == p.FluxInpaintPipeline.__name__ - return isinstance(model, p.FluxPipeline) or isinstance(model, p.FluxImg2ImgPipeline) or isinstance(model, p.FluxInpaintPipeline) + compatible = [ + p.FluxPipeline, + p.FluxImg2ImgPipeline, + p.FluxInpaintPipeline, + p.FluxControlNetPipeline, + ] + return is_compatible(model, compatible) + def is_sd3(model): - if model is None: - return False - if hasattr(model, '__name__'): - return model.__name__ == p.StableDiffusion3Pipeline.__name__ or model.__name__ == p.StableDiffusion3Img2ImgPipeline.__name__ or model.__name__ == p.StableDiffusion3InpaintPipeline.__name__ - return isinstance(model, p.StableDiffusion3Pipeline) or isinstance(model, p.StableDiffusion3Img2ImgPipeline) or isinstance(model, p.StableDiffusion3InpaintPipeline) + compatible = [ + p.StableDiffusion3Pipeline, + p.StableDiffusion3Img2ImgPipeline, + p.StableDiffusion3InpaintPipeline, + p.StableDiffusion3ControlNetPipeline, + ] + return is_compatible(model, compatible) diff --git a/modules/processing.py b/modules/processing.py index 1eb43c373..0c1b20c17 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -414,10 +414,10 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: devices.torch_gc() - if not p.xyz: - if hasattr(shared.sd_model, 'restore_pipeline') and (shared.sd_model.restore_pipeline is not None): - shared.sd_model.restore_pipeline() - shared.sd_model = sd_models.set_diffuser_pipe(shared.sd_model, sd_models.DiffusersTaskType.TEXT_2_IMAGE) + # if not p.xyz: + if hasattr(shared.sd_model, 'restore_pipeline') and (shared.sd_model.restore_pipeline is not None): + shared.sd_model.restore_pipeline() + shared.sd_model = sd_models.set_diffuser_pipe(shared.sd_model, sd_models.DiffusersTaskType.TEXT_2_IMAGE) t1 = time.time() diff --git a/modules/processing_class.py b/modules/processing_class.py index 9da434d51..0def1e2a0 100644 --- a/modules/processing_class.py +++ b/modules/processing_class.py @@ -311,6 +311,7 @@ class StableDiffusionProcessing: self.negative_pooleds = [] self.prompt_attention_masks = [] self.negative_prompt_attention_mask = [] + self.xyz = xyz def __str__(self): return f'{self.__class__.__name__}: {self.__dict__}' diff --git a/modules/scripts_manager.py b/modules/scripts_manager.py index 1ff034f6f..5f1cc92a7 100644 --- a/modules/scripts_manager.py +++ b/modules/scripts_manager.py @@ -367,6 +367,7 @@ class ScriptRunner: self.selectable_scripts.append(script) except Exception as e: errors.log.error(f'Script initialize: {path} {e}') + errors.display(e, f'script') def initialize_scripts(self, is_img2img=False, is_control=False): from modules import scripts_auto_postprocessing @@ -384,10 +385,17 @@ class ScriptRunner: self.selectable_scripts.clear() self.auto_processing_scripts = scripts_auto_postprocessing.create_auto_preprocessing_script_data() - sorted_scripts = sorted(scripts_data, key=lambda x: x.script_class().title().lower()) + try: + sorted_scripts = sorted(scripts_data, key=lambda x: x.script_class().title().lower()) + except Exception: + sorted_scripts = scripts_data for script_class, path, _basedir, _script_module in sorted_scripts: self.add_script(script_class, path, is_img2img, is_control) - sorted_scripts = sorted(self.auto_processing_scripts, key=lambda x: x.script_class().title().lower()) + + try: + sorted_scripts = sorted(self.auto_processing_scripts, key=lambda x: x.script_class().title().lower()) + except Exception: + sorted_scripts = self.auto_processing_scripts for script_class, path, _basedir, _script_module in sorted_scripts: self.add_script(script_class, path, is_img2img, is_control) diff --git a/scripts/apg.py b/scripts/apg.py index 616bbd18d..c17531f11 100644 --- a/scripts/apg.py +++ b/scripts/apg.py @@ -38,15 +38,17 @@ class Script(scripts_manager.Script): return fun import sys - xyz_classes = [v for k, v in sys.modules.items() if 'xyz_grid_classes' in k][0] - options = [ - xyz_classes.AxisOption("[APG] ETA", float, apply_field("apg_eta")), - xyz_classes.AxisOption("[APG] Momentum", float, apply_field("apg_momentum")), - xyz_classes.AxisOption("[APG] Threshold", float, apply_field("apg_threshold")), - ] - for option in options: - if option not in xyz_classes.axis_options: - xyz_classes.axis_options.append(option) + xyz_classes = [v for k, v in sys.modules.items() if 'xyz_grid_classes' in k] + if xyz_classes and len(xyz_classes) > 0: + xyz_classes = xyz_classes[0] + options = [ + xyz_classes.AxisOption("[APG] ETA", float, apply_field("apg_eta")), + xyz_classes.AxisOption("[APG] Momentum", float, apply_field("apg_momentum")), + xyz_classes.AxisOption("[APG] Threshold", float, apply_field("apg_threshold")), + ] + for option in options: + if option not in xyz_classes.axis_options: + xyz_classes.axis_options.append(option) def run(self, p: processing.StableDiffusionProcessing, eta = 0.0, momentum = 0.0, threshold = 0.0): # pylint: disable=arguments-differ supported_model_list = ['sd', 'sdxl', 'sc'] diff --git a/scripts/pulid_ext.py b/scripts/pulid_ext.py index fe9d8340f..59ced45b6 100644 --- a/scripts/pulid_ext.py +++ b/scripts/pulid_ext.py @@ -47,15 +47,17 @@ class Script(scripts_manager.Script): return fun import sys - xyz_classes = [v for k, v in sys.modules.items() if 'xyz_grid_classes' in k][0] - options = [ - xyz_classes.AxisOption("[PuLID] Strength", float, apply_field("pulid_strength")), - xyz_classes.AxisOption("[PuLID] Zero", int, apply_field("pulid_zero")), - xyz_classes.AxisOption("[PuLID] Ortho", str, apply_field("pulid_ortho"), choices=lambda: ['off', 'v1', 'v2']), - ] - for option in options: - if option not in xyz_classes.axis_options: - xyz_classes.axis_options.append(option) + xyz_classes = [v for k, v in sys.modules.items() if 'xyz_grid_classes' in k] + if xyz_classes and len(xyz_classes) > 0: + xyz_classes = xyz_classes[0] + options = [ + xyz_classes.AxisOption("[PuLID] Strength", float, apply_field("pulid_strength")), + xyz_classes.AxisOption("[PuLID] Zero", int, apply_field("pulid_zero")), + xyz_classes.AxisOption("[PuLID] Ortho", str, apply_field("pulid_ortho"), choices=lambda: ['off', 'v1', 'v2']), + ] + for option in options: + if option not in xyz_classes.axis_options: + xyz_classes.axis_options.append(option) def decode_image(self, b64): diff --git a/scripts/skip_layer_guidance.py b/scripts/skip_layer_guidance.py index 95c5a7b67..c8b72cd03 100644 --- a/scripts/skip_layer_guidance.py +++ b/scripts/skip_layer_guidance.py @@ -45,13 +45,15 @@ class Script(scripts_manager.Script): p.task_args[field] = val return fun - xyz_classes = [v for k, v in sys.modules.items() if 'xyz_grid_classes' in k][0] - options = [ - xyz_classes.AxisOption("[SLG] Layers", str, apply_task_args("skip_guidance_layers")), - ] - for option in options: - if option not in xyz_classes.axis_options: - xyz_classes.axis_options.append(option) + xyz_classes = [v for k, v in sys.modules.items() if 'xyz_grid_classes' in k] + if xyz_classes and len(xyz_classes) > 0: + xyz_classes = xyz_classes[0] + options = [ + xyz_classes.AxisOption("[SLG] Layers", str, apply_task_args("skip_guidance_layers")), + ] + for option in options: + if option not in xyz_classes.axis_options: + xyz_classes.axis_options.append(option) def run(self, p: processing.StableDiffusionProcessing, layers: str = '', scale: float = 1.0, start: float = 1.0, stop: float = 1.0): # pylint: disable=arguments-differ, unused-argument diff --git a/scripts/xyz_grid_classes.py b/scripts/xyz/xyz_grid_classes.py similarity index 90% rename from scripts/xyz_grid_classes.py rename to scripts/xyz/xyz_grid_classes.py index 705a9c4b6..c47446e83 100644 --- a/scripts/xyz_grid_classes.py +++ b/scripts/xyz/xyz_grid_classes.py @@ -1,5 +1,47 @@ -from scripts.xyz_grid_shared import apply_field, apply_task_arg, apply_task_args, apply_setting, apply_prompt_primary, apply_prompt_refine, apply_prompt_detailer, apply_prompt_all, apply_order, apply_sampler, apply_hr_sampler_name, confirm_samplers, apply_checkpoint, apply_refiner, apply_unet, apply_clip_skip, apply_vae, list_lora, apply_lora, apply_lora_strength, apply_te, apply_styles, apply_upscaler, apply_context, apply_detailer, apply_override, apply_processing, apply_options, apply_seed, apply_sdnq_quant, apply_sdnq_quant_te, format_value_add_label, format_bool, format_value, format_value_join_list, do_nothing, format_nothing, str_permutations # pylint: disable=no-name-in-module, unused-import +from scripts.xyz.xyz_grid_shared import ( + apply_field, + apply_task_arg, + apply_task_args, + apply_setting, + apply_prompt_primary, + apply_prompt_refine, + apply_prompt_detailer, + apply_prompt_all, + apply_order, + apply_sampler, + apply_hr_sampler_name, + confirm_samplers, + apply_checkpoint, + apply_refiner, + apply_unet, + apply_clip_skip, + apply_vae, + list_lora, + apply_lora, + apply_lora_strength, + apply_te, + apply_styles, + apply_upscaler, + apply_context, + apply_detailer, + apply_override, + apply_processing, + apply_options, + apply_seed, + apply_sdnq_quant, + apply_sdnq_quant_te, + apply_control, + format_value_add_label, + format_bool, + format_value, + format_value_join_list, + do_nothing, + format_nothing, + str_permutations, + ) # pylint: disable=no-name-in-module, unused-import from modules import shared, shared_items, sd_samplers, ipadapter, sd_models, sd_vae, sd_unet +from modules.control.units import controlnet, t2iadapter +from modules.control.processors_list import processors class AxisOption: @@ -215,6 +257,13 @@ axis_options = [ AxisOption("[IP adapter] Scale", float, apply_field('ip_adapter_scales')), AxisOption("[IP adapter] Starts", float, apply_field('ip_adapter_starts')), AxisOption("[IP adapter] Ends", float, apply_field('ip_adapter_ends')), + AxisOption("[Control] ControlNet", str, apply_control('controlnet'), cost=0.9, choices=lambda: list(controlnet.all_models)), + AxisOption("[Control] T2IAdapter", str, apply_control('t2i adapter'), cost=0.9, choices=lambda: list(t2iadapter.all_models)), + AxisOption("[Control] Processor", str, apply_control('processor'), cost=2.0, choices=lambda: processors), + AxisOption("[Control] Strength", float, apply_control('control_strength')), + AxisOption("[Control] Start", float, apply_control('control_start')), + AxisOption("[Control] End", float, apply_control('control_end')), + AxisOption("[HiDiffusion] T1", float, apply_override('hidiffusion_t1')), AxisOption("[HiDiffusion] T2", float, apply_override('hidiffusion_t2')), AxisOption("[HiDiffusion] Agression step", float, apply_field('hidiffusion_steps')), diff --git a/scripts/xyz_grid_draw.py b/scripts/xyz/xyz_grid_draw.py similarity index 100% rename from scripts/xyz_grid_draw.py rename to scripts/xyz/xyz_grid_draw.py diff --git a/scripts/xyz_grid_shared.py b/scripts/xyz/xyz_grid_shared.py similarity index 81% rename from scripts/xyz_grid_shared.py rename to scripts/xyz/xyz_grid_shared.py index 3698f1c51..9203c3168 100644 --- a/scripts/xyz_grid_shared.py +++ b/scripts/xyz/xyz_grid_shared.py @@ -287,6 +287,51 @@ def apply_detailer(p, opt, x): shared.log.debug(f'XYZ grid apply face-restore: "{x}"') +def apply_control(field): + def fun(p, x, xs): + shared.log.debug(f'XYZ grid apply control: {field}={x}') + if field in ['controlnet', 't2i adapter']: + from modules.control import run + vals = x.split(':') + model_id = vals[0].strip() if len(vals) > 0 else None + process_id = vals[1].strip() if len(vals) > 1 else None + strength = float(vals[2].strip()) if len(vals) > 2 else 1.0 + start = float(vals[3].strip()) if len(vals) > 3 else 0.0 + end = float(vals[4].strip()) if len(vals) > 4 else 1.0 + unit = run.unit.Unit( + index=0, + enabled=True, + unit_type=field, + model_id=model_id, + process_id=process_id, + strength=strength, + start=start, + end=end, + ) + run.init_units([unit]) + active_process, active_model, active_strength, active_start, active_end = run.check_active(p, unit.type, [unit]) + has_models, selected_models, control_conditioning, control_guidance_start, control_guidance_end = run.check_enabled(p, unit.type, [unit], active_model, active_strength, active_start, active_end) + pipe = run.set_pipe(p, has_models, unit.type, selected_models, active_model, active_strength, control_conditioning, control_guidance_start, control_guidance_end) + if pipe is not None: + shared.sd_model = pipe + elif field == 'processor': + from modules.control.processors import Processor + processor = Processor(x) + if processor is not None: + processor.reset() + # p.task_args['image'] = [processor(p.init_images)] + p.task_args['image'] = processor(p.init_images) + p.init_images = None + elif field == 'control_start': + p.task_args['control_guidance_start'] = float(x) + elif field == 'control_end': + p.task_args['control_guidance_end'] = float(x) + elif field == 'control_strength': + p.task_args['adapter_conditioning_scale'] = float(x) + p.task_args['controlnet_conditioning_scale'] = float(x) + return fun + + def apply_override(field): def fun(p, x, xs): p.override_settings[field] = x diff --git a/scripts/xyz_grid.py b/scripts/xyz_grid.py index a7c55eca3..051f9c766 100644 --- a/scripts/xyz_grid.py +++ b/scripts/xyz_grid.py @@ -10,10 +10,10 @@ from io import StringIO from PIL import Image import numpy as np import gradio as gr -from scripts.xyz_grid_shared import str_permutations, list_to_csv_string, re_range # pylint: disable=no-name-in-module -from scripts.xyz_grid_classes import axis_options, AxisOption, SharedSettingsStackHelper # pylint: disable=no-name-in-module -from scripts.xyz_grid_draw import draw_xyz_grid # pylint: disable=no-name-in-module -from scripts.xyz_grid_shared import apply_field, apply_task_args, apply_setting, apply_prompt, apply_order, apply_sampler, apply_hr_sampler_name, confirm_samplers, apply_checkpoint, apply_refiner, apply_unet, apply_clip_skip, apply_vae, list_lora, apply_lora, apply_lora_strength, apply_te, apply_styles, apply_upscaler, apply_context, apply_detailer, apply_override, apply_processing, apply_options, apply_seed, format_value_add_label, format_value, format_value_join_list, do_nothing, format_nothing # pylint: disable=no-name-in-module, unused-import +from scripts.xyz.xyz_grid_shared import str_permutations, list_to_csv_string, re_range # pylint: disable=no-name-in-module +from scripts.xyz.xyz_grid_classes import axis_options, AxisOption, SharedSettingsStackHelper # pylint: disable=no-name-in-module +from scripts.xyz.xyz_grid_draw import draw_xyz_grid # pylint: disable=no-name-in-module +from scripts.xyz.xyz_grid_shared import apply_field, apply_task_args, apply_setting, apply_prompt, apply_order, apply_sampler, apply_hr_sampler_name, confirm_samplers, apply_checkpoint, apply_refiner, apply_unet, apply_clip_skip, apply_vae, list_lora, apply_lora, apply_lora_strength, apply_te, apply_styles, apply_upscaler, apply_context, apply_detailer, apply_override, apply_processing, apply_options, apply_seed, format_value_add_label, format_value, format_value_join_list, do_nothing, format_nothing # pylint: disable=no-name-in-module, unused-import from modules import shared, errors, scripts_manager, images, processing from modules.ui_components import ToolButton from modules.ui_sections import create_video_inputs diff --git a/scripts/xyz_grid_on.py b/scripts/xyz_grid_on.py index 42327fae0..b0dea5ead 100644 --- a/scripts/xyz_grid_on.py +++ b/scripts/xyz_grid_on.py @@ -10,9 +10,9 @@ from io import StringIO from PIL import Image import numpy as np import gradio as gr -from scripts.xyz_grid_shared import str_permutations, list_to_csv_string, re_range # pylint: disable=no-name-in-module -from scripts.xyz_grid_classes import axis_options, AxisOption, SharedSettingsStackHelper # pylint: disable=no-name-in-module -from scripts.xyz_grid_draw import draw_xyz_grid # pylint: disable=no-name-in-module +from scripts.xyz.xyz_grid_shared import str_permutations, list_to_csv_string, re_range # pylint: disable=no-name-in-module +from scripts.xyz.xyz_grid_classes import axis_options, AxisOption, SharedSettingsStackHelper # pylint: disable=no-name-in-module +from scripts.xyz.xyz_grid_draw import draw_xyz_grid # pylint: disable=no-name-in-module from modules import shared, errors, scripts_manager, images, processing from modules.ui_components import ToolButton from modules.ui_sections import create_video_inputs