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',