diff --git a/html/locale_en.json b/html/locale_en.json
index 253dc3f3c..6308416a0 100644
--- a/html/locale_en.json
+++ b/html/locale_en.json
@@ -20,7 +20,7 @@
{"id":"","label":"☲","localized":"","hint":"Change view type"},
{"id":"","label":"📐","localized":"","hint":"Measure"},
{"id":"","label":"🔍","localized":"","hint":"Search"},
- {"id":"","label":"🖼️","localized":"","hint":"LaMa remove selected object from image"},
+ {"id":"","label":"🖌️","localized":"","hint":"LaMa remove selected object from image"},
{"id":"","label":"🖼️","localized":"","hint":"Show preview"},
{"id":"","label":"✎","localized":"","hint":"Interrogate image using BLIP model"},
{"id":"","label":"✐","localized":"","hint":"Interrogate image using DeepBooru model"}
diff --git a/javascript/base.css b/javascript/base.css
index c84bb08c3..18ff4d42f 100644
--- a/javascript/base.css
+++ b/javascript/base.css
@@ -1,5 +1,5 @@
/* toolbutton */
-.gradio-button.tool { max-width: min-content; min-width: min-content !important; align-self: end; font-size: 1.4em; color: var(--body-text-color) !important; margin-bottom: var(--spacing-md); }
+.gradio-button.tool { max-width: min-content; min-width: min-content !important; align-self: end; font-size: 1.4em; color: var(--body-text-color) !important; }
/* token counters */
.block.token-counter{ position: absolute; display: inline-block; right: 0; min-width: 0 !important; width: auto; z-index: 100; top: 0; }
diff --git a/javascript/sdnext.css b/javascript/sdnext.css
index 8b2e447f0..8236450cc 100644
--- a/javascript/sdnext.css
+++ b/javascript/sdnext.css
@@ -27,7 +27,7 @@ button { font-size: var(--text-lg) !important; }
.gradio-button.secondary-down { background: var(--button-secondary-background-fill); color: var(--button-secondary-text-color); }
.gradio-button.secondary-down, .gradio-button.secondary-down:hover { box-shadow: 1px 1px 1px rgba(0,0,0,0.25) inset, 0px 0px 3px rgba(0,0,0,0.15) inset; }
.gradio-button.secondary-down:hover { background: var(--button-secondary-background-fill-hover); color: var(--button-secondary-text-color-hover); }
-.gradio-button.tool { max-width: min-content; min-width: min-content !important; align-self: end; font-size: 20px !important; color: var(--body-text-color) !important; margin-top: auto; margin-bottom: var(--spacing-md); align-self: center; }
+.gradio-button.tool { max-width: min-content; min-width: min-content !important; align-self: end; font-size: 20px !important; color: var(--body-text-color) !important; align-self: center; }
.gradio-checkbox { margin: 0.75em 1.5em 0 0; align-self: center; }
.gradio-column { min-width: min(160px, 100%) !important; }
.gradio-container { max-width: unset !important; padding: var(--block-label-padding) !important; }
diff --git a/modules/control/processors.py b/modules/control/processors.py
index 99e9e9e0b..82393df35 100644
--- a/modules/control/processors.py
+++ b/modules/control/processors.py
@@ -4,7 +4,7 @@ import numpy as np
from PIL import Image
from modules.shared import log
from modules.errors import display
-from modules import devices
+from modules import devices, images
from modules.control.proc.hed import HEDdetector
from modules.control.proc.canny import CannyDetector
@@ -33,6 +33,8 @@ cache_dir = 'models/control/processors'
debug = log.trace if os.environ.get('SD_CONTROL_DEBUG', None) is not None else lambda *args, **kwargs: None
debug('Trace: CONTROL')
config = {
+ # placeholder
+ 'None': {},
# pose models
'OpenPose': {'class': OpenposeDetector, 'checkpoint': True, 'params': {'include_body': True, 'include_hand': False, 'include_face': False}},
'DWPose': {'class': DWposeDetector, 'checkpoint': False, 'model': 'Tiny', 'params': {'min_confidence': 0.3}},
@@ -68,7 +70,7 @@ def list_models(refresh=False):
global models # pylint: disable=global-statement
if not refresh and len(models) > 0:
return models
- models = ['None'] + list(config)
+ models = list(config)
debug(f'Control list processors: path={cache_dir} models={models}')
return models
@@ -124,7 +126,7 @@ class Processor():
def __init__(self, processor_id: str = None, resize = True):
self.model = None
self.processor_id = None
- self.override = None
+ # self.override = None
self.resize = resize
self.reset()
self.config(processor_id)
@@ -133,7 +135,7 @@ class Processor():
def reset(self, processor_id: str = None):
if self.model is not None:
- log.debug(f'Control Processor unloaded: id="{self.processor_id}"')
+ debug(f'Control Processor unloaded: id="{self.processor_id}"')
self.model = None
self.processor_id = processor_id
self.override = None
@@ -204,14 +206,20 @@ class Processor():
display(e, 'Control Processor load')
return f'Processor load filed: {processor_id}'
- def __call__(self, image_input: Image, mode: str = 'RGB'):
+ def __call__(self, image_input: Image, mode: str = 'RGB', resize_mode: int = 0, resize_name: str = 'None', scale_tab: int = 1, scale_by: float = 1.0):
if self.processor_id is None or self.processor_id == 'None':
return image_input
if self.override is not None:
+ debug(f'Control Processor: id="{self.processor_id}" override={self.override}')
image_input = self.override
+ if resize_mode != 0 and resize_name != 'None':
+ if scale_tab == 1:
+ width_before, height_before = int(image_input.width * scale_by), int(image_input.height * scale_by)
+ debug(f'Control resize: op=before image={image_input} width={width_before} height={height_before} mode={resize_mode} name={resize_name}')
+ image_input = images.resize_image(resize_mode, image_input, width_before, height_before, resize_name)
image_process = image_input
if image_input is None:
- log.error('Control Processor: no input')
+ # log.error('Control Processor: no input')
return image_process
if config[self.processor_id].get('dirty', False):
processor_id = self.processor_id
@@ -250,5 +258,5 @@ class Processor():
input_image = modules.ui_control.input_source
if isinstance(input_image, list):
input_image = input_image[0]
- if isinstance(input_image, Image.Image):
- return self.__call__(input_image)
+ debug('Control process preview')
+ return self.__call__(input_image)
diff --git a/modules/control/run.py b/modules/control/run.py
index 834b811f9..8b443be3d 100644
--- a/modules/control/run.py
+++ b/modules/control/run.py
@@ -376,33 +376,33 @@ def control_run(units: List[unit.Unit], inputs, inits, mask, unit_type: str, is_
p.height = input_image.height
debug(f'Control: input image={input_image}')
- # process
- if input_image is None:
- p.image = []
- debug(f'Control: process=None image={p.image} mask={mask}')
- elif len(active_process) == 0:
- # p.image = [masking.run_mask(input_image=input_image, input_mask=mask, return_type='Masked') if mask is not None else input_image]
- pass
- elif len(active_process) > 0:
- p.image = []
- masked_image = masking.run_mask(input_image=input_image, input_mask=mask, return_type='Masked') if mask is not None else input_image
- for i, process in enumerate(active_process): # list[image]
- image_mode = 'L' if unit_type == 'adapter' and len(active_model) > i and ('Canny' in active_model[i].model_id or 'Sketch' in active_model[i].model_id) else 'RGB' # t2iadapter canny and sketch work in grayscale only
- debug(f'Control: process="{process.processor_id}" i={i} image={p.image}')
- processed_image = process(masked_image, image_mode)
+ p.image = []
+ masked_image = masking.run_mask(input_image=input_image, input_mask=mask, return_type='Masked') if mask is not None else input_image
+ for i, process in enumerate(active_process): # list[image]
+ image_mode = 'L' if unit_type == 'adapter' and len(active_model) > i and ('Canny' in active_model[i].model_id or 'Sketch' in active_model[i].model_id) else 'RGB' # t2iadapter canny and sketch work in grayscale only
+ debug(f'Control: i={i+1} process="{process.processor_id}" input={masked_image} override={process.override}')
+ processed_image = process(
+ image_input=masked_image,
+ mode=image_mode,
+ resize_mode=resize_mode_before,
+ resize_name=resize_name_before,
+ scale_tab=selected_scale_tab_before,
+ scale_by=scale_by_before,
+ )
+ if processed_image is not None:
p.image.append(processed_image)
- if shared.opts.control_unload_processor:
- processors.config[process.processor_id]['dirty'] = True # to force reload
- process.model = None
+ if shared.opts.control_unload_processor:
+ processors.config[process.processor_id]['dirty'] = True # to force reload
+ process.model = None
- if p.image is not None and len(p.image) > 0:
- p.init_images = p.image
+ if len(p.image) > 0:
p.extra_generation_params["Control process"] = [p.processor_id for p in active_process]
if any(img is None for img in p.image):
msg = 'Control: attempting process but output is none'
- shared.log.error(msg)
+ shared.log.error(f'{msg}: {p.image}')
restore_pipeline()
return msg
+ p.init_images = p.image
processed_image = [np.array(i) for i in p.image]
processed_image = util.blend(processed_image) # blend all processed images into one
processed_image = Image.fromarray(processed_image)
diff --git a/modules/control/units/controlnet.py b/modules/control/units/controlnet.py
index bac2196d1..3d98a4a1e 100644
--- a/modules/control/units/controlnet.py
+++ b/modules/control/units/controlnet.py
@@ -111,7 +111,7 @@ class ControlNet():
def reset(self):
if self.model is not None:
- log.debug(f'Control {what} model unloaded')
+ debug(f'Control {what} model unloaded')
self.model = None
self.model_id = None
diff --git a/modules/control/units/lite.py b/modules/control/units/lite.py
index eb97165dd..96ed0dc35 100644
--- a/modules/control/units/lite.py
+++ b/modules/control/units/lite.py
@@ -74,7 +74,7 @@ class ControlLLLite():
def reset(self):
if self.model is not None:
- log.debug(f'Control {what} model unloaded')
+ debug(f'Control {what} model unloaded')
self.model = None
self.model_id = None
diff --git a/modules/control/units/t2iadapter.py b/modules/control/units/t2iadapter.py
index 04d72c80c..6514d0a63 100644
--- a/modules/control/units/t2iadapter.py
+++ b/modules/control/units/t2iadapter.py
@@ -75,7 +75,7 @@ class Adapter():
def reset(self):
if self.model is not None:
- log.debug(f'Control {what} model unloaded')
+ debug(f'Control {what} model unloaded')
self.model = None
self.model_id = None
diff --git a/modules/control/units/xs.py b/modules/control/units/xs.py
index e1c7a561e..4f5396356 100644
--- a/modules/control/units/xs.py
+++ b/modules/control/units/xs.py
@@ -70,7 +70,7 @@ class ControlNetXS():
def reset(self):
if self.model is not None:
- log.debug(f'Control {what} model unloaded')
+ debug(f'Control {what} model unloaded')
self.model = None
self.model_id = None
diff --git a/modules/ui_control.py b/modules/ui_control.py
index 05b3062b5..7b9905332 100644
--- a/modules/ui_control.py
+++ b/modules/ui_control.py
@@ -155,7 +155,7 @@ def select_input(input_mode, input_image, selected_init, init_type, input_resize
if selected_input is None:
input_source = None
busy = False
- debug('Control clear input')
+ debug('Control input: none')
return [gr.Tabs.update(), '']
debug(f'Control select input: source={selected_input} init={selected_init} type={init_type} mode={input_mode}')
input_type = type(selected_input)
@@ -410,7 +410,7 @@ def create_ui(_blocks: gr.Blocks=None):
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
process_btn= ui_components.ToolButton(value=ui_symbols.preview)
- image_preview = gr.Image(label="Input", show_label=False, type="pil", source="upload", interactive=False, height=128, width=128, visible=False)
+ image_preview = gr.Image(label="Input", type="pil", source="upload", height=128, width=128, visible=False, interactive=True, show_label=False, show_download_button=False, container=False)
controlnet_ui_units.append(unit_ui)
units.append(unit.Unit(
unit_type = 'controlnet',