diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cb2c92c5..5f723ac1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,10 +16,10 @@ ModernUI is still in early development and not all features are available yet, p Thanks to @BinaryQuantumSoul for his hard work on this project! *Note*: [SD.Next](https://github.com/vladmandic/automatic) is no longer marked as a fork of [A1111](https://github.com/AUTOMATIC1111/stable-diffusion-webui/) and github project has been fully detached -Given huge number of changes with *+3443/-3342 commits diff over the past year, a completely different backend/engine and a change of focus, +Given huge number of changes with *+3443/-3342* commits diff (at the time of fork detach) over the past year, a completely different backend/engine and a change of focus, it is time to give credit to original [author](https://github.com/auTOMATIC1111), and move on! -## Update for 2024-05-06 +## Update for 2024-05-07 - **Features**: - **ModernUI** preview of the new [ModernUI](https://github.com/BinaryQuantumSoul/sdnext-modernui) @@ -53,24 +53,13 @@ it is time to give credit to original [author](https://github.com/auTOMATIC1111) Newer alternative method to [ToMe](https://github.com/dbolya/tomesd) that can provide speed-up with minimal quality loss Enable in *settings -> inference settings -> token merging* Also available in XYZ grid -- **UI**: - - Faster **UI** load times - - Theme types: - **Standard* (built-in themes), **Modern** (experimental nextgen ui), **None** (used for Gradio and Huggingface 3rd party themes) - Specifying a theme type updates list of available themes - For example, *Gradio* themes will not appear as available if theme type is set to *Standard* - - Redesign of base txt2img interface - - Minor tweaks to styles: refresh/apply/save - - See details in [WiKi](https://github.com/vladmandic/automatic/wiki/Themes) -- **API**: - - Add API endpoint `/sdapi/v1/control` and CLI util `cli/simple-control.py` - (in addition to previously added `/sdapi/v1/preprocessors` and `/sdapi/v1/masking`) - example: - > simple-control.py --prompt 'woman in the city' --sampler UniPC --steps 20 - > --input ~/generative/Samples/cutie-512.png --output /tmp/test.png --processed /tmp/proc.png - > --control 'Canny:Canny FP16:0.7, OpenPose:OpenPose FP16:0.8' --type controlnet - > --ipadapter 'Plus:~/generative/Samples/cutie-512.png:0.5' - - Add API endpoint `/sdapi/v1/vqa` and CLI util `cli/simple-vqa.py` + - **Outpaint**: + New method of outpainting that uses a combination of auto-masking and edge generation to create seamless transitions between original and generated image + Use on control tab: + - *input -> denoising strength: 0.5 or higher* + - *select image -> outpaint -> expand edges or zoom out to desired size* + - *size -> mode: outpaint, method: nearest* + - *mask -> inpaint masked only (if you want to keep original image)* - **Models**: - **Load UNET**: ability to override/load external UNET to a selected model Works similar to how VAE is selected and loaded: Set UNet folder and UNet model in settings @@ -89,6 +78,24 @@ it is time to give credit to original [author](https://github.com/auTOMATIC1111) - [Hyper-SD](https://huggingface.co/ByteDance/Hyper-SD) sd15 and sdxl 1-step, 2-step, 4-step and 8-step optimized models using lora set parameters: *sampler: TCD or LCM, steps: 1/2/4/8, cfg_scale: 0.0* +- **UI**: + - Faster **UI** load times + - Theme types: + **Standard** (built-in themes), **Modern** (experimental nextgen ui), **None** (used for Gradio and Huggingface 3rd party themes) + Specifying a theme type updates list of available themes + For example, *Gradio* themes will not appear as available if theme type is set to *Standard* + - Redesign of base txt2img interface + - Minor tweaks to styles: refresh/apply/save + - See details in [WiKi](https://github.com/vladmandic/automatic/wiki/Themes) +- **API**: + - Add API endpoint `/sdapi/v1/control` and CLI util `cli/simple-control.py` + (in addition to previously added `/sdapi/v1/preprocessors` and `/sdapi/v1/masking`) + example: + > simple-control.py --prompt 'woman in the city' --sampler UniPC --steps 20 + > --input ~/generative/Samples/cutie-512.png --output /tmp/test.png --processed /tmp/proc.png + > --control 'Canny:Canny FP16:0.7, OpenPose:OpenPose FP16:0.8' --type controlnet + > --ipadapter 'Plus:~/generative/Samples/cutie-512.png:0.5' + - Add API endpoint `/sdapi/v1/vqa` and CLI util `cli/simple-vqa.py` - **Changes**: - Removed built-in extensions: *ControlNet* and *Image-Browser* as both *image-browser* and *controlnet* have native built-in equivalents diff --git a/modules/masking.py b/modules/masking.py index 8a6c11a90..b2200ad57 100644 --- a/modules/masking.py +++ b/modules/masking.py @@ -351,6 +351,7 @@ def outpaint(input_image: Image.Image, outpaint_type: str = 'Edge'): mask = cv2.cvtColor(np.array(mask), cv2.COLOR_BGR2GRAY) mask = cv2.threshold(mask, 0, 255, cv2.THRESH_BINARY)[1] sigmaX, sigmaY = int((h0-h1)/3), int((w0-w1)/3) + sigmaX, sigmaY = max(1, sigmaX), max(1, sigmaY) kernel = np.ones((5, 5), np.uint8) mask = cv2.erode(mask, kernel, iterations=max(sigmaX, sigmaY) // 3) # increase overlap area mask = cv2.GaussianBlur(mask, (0, 0), sigmaX=sigmaX, sigmaY=sigmaY) # blur mask @@ -366,6 +367,8 @@ def outpaint(input_image: Image.Image, outpaint_type: str = 'Edge'): # noised[y1:y1 + h, x1:x1 + w] = cropped # overlay original over initialized # image = noised + # mask = Image.new('L', (w0, h0), 0) + image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) image = Image.fromarray(image) return image, mask diff --git a/modules/ui_control.py b/modules/ui_control.py index 6b396c8e7..b3281888a 100644 --- a/modules/ui_control.py +++ b/modules/ui_control.py @@ -179,7 +179,7 @@ def create_ui(_blocks: gr.Blocks=None): with gr.Column(scale=9, elem_id='control-preview-column', visible=True) as column_preview: gr.HTML('Preview

') with gr.Tabs(elem_classes=['control-tabs'], elem_id='control-tab-preview'): - with gr.Tab('Preview', id='preview-image') as tab_image: + with gr.Tab('Preview', id='preview-image') as _tab_preview: preview_process = gr.Image(label="Preview", show_label=False, type="pil", source="upload", interactive=False, height=gr_height, visible=True, elem_id='control_preview', elem_classes=['control-image']) with gr.Accordion('Control elements', open=False, elem_id="control_elements"): @@ -474,7 +474,7 @@ def create_ui(_blocks: gr.Blocks=None): btn_interrogate_booru.click(fn=helpers.interrogate_booru, inputs=[], outputs=[prompt]) select_fields = [input_mode, input_image, init_image, input_type, input_resize, input_inpaint, input_video, input_batch, input_folder] - select_output = [output_tabs, result_txt] + select_output = [output_tabs, preview_process, result_txt] select_dict = dict( fn=helpers.select_input, _js="controlInputMode", diff --git a/modules/ui_control_helpers.py b/modules/ui_control_helpers.py index 79493780a..175ee8495 100644 --- a/modules/ui_control_helpers.py +++ b/modules/ui_control_helpers.py @@ -126,12 +126,12 @@ def select_input(input_mode, input_image, init_image, init_type, input_resize, i input_source = None busy = False # debug('Control input: none') - return [gr.Tabs.update(), ''] + return [gr.Tabs.update(), None, ''] debug(f'Control select input: source={selected_input} init={init_image} type={init_type} mode={input_mode}') input_type = type(selected_input) input_mask = None status = 'Control input | Unknown' - res = [gr.Tabs.update(selected='out-gallery'), status] + res = [gr.Tabs.update(selected='out-gallery'), input_mask, status] # control inputs if isinstance(selected_input, Image.Image): # image via upload -> image if input_mode == 'Outpaint': @@ -140,23 +140,23 @@ def select_input(input_mode, input_image, init_image, init_type, input_resize, i input_source = [selected_input] input_type = 'PIL.Image' status = f'Control input | Image | Size {selected_input.width}x{selected_input.height} | Mode {selected_input.mode}' - res = [gr.Tabs.update(selected='out-gallery'), status] + res = [gr.Tabs.update(selected='out-gallery'), input_mask, status] elif isinstance(selected_input, dict): # inpaint -> dict image+mask input_mask = selected_input['mask'] selected_input = selected_input['image'] input_source = [selected_input] input_type = 'PIL.Image' status = f'Control input | Image | Size {selected_input.width}x{selected_input.height} | Mode {selected_input.mode}' - res = [gr.Tabs.update(selected='out-gallery'), status] + res = [gr.Tabs.update(selected='out-gallery'), input_mask, status] elif isinstance(selected_input, gr.components.image.Image): # not likely input_source = [selected_input.value] input_type = 'gr.Image' - res = [gr.Tabs.update(selected='out-gallery'), status] + res = [gr.Tabs.update(selected='out-gallery'), input_mask, status] elif isinstance(selected_input, str): # video via upload > tmp filepath to video input_source = selected_input input_type = 'gr.Video' status = get_video(input_source) - res = [gr.Tabs.update(selected='out-video'), status] + res = [gr.Tabs.update(selected='out-video'), input_mask, status] elif isinstance(selected_input, list): # batch or folder via upload -> list of tmp filepaths if hasattr(selected_input[0], 'name'): input_type = 'tempfiles' @@ -165,7 +165,7 @@ def select_input(input_mode, input_image, init_image, init_type, input_resize, i input_type = 'files' input_source = selected_input status = f'Control input | Images | Files {len(input_source)}' - res = [gr.Tabs.update(selected='out-gallery'), status] + res = [gr.Tabs.update(selected='out-gallery'), input_mask, status] else: # unknown input_source = None shared.log.debug(f'Control input: type={input_type} input={input_source}') @@ -176,7 +176,7 @@ def select_input(input_mode, input_image, init_image, init_type, input_resize, i input_init = None elif init_type == 2: # Separate init image input_init = [init_image] - debug(f'Control select input: source={input_source} init={input_init} mode={input_mode}') + debug(f'Control select input: source={input_source} init={input_init} mask={input_mask} mode={input_mode}') busy = False return res