diff --git a/CHANGELOG.md b/CHANGELOG.md index 28edde7b4..96353ce14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,11 @@ - new module for creating videos from images - simply enable from *img2img -> scripts -> image2video* - based on [VGen](https://huggingface.co/ali-vilab/i2vgen-xl) +- **Composable LoRA**, thanks @AI-Casanova + - control lora strength for each step + for example: `` means strength=0.1 for step at 0% and intepolate towards strength=0.9 for step at 100% + - set sampler to *Composable LoRA* + - *note*: this is a very experimental feature and may not work as expected - **Samplers** - [TCD](https://mhh0318.github.io/tcd/): Trajectory Consistency Distillation new sampler that produces consistent results in a very low number of steps (comparable to LCM but without reliance on LoRA) @@ -43,6 +48,7 @@ EDM is a new solver algorithm currently available for DPM++2M and Euler samplers Note that using EDM samplers with non-EDM optimized models will provide just noise and vice-versa - **UI** + - *aspect-ratio** add selector and lock to width/height control - *interrogate* tab is now merged into *process* tab - *image viewer* now displays image metadata - *themes* improve on-the-fly switching diff --git a/javascript/sdnext.css b/javascript/sdnext.css index 6cd176164..622182d5e 100644 --- a/javascript/sdnext.css +++ b/javascript/sdnext.css @@ -250,6 +250,8 @@ table.settings-value-table td { padding: 0.4em; border: 1px solid #ccc; max-widt #models_tab { flex-flow: row-reverse; } #swap_axes>button { min-width: 100px; font-size: var(--text-md); } #ui_defaults_review { margin: 1em; } +.ar-dropdown { font-size: 0.9em; min-width: 5em !important; max-width: 5em !important; margin: 0 !important; padding: 0 !important; align-content: center; } +.ar-dropdown div { margin: 0; background: var(--background-color)} /* extras */ .extras { gap: 0.2em 1em !important } diff --git a/modules/extra_networks.py b/modules/extra_networks.py index aa092b6c3..fee0b51e6 100644 --- a/modules/extra_networks.py +++ b/modules/extra_networks.py @@ -67,7 +67,8 @@ def is_stepwise(en_obj): for en in en_obj: all_args.extend(en.positional[1:]) all_args.extend(en.named.values()) - return any([len(str(x).split("@")) > 1 for x in all_args]) + return any([len(str(x).split("@")) > 1 for x in all_args]) # noqa C419 + def activate(p, extra_network_data, step=0): """call activate for extra networks in extra_network_data in specified order, then call activate for all remaining registered networks with an empty argument list""" @@ -101,7 +102,7 @@ def activate(p, extra_network_data, step=0): errors.display(e, f"activating extra network: name={extra_network_name}") if stepwise: p.extra_network_data = extra_network_data - shared.opts.lora_functional = functional + shared.opts.data['lora_functional'] = functional def deactivate(p, extra_network_data): diff --git a/modules/ui_sections.py b/modules/ui_sections.py index 214dce4c3..3907a344b 100644 --- a/modules/ui_sections.py +++ b/modules/ui_sections.py @@ -55,6 +55,29 @@ def create_toprow(is_img2img: bool = False, id_part: str = None): return prompt, styles, negative_prompt, submit, button_paste, button_extra, token_counter, token_button, negative_token_counter, negative_token_button +def ar_change(ar, width, height): + if ar == 'AR': + return gr.update(interactive=True), gr.update(interactive=True) + (w, h) = [int(x) for x in ar.split(':')] + if w > h: + return gr.update(interactive=True, value=width), gr.update(interactive=False, value=int(width * int(h) / int(w))) + elif w < h: + return gr.update(interactive=False, value=int(height * int(w) / int(h))), gr.update(interactive=True, value=height) + else: + return gr.update(interactive=True, value=width), gr.update(interactive=False, value=width) + + +def create_resolution_inputs(tab): + width = gr.Slider(minimum=64, maximum=4096, step=8, label="Width", value=512, elem_id=f"{tab}_width") + height = gr.Slider(minimum=64, maximum=4096, step=8, label="Height", value=512, elem_id=f"{tab}_height") + ar_dropdown = gr.Dropdown(show_label=False, interactive=True, choices=["AR", "1:1", "4:3", "16:9", "16:10", "21:9", "3:4", "9:16", "10:16", "9:21"], value="AR", elem_id=f"{tab}_ar", elem_classes=["ar-dropdown"]) + for c in [ar_dropdown, width, height]: + c.change(fn=ar_change, inputs=[ar_dropdown, width, height], outputs=[width, height], show_progress=False) + res_switch_btn = ToolButton(value=ui_symbols.switch, elem_id=f"{tab}_res_switch_btn", label="Switch dims") + res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress=False) + return width, height + + def create_interrogate_buttons(tab): button_interrogate = gr.Button(ui_symbols.int_clip, elem_id=f"{tab}_interrogate", elem_classes=['interrogate-clip']) button_deepbooru = gr.Button(ui_symbols.int_blip, elem_id=f"{tab}_deepbooru", elem_classes=['interrogate-blip']) @@ -239,6 +262,9 @@ def create_resize_inputs(tab, images, scale_visible=True, mode=None, accordion=T with gr.Row(): width = gr.Slider(minimum=64, maximum=8192, step=8, label="Width", value=512, elem_id=f"{tab}_width") height = gr.Slider(minimum=64, maximum=8192, step=8, label="Height", value=512, elem_id=f"{tab}_height") + ar_dropdown = gr.Dropdown(scale=1, show_label=False, interactive=True, choices=["AR", "1:1", "4:3", "16:9", "16:10", "21:9", "3:4", "9:16", "10:16", "9:21"], value="AR", elem_id=f"{tab}_ar", elem_classes=["ar-dropdown"]) + for c in [ar_dropdown, width, height]: + c.change(fn=ar_change, inputs=[ar_dropdown, width, height], outputs=[width, height], show_progress=False) res_switch_btn = ToolButton(value=ui_symbols.switch, elem_id=f"{tab}_res_switch_btn") res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress=False) detect_image_size_btn = ToolButton(value=ui_symbols.detect, elem_id=f"{tab}_detect_image_size_btn") diff --git a/modules/ui_txt2img.py b/modules/ui_txt2img.py index 7a58acbb8..18b4db8ae 100644 --- a/modules/ui_txt2img.py +++ b/modules/ui_txt2img.py @@ -1,7 +1,7 @@ import gradio as gr from modules.call_queue import wrap_gradio_gpu_call, wrap_queued_call -from modules import timer, shared, ui_common, ui_symbols, ui_sections, generation_parameters_copypaste -from modules.ui_components import ToolButton +from modules import timer, shared, ui_common, ui_sections, generation_parameters_copypaste +from modules.ui_components import ToolButton # pylint: disable=unused-import def calc_resolution_hires(width, height, hr_scale, hr_resize_x, hr_resize_y, hr_upscaler): @@ -35,10 +35,7 @@ def create_ui(): with gr.Column(variant='compact', elem_id="txt2img_settings"): with gr.Row(): - width = gr.Slider(minimum=64, maximum=4096, step=8, label="Width", value=512, elem_id="txt2img_width") - height = gr.Slider(minimum=64, maximum=4096, step=8, label="Height", value=512, elem_id="txt2img_height") - res_switch_btn = ToolButton(value=ui_symbols.switch, elem_id="txt2img_res_switch_btn", label="Switch dims") - res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress=False) + width, height = ui_sections.create_resolution_inputs('txt2img') with gr.Group(elem_classes="settings-accordion"):