diff --git a/TODO.md b/TODO.md index bf0cecbb4..6d89c838f 100644 --- a/TODO.md +++ b/TODO.md @@ -11,24 +11,21 @@ Main ToDo list can be found at [GitHub projects](https://github.com/users/vladma ## Future Candidates - Flux NF4 loader: - -## Other - - IPAdapter negative: - Control API enhance scripts compatibility - PixelSmith: ## Code TODO -- TODO install: python 3.12.4 or higher cause a mess with pydantic (fixme) -- TODO install: enable ROCm for windows when available (fixme) -- TODO resize image: enable full VAE mode for resize-latent (fixme) -- TODO processing: remove duplicate mask params (fixme) -- TODO flux: fix loader for civitai nf4 models (fixme) -- TODO model loader: implement model in-memory caching (fixme) -- TODO hypertile: vae breaks when using non-standard sizes (fixme) -- TODO model load: force-reloading entire model as loading transformers only leads to massive memory usage (fixme) -- TODO lora load: direct with bnb (fixme) -- TODO: lora make: support quantized flux (fixme) -- TODO control: support scripts via api (fixme) -- TODO modernui: monkey-patch for missing tabs.select event (fixme) +- TODO install: python 3.12.4 or higher cause a mess with pydantic +- TODO install: enable ROCm for windows when available +- TODO resize image: enable full VAE mode for resize-latent +- TODO processing: remove duplicate mask params +- TODO flux: fix loader for civitai nf4 models +- TODO model loader: implement model in-memory caching +- TODO hypertile: vae breaks when using non-standard sizes +- TODO model load: force-reloading entire model as loading transformers only leads to massive memory usage +- TODO lora load: direct with bnb +- TODO lora make: support quantized flux +- TODO control: support scripts via api +- TODO modernui: monkey-patch for missing tabs.select event diff --git a/modules/scripts.py b/modules/scripts.py index 029d0db79..9c410a3b6 100644 --- a/modules/scripts.py +++ b/modules/scripts.py @@ -330,6 +330,7 @@ class ScriptRunner: self.scripts = [] self.selectable_scripts = [] self.alwayson_scripts = [] + self.auto_processing_scripts = [] self.titles = [] self.infotext_fields = [] self.paste_field_names = [] @@ -337,6 +338,31 @@ class ScriptRunner: self.is_img2img = False self.inputs = [None] + def add_script(self, script_class, path, is_img2img, is_control): + try: + script = script_class() + script.filename = path + script.is_txt2img = not is_img2img + script.is_img2img = is_img2img + if is_control: # this is messy but show is a legacy function that is not aware of control tab + v1 = script.show(script.is_txt2img) + v2 = script.show(script.is_img2img) + if v1 == AlwaysVisible or v2 == AlwaysVisible: + visibility = AlwaysVisible + else: + visibility = v1 or v2 + else: + visibility = script.show(script.is_img2img) + if visibility == AlwaysVisible: + self.scripts.append(script) + self.alwayson_scripts.append(script) + script.alwayson = True + elif visibility: + self.scripts.append(script) + self.selectable_scripts.append(script) + except Exception as e: + errors.log.error(f'Script initialize: {path} {e}') + def initialize_scripts(self, is_img2img=False, is_control=False): from modules import scripts_auto_postprocessing @@ -351,34 +377,14 @@ class ScriptRunner: self.scripts.clear() self.alwayson_scripts.clear() self.selectable_scripts.clear() - auto_processing_scripts = scripts_auto_postprocessing.create_auto_preprocessing_script_data() + self.auto_processing_scripts = scripts_auto_postprocessing.create_auto_preprocessing_script_data() - all_scripts = auto_processing_scripts + scripts_data - sorted_scripts = sorted(all_scripts, key=lambda x: x.script_class().title().lower()) + sorted_scripts = sorted(scripts_data, key=lambda x: x.script_class().title().lower()) for script_class, path, _basedir, _script_module in sorted_scripts: - try: - script = script_class() - script.filename = path - script.is_txt2img = not is_img2img - script.is_img2img = is_img2img - if is_control: # this is messy but show is a legacy function that is not aware of control tab - v1 = script.show(script.is_txt2img) - v2 = script.show(script.is_img2img) - if v1 == AlwaysVisible or v2 == AlwaysVisible: - visibility = AlwaysVisible - else: - visibility = v1 or v2 - else: - visibility = script.show(script.is_img2img) - if visibility == AlwaysVisible: - self.scripts.append(script) - self.alwayson_scripts.append(script) - script.alwayson = True - elif visibility: - self.scripts.append(script) - self.selectable_scripts.append(script) - except Exception as e: - errors.log.error(f'Script initialize: {path} {e}') + 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()) + for script_class, path, _basedir, _script_module in sorted_scripts: + self.add_script(script_class, path, is_img2img, is_control) def prepare_ui(self): self.inputs = [None] diff --git a/modules/shared.py b/modules/shared.py index 36e58ebb8..31429e076 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -837,7 +837,7 @@ options_templates.update(options_section(('sampler-params', "Sampler Settings"), options_templates.update(options_section(('postprocessing', "Postprocessing"), { 'postprocessing_enable_in_main_ui': OptionInfo([], "Additional postprocessing operations", gr.Dropdown, lambda: {"multiselect":True, "choices": [x.name for x in shared_items.postprocessing_scripts()]}), - 'postprocessing_operation_order': OptionInfo([], "Postprocessing operation order", gr.Dropdown, lambda: {"multiselect":True, "choices": [x.name for x in shared_items.postprocessing_scripts()]}), + 'postprocessing_operation_order': OptionInfo([], "Postprocessing operation order", gr.Dropdown, lambda: {"multiselect":True, "choices": [x.name for x in shared_items.postprocessing_scripts()], "visible": False }), "postprocessing_sep_img2img": OptionInfo("

Inpaint

", "", gr.HTML), "img2img_color_correction": OptionInfo(False, "Apply color correction"), diff --git a/scripts/postprocessing_codeformer.py b/scripts/postprocessing_codeformer.py index 3e5fd451d..8ed677736 100644 --- a/scripts/postprocessing_codeformer.py +++ b/scripts/postprocessing_codeformer.py @@ -10,9 +10,10 @@ class ScriptPostprocessingCodeFormer(scripts_postprocessing.ScriptPostprocessing order = 3000 def ui(self): - with gr.Row(): - codeformer_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label="Strength", value=0.0, elem_id="extras_codeformer_visibility") - codeformer_weight = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label="Weight", value=0.2, elem_id="extras_codeformer_weight") + with gr.Accordion('Restore faces: CodeFormer', open = False): + with gr.Row(): + codeformer_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label="Strength", value=0.0, elem_id="extras_codeformer_visibility") + codeformer_weight = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label="Weight", value=0.2, elem_id="extras_codeformer_weight") return { "codeformer_visibility": codeformer_visibility, "codeformer_weight": codeformer_weight } def process(self, pp: scripts_postprocessing.PostprocessedImage, codeformer_visibility, codeformer_weight): # pylint: disable=arguments-differ diff --git a/scripts/postprocessing_gfpgan.py b/scripts/postprocessing_gfpgan.py index a69f97c9e..1e17c2b16 100644 --- a/scripts/postprocessing_gfpgan.py +++ b/scripts/postprocessing_gfpgan.py @@ -9,8 +9,9 @@ class ScriptPostprocessingGfpGan(scripts_postprocessing.ScriptPostprocessing): order = 2000 def ui(self): - with gr.Row(): - gfpgan_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Strength", value=0, elem_id="extras_gfpgan_visibility") + with gr.Accordion('Restore faces: GFPGan', open = False): + with gr.Row(): + gfpgan_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Strength", value=0, elem_id="extras_gfpgan_visibility") return { "gfpgan_visibility": gfpgan_visibility } def process(self, pp: scripts_postprocessing.PostprocessedImage, gfpgan_visibility): # pylint: disable=arguments-differ diff --git a/scripts/postprocessing_upscale.py b/scripts/postprocessing_upscale.py index 0e07bc847..104a0fb37 100644 --- a/scripts/postprocessing_upscale.py +++ b/scripts/postprocessing_upscale.py @@ -10,43 +10,43 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): order = 1000 def ui(self): - selected_tab = gr.State(value=0) # pylint: disable=abstract-class-instantiated + with gr.Accordion('Postprocess Upscale', open = False): + selected_tab = gr.State(value=0) # pylint: disable=abstract-class-instantiated + with gr.Column(): + with gr.Row(elem_id="extras_upscale"): + with gr.Tabs(elem_id="extras_resize_mode"): + with gr.TabItem('Scale by', elem_id="extras_scale_by_tab") as tab_scale_by: + upscaling_resize = gr.Slider(minimum=0.1, maximum=8.0, step=0.05, label="Resize", value=2.0, elem_id="extras_upscaling_resize") - with gr.Column(): - with gr.Row(elem_id="extras_upscale"): - with gr.Tabs(elem_id="extras_resize_mode"): - with gr.TabItem('Scale by', elem_id="extras_scale_by_tab") as tab_scale_by: - upscaling_resize = gr.Slider(minimum=0.1, maximum=8.0, step=0.05, label="Resize", value=2.0, elem_id="extras_upscaling_resize") + with gr.TabItem('Scale to', elem_id="extras_scale_to_tab") as tab_scale_to: + with gr.Row(): + with gr.Row(elem_id="upscaling_column_size"): + upscaling_resize_w = gr.Slider(minimum=64, maximum=4096, step=8, label="Width", value=1024, elem_id="extras_upscaling_resize_w") + upscaling_resize_h = gr.Slider(minimum=64, maximum=4096, step=8, label="Height", value=1024, elem_id="extras_upscaling_resize_h") + upscaling_res_switch_btn = ToolButton(value=symbols.switch, elem_id="upscaling_res_switch_btn") + upscaling_crop = gr.Checkbox(label='Crop to fit', value=True, elem_id="extras_upscaling_crop") - with gr.TabItem('Scale to', elem_id="extras_scale_to_tab") as tab_scale_to: - with gr.Row(): - with gr.Row(elem_id="upscaling_column_size"): - upscaling_resize_w = gr.Slider(minimum=64, maximum=4096, step=8, label="Width", value=1024, elem_id="extras_upscaling_resize_w") - upscaling_resize_h = gr.Slider(minimum=64, maximum=4096, step=8, label="Height", value=1024, elem_id="extras_upscaling_resize_h") - upscaling_res_switch_btn = ToolButton(value=symbols.switch, elem_id="upscaling_res_switch_btn") - upscaling_crop = gr.Checkbox(label='Crop to fit', value=True, elem_id="extras_upscaling_crop") + with gr.Row(): + extras_upscaler_1 = gr.Dropdown(label='Upscaler', elem_id="extras_upscaler_1", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) - with gr.Row(): - extras_upscaler_1 = gr.Dropdown(label='Upscaler', elem_id="extras_upscaler_1", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) + with gr.Row(): + extras_upscaler_2 = gr.Dropdown(label='Refine Upscaler', elem_id="extras_upscaler_2", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) + extras_upscaler_2_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Upscaler 2 visibility", value=0.0, elem_id="extras_upscaler_2_visibility") - with gr.Row(): - extras_upscaler_2 = gr.Dropdown(label='Refine Upscaler', elem_id="extras_upscaler_2", choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name) - extras_upscaler_2_visibility = gr.Slider(minimum=0.0, maximum=1.0, step=0.001, label="Upscaler 2 visibility", value=0.0, elem_id="extras_upscaler_2_visibility") + upscaling_res_switch_btn.click(lambda w, h: (h, w), inputs=[upscaling_resize_w, upscaling_resize_h], outputs=[upscaling_resize_w, upscaling_resize_h], show_progress=False) + tab_scale_by.select(fn=lambda: 0, inputs=[], outputs=[selected_tab]) + tab_scale_to.select(fn=lambda: 1, inputs=[], outputs=[selected_tab]) - upscaling_res_switch_btn.click(lambda w, h: (h, w), inputs=[upscaling_resize_w, upscaling_resize_h], outputs=[upscaling_resize_w, upscaling_resize_h], show_progress=False) - tab_scale_by.select(fn=lambda: 0, inputs=[], outputs=[selected_tab]) - tab_scale_to.select(fn=lambda: 1, inputs=[], outputs=[selected_tab]) - - return { - "upscale_mode": selected_tab, - "upscale_by": upscaling_resize, - "upscale_to_width": upscaling_resize_w, - "upscale_to_height": upscaling_resize_h, - "upscale_crop": upscaling_crop, - "upscaler_1_name": extras_upscaler_1, - "upscaler_2_name": extras_upscaler_2, - "upscaler_2_visibility": extras_upscaler_2_visibility, - } + return { + "upscale_mode": selected_tab, + "upscale_by": upscaling_resize, + "upscale_to_width": upscaling_resize_w, + "upscale_to_height": upscaling_resize_h, + "upscale_crop": upscaling_crop, + "upscaler_1_name": extras_upscaler_1, + "upscaler_2_name": extras_upscaler_2, + "upscaler_2_visibility": extras_upscaler_2_visibility, + } def upscale(self, image, info, upscaler, upscale_mode, upscale_by, upscale_to_width, upscale_to_height, upscale_crop): if upscale_mode == 1: diff --git a/scripts/postprocessing_video.py b/scripts/postprocessing_video.py index 8c266639c..75d375572 100644 --- a/scripts/postprocessing_video.py +++ b/scripts/postprocessing_video.py @@ -7,40 +7,41 @@ class ScriptPostprocessingUpscale(scripts_postprocessing.ScriptPostprocessing): name = "Video" def ui(self): - def video_type_change(video_type): - return [ - gr.update(visible=video_type != 'None'), - gr.update(visible=video_type == 'GIF' or video_type == 'PNG'), - gr.update(visible=video_type == 'MP4'), - gr.update(visible=video_type == 'MP4'), - gr.update(visible=video_type == 'MP4'), - gr.update(visible=video_type == 'MP4'), - ] + with gr.Accordion('Create video', open = False): + def video_type_change(video_type): + return [ + gr.update(visible=video_type != 'None'), + gr.update(visible=video_type == 'GIF' or video_type == 'PNG'), + gr.update(visible=video_type == 'MP4'), + gr.update(visible=video_type == 'MP4'), + gr.update(visible=video_type == 'MP4'), + gr.update(visible=video_type == 'MP4'), + ] - with gr.Row(): - gr.HTML("  Video
") - with gr.Row(): - video_type = gr.Dropdown(label='Video file', choices=['None', 'GIF', 'PNG', 'MP4'], value='None', elem_id="extras_video_type") - duration = gr.Slider(label='Duration', minimum=0.25, maximum=10, step=0.25, value=2, visible=False, elem_id="extras_video_duration") - with gr.Row(): - loop = gr.Checkbox(label='Loop', value=True, visible=False, elem_id="extras_video_loop") - pad = gr.Slider(label='Pad frames', minimum=0, maximum=24, step=1, value=1, visible=False, elem_id="extras_video_pad") - interpolate = gr.Slider(label='Interpolate frames', minimum=0, maximum=24, step=1, value=0, visible=False, elem_id="extras_video_interpolate") - scale = gr.Slider(label='Rescale', minimum=0.5, maximum=2, step=0.05, value=1, visible=False, elem_id="extras_video_scale") - change = gr.Slider(label='Frame change sensitivity', minimum=0, maximum=1, step=0.05, value=0.3, visible=False, elem_id="extras_video_change") - with gr.Row(): - filename = gr.Textbox(label='Filename', placeholder='enter filename', lines=1, elem_id="extras_video_filename") - video_type.change(fn=video_type_change, inputs=[video_type], outputs=[duration, loop, pad, interpolate, scale, change]) - return { - "filename": filename, - "video_type": video_type, - "duration": duration, - "loop": loop, - "pad": pad, - "interpolate": interpolate, - "scale": scale, - "change": change, - } + with gr.Row(): + gr.HTML("  Video
") + with gr.Row(): + video_type = gr.Dropdown(label='Video file', choices=['None', 'GIF', 'PNG', 'MP4'], value='None', elem_id="extras_video_type") + duration = gr.Slider(label='Duration', minimum=0.25, maximum=10, step=0.25, value=2, visible=False, elem_id="extras_video_duration") + with gr.Row(): + loop = gr.Checkbox(label='Loop', value=True, visible=False, elem_id="extras_video_loop") + pad = gr.Slider(label='Pad frames', minimum=0, maximum=24, step=1, value=1, visible=False, elem_id="extras_video_pad") + interpolate = gr.Slider(label='Interpolate frames', minimum=0, maximum=24, step=1, value=0, visible=False, elem_id="extras_video_interpolate") + scale = gr.Slider(label='Rescale', minimum=0.5, maximum=2, step=0.05, value=1, visible=False, elem_id="extras_video_scale") + change = gr.Slider(label='Frame change sensitivity', minimum=0, maximum=1, step=0.05, value=0.3, visible=False, elem_id="extras_video_change") + with gr.Row(): + filename = gr.Textbox(label='Filename', placeholder='enter filename', lines=1, elem_id="extras_video_filename") + video_type.change(fn=video_type_change, inputs=[video_type], outputs=[duration, loop, pad, interpolate, scale, change]) + return { + "filename": filename, + "video_type": video_type, + "duration": duration, + "loop": loop, + "pad": pad, + "interpolate": interpolate, + "scale": scale, + "change": change, + } def postprocess(self, images, filename, video_type, duration, loop, pad, interpolate, scale, change): # pylint: disable=arguments-differ filename = filename.strip() if filename is not None else ''