diff --git a/README.md b/README.md index d355db34..c1538100 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ A browser interface based on Gradio library for Stable Diffusion. - CodeFormer, face restoration tool as an alternative to GFPGAN - RealESRGAN, neural network upscaler - ESRGAN, neural network with a lot of third party models + - SwinIR, neural network upscaler + - LDSR, Latent diffusion super resolution upscaling - Resizing aspect ratio options - Sampling method selection - Interrupt processing at any time @@ -41,6 +43,10 @@ A browser interface based on Gradio library for Stable Diffusion. - Seed resizing - CLIP interrogator - Prompt Editing +- Batch Processing +- Img2img Alternative +- Highres Fix +- LDSR Upscaling ## Installation and Running Make sure the required [dependencies](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Dependencies) are met and follow the instructions available for both [NVidia](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Install-and-Run-on-NVidia-GPUs) (recommended) and [AMD](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Install-and-Run-on-AMD-GPUs) GPUs. @@ -79,6 +85,8 @@ The documentation was moved from this README over to the project's [wiki](https: - GFPGAN - https://github.com/TencentARC/GFPGAN.git - CodeFormer - https://github.com/sczhou/CodeFormer - ESRGAN - https://github.com/xinntao/ESRGAN +- SwinIR - https://github.com/JingyunLiang/SwinIR +- LDSR - https://github.com/Hafiidz/latent-diffusion - Ideas for optimizations - https://github.com/basujindal/stable-diffusion - Doggettx - Cross Attention layer optimization - https://github.com/Doggettx/stable-diffusion, original idea for prompt editing. - Idea for SD upscale - https://github.com/jquesnelle/txt2imghd diff --git a/javascript/progressbar.js b/javascript/progressbar.js index 065f259b..20b4dc62 100644 --- a/javascript/progressbar.js +++ b/javascript/progressbar.js @@ -1,27 +1,33 @@ // code related to showing and updating progressbar shown as the image is being made - global_progressbar = null onUiUpdate(function(){ - progressbar = gradioApp().getElementById('progressbar') + progressbar = gradioApp().getElementById('progressbar') + progressDiv = gradioApp().querySelectorAll('.progressDiv').length > 0; + interrupt = gradioApp().getElementById('interrupt') if(progressbar!= null && progressbar != global_progressbar){ global_progressbar = progressbar var mutationObserver = new MutationObserver(function(m){ txt2img_preview = gradioApp().getElementById('txt2img_preview') txt2img_gallery = gradioApp().getElementById('txt2img_gallery') - img2img_preview = gradioApp().getElementById('img2img_preview') img2img_gallery = gradioApp().getElementById('img2img_gallery') if(txt2img_preview != null && txt2img_gallery != null){ txt2img_preview.style.width = txt2img_gallery.clientWidth + "px" - txt2img_preview.style.height = txt2img_gallery.clientHeight + "px" + txt2img_preview.style.height = txt2img_gallery.clientHeight + "px" + if(!progressDiv){ + interrupt.style.display = "none" + } } if(img2img_preview != null && img2img_gallery != null){ img2img_preview.style.width = img2img_gallery.clientWidth + "px" - img2img_preview.style.height = img2img_gallery.clientHeight + "px" + img2img_preview.style.height = img2img_gallery.clientHeight + "px" + if(!progressDiv){ + interrupt.style.display = "none" + } } window.setTimeout(requestMoreProgress, 500) @@ -29,12 +35,15 @@ onUiUpdate(function(){ mutationObserver.observe( progressbar, { childList:true, subtree:true }) } }) - function requestMoreProgress(){ btn = gradioApp().getElementById("check_progress"); if(btn==null) return; btn.click(); + progressDiv = gradioApp().querySelectorAll('.progressDiv').length > 0; + if(progressDiv){ + interrupt.style.display = "block" + } } function requestProgress(){ @@ -43,4 +52,3 @@ function requestProgress(){ btn.click(); } - diff --git a/modules/realesrgan_model.py b/modules/realesrgan_model.py index e2cef0c8..c32d6c4c 100644 --- a/modules/realesrgan_model.py +++ b/modules/realesrgan_model.py @@ -1,17 +1,66 @@ import sys import traceback from collections import namedtuple + import numpy as np from PIL import Image +from realesrgan import RealESRGANer import modules.images from modules.shared import cmd_opts, opts RealesrganModelInfo = namedtuple("RealesrganModelInfo", ["name", "location", "model", "netscale"]) - realesrgan_models = [] have_realesrgan = False -RealESRGANer_constructor = None + + +def get_realesrgan_models(): + try: + from basicsr.archs.rrdbnet_arch import RRDBNet + from realesrgan import RealESRGANer + from realesrgan.archs.srvgg_arch import SRVGGNetCompact + models = [ + RealesrganModelInfo( + name="Real-ESRGAN General x4x3", + location="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.5.0/realesr-general-x4v3.pth", + netscale=4, + model=lambda: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=32, upscale=4, act_type='prelu') + ), + RealesrganModelInfo( + name="Real-ESRGAN General WDN x4x3", + location="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.5.0/realesr-general-wdn-x4v3.pth", + netscale=4, + model=lambda: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=32, upscale=4, act_type='prelu') + ), + RealesrganModelInfo( + name="Real-ESRGAN AnimeVideo", + location="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.5.0/realesr-animevideov3.pth", + netscale=4, + model=lambda: SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=16, upscale=4, act_type='prelu') + ), + RealesrganModelInfo( + name="Real-ESRGAN 4x plus", + location="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth", + netscale=4, + model=lambda: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=4) + ), + RealesrganModelInfo( + name="Real-ESRGAN 4x plus anime 6B", + location="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/RealESRGAN_x4plus_anime_6B.pth", + netscale=4, + model=lambda: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=6, num_grow_ch=32, scale=4) + ), + RealesrganModelInfo( + name="Real-ESRGAN 2x plus", + location="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.1/RealESRGAN_x2plus.pth", + netscale=2, + model=lambda: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=2) + ), + ] + return models + except Exception as e: + print("Error makeing Real-ESRGAN midels list:", file=sys.stderr) + print(traceback.format_exc(), file=sys.stderr) class UpscalerRealESRGAN(modules.images.Upscaler): @@ -27,35 +76,18 @@ class UpscalerRealESRGAN(modules.images.Upscaler): def setup_realesrgan(): global realesrgan_models global have_realesrgan - global RealESRGANer_constructor try: from basicsr.archs.rrdbnet_arch import RRDBNet from realesrgan import RealESRGANer from realesrgan.archs.srvgg_arch import SRVGGNetCompact - realesrgan_models = [ - RealesrganModelInfo( - name="Real-ESRGAN 4x plus", - location="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth", - netscale=4, model=lambda: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=4) - ), - RealesrganModelInfo( - name="Real-ESRGAN 4x plus anime 6B", - location="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.2.4/RealESRGAN_x4plus_anime_6B.pth", - netscale=4, model=lambda: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=6, num_grow_ch=32, scale=4) - ), - RealesrganModelInfo( - name="Real-ESRGAN 2x plus", - location="https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.1/RealESRGAN_x2plus.pth", - netscale=2, model=lambda: RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=2) - ), - ] + realesrgan_models = get_realesrgan_models() have_realesrgan = True - RealESRGANer_constructor = RealESRGANer for i, model in enumerate(realesrgan_models): - modules.shared.sd_upscalers.append(UpscalerRealESRGAN(model.netscale, i)) + if model.name in opts.realesrgan_enabled_models: + modules.shared.sd_upscalers.append(UpscalerRealESRGAN(model.netscale, i)) except Exception: print("Error importing Real-ESRGAN:", file=sys.stderr) @@ -66,13 +98,13 @@ def setup_realesrgan(): def upscale_with_realesrgan(image, RealESRGAN_upscaling, RealESRGAN_model_index): - if not have_realesrgan or RealESRGANer_constructor is None: + if not have_realesrgan: return image info = realesrgan_models[RealESRGAN_model_index] model = info.model() - upsampler = RealESRGANer_constructor( + upsampler = RealESRGANer( scale=info.netscale, model_path=info.location, model=model, diff --git a/modules/shared.py b/modules/shared.py index 280d6c4c..0a852838 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -95,6 +95,11 @@ face_restorers = [] modules.sd_models.list_models() +def realesrgan_models_names(): + import modules.realesrgan_model + return [x.name for x in modules.realesrgan_model.get_realesrgan_models()] + + class Options: class OptionInfo: def __init__(self, default=None, label="", component=None, component_args=None, onchange=None): @@ -142,14 +147,12 @@ class Options: "save_txt": OptionInfo(False, "Create a text file next to every image with generation parameters."), "ESRGAN_tile": OptionInfo(192, "Tile size for ESRGAN upscalers. 0 = no tiling.", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}), "ESRGAN_tile_overlap": OptionInfo(8, "Tile overlap, in pixels for ESRGAN upscalers. Low values = visible seam.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}), + "realesrgan_enabled_models": OptionInfo(["Real-ESRGAN 4x plus", "Real-ESRGAN 4x plus anime 6B"],"Select which RealESRGAN models to show in the web UI. (Requires restart)", gr.CheckboxGroup, lambda: {"choices": realesrgan_models_names()}), "SWIN_tile": OptionInfo(192, "Tile size for all SwinIR.", gr.Slider, {"minimum": 16, "maximum": 512, "step": 16}), "SWIN_tile_overlap": OptionInfo(8, "Tile overlap, in pixels for SwinIR. Low values = visible seam.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}), - "ldsr_steps": OptionInfo(100, "LDSR processing steps. Lower = faster", - gr.Slider, {"minimum": 1, "maximum": 200, "step": 1}), - "ldsr_pre_down":OptionInfo(1, "LDSR Pre-process downssample scale. 1 = no down-sampling, 4 = 1/4 scale.", - gr.Slider, {"minimum": 1, "maximum": 4, "step": 1}), - "ldsr_post_down":OptionInfo(1, "LDSR Post-process down-sample scale. 1 = no down-sampling, 4 = 1/4 scale.", - gr.Slider, {"minimum": 1, "maximum": 4, "step": 1}), + "ldsr_steps": OptionInfo(100, "LDSR processing steps. Lower = faster", gr.Slider, {"minimum": 1, "maximum": 200, "step": 1}), + "ldsr_pre_down":OptionInfo(1, "LDSR Pre-process downssample scale. 1 = no down-sampling, 4 = 1/4 scale.", gr.Slider, {"minimum": 1, "maximum": 4, "step": 1}), + "ldsr_post_down":OptionInfo(1, "LDSR Post-process down-sample scale. 1 = no down-sampling, 4 = 1/4 scale.", gr.Slider, {"minimum": 1, "maximum": 4, "step": 1}), "random_artist_categories": OptionInfo([], "Allowed categories for random artists selection when using the Roll button", gr.CheckboxGroup, {"choices": artist_db.categories()}), "upscale_at_full_resolution_padding": OptionInfo(16, "Inpainting at full resolution: padding, in pixels, for the masked region.", gr.Slider, {"minimum": 0, "maximum": 128, "step": 4}), "upscaler_for_hires_fix": OptionInfo(None, "Upscaler for highres. fix", gr.Radio, lambda: {"choices": [x.name for x in sd_upscalers]}), diff --git a/modules/ui.py b/modules/ui.py index e3cd7f3a..572dcfb9 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -350,8 +350,15 @@ def create_toprow(is_img2img): with gr.Column(scale=1): with gr.Row(): + interrupt = gr.Button('Interrupt', elem_id="interrupt") submit = gr.Button('Generate', elem_id="generate", variant='primary') + interrupt.click( + fn=lambda: shared.state.interrupt(), + inputs=[], + outputs=[], + ) + with gr.Row(): if is_img2img: interrogate = gr.Button('Interrogate', elem_id="interrogate") @@ -386,6 +393,15 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): txt2img_prompt, roll, txt2img_prompt_style, txt2img_negative_prompt, txt2img_prompt_style2, submit, _, txt2img_prompt_style_apply, txt2img_save_style = create_toprow(is_img2img=False) dummy_component = gr.Label(visible=False) + with gr.Row(elem_id='progressRow'): + with gr.Column(scale=1): + columnEmpty = "Empty" + + with gr.Column(scale=1): + progressbar = gr.HTML(elem_id="progressbar") + txt2img_preview = gr.Image(elem_id='txt2img_preview', visible=False) + setup_progressbar(progressbar, txt2img_preview) + with gr.Row().style(equal_height=False): with gr.Column(variant='panel'): steps = gr.Slider(minimum=1, maximum=150, step=1, label="Sampling Steps", value=20) @@ -416,21 +432,17 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): custom_inputs = modules.scripts.scripts_txt2img.setup_ui(is_img2img=False) with gr.Column(variant='panel'): - progressbar = gr.HTML(elem_id="progressbar") with gr.Group(): txt2img_preview = gr.Image(elem_id='txt2img_preview', visible=False) txt2img_gallery = gr.Gallery(label='Output', elem_id='txt2img_gallery').style(grid=4) - setup_progressbar(progressbar, txt2img_preview) - with gr.Group(): with gr.Row(): save = gr.Button('Save') send_to_img2img = gr.Button('Send to img2img') send_to_inpaint = gr.Button('Send to inpaint') send_to_extras = gr.Button('Send to extras') - interrupt = gr.Button('Interrupt') with gr.Group(): html_info = gr.HTML() @@ -479,12 +491,6 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): outputs=[hr_options], ) - interrupt.click( - fn=lambda: shared.state.interrupt(), - inputs=[], - outputs=[], - ) - save.click( fn=wrap_gradio_call(save_files), _js="(x, y, z) => [x, y, selected_gallery_index()]", @@ -513,6 +519,15 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): with gr.Blocks(analytics_enabled=False) as img2img_interface: img2img_prompt, roll, img2img_prompt_style, img2img_negative_prompt, img2img_prompt_style2, submit, img2img_interrogate, img2img_prompt_style_apply, img2img_save_style = create_toprow(is_img2img=True) + with gr.Row(elem_id='progressRow'): + with gr.Column(scale=1): + columnEmpty = "Empty" + + with gr.Column(scale=1): + progressbar = gr.HTML(elem_id="progressbar") + img2img_preview = gr.Image(elem_id='img2img_preview', visible=False) + setup_progressbar(progressbar, img2img_preview) + with gr.Row().style(equal_height=False): with gr.Column(variant='panel'): with gr.Group(): @@ -561,21 +576,17 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): custom_inputs = modules.scripts.scripts_img2img.setup_ui(is_img2img=True) with gr.Column(variant='panel'): - progressbar = gr.HTML(elem_id="progressbar") with gr.Group(): img2img_preview = gr.Image(elem_id='img2img_preview', visible=False) img2img_gallery = gr.Gallery(label='Output', elem_id='img2img_gallery').style(grid=4) - setup_progressbar(progressbar, img2img_preview) - with gr.Group(): with gr.Row(): save = gr.Button('Save') img2img_send_to_img2img = gr.Button('Send to img2img') img2img_send_to_inpaint = gr.Button('Send to inpaint') img2img_send_to_extras = gr.Button('Send to extras') - interrupt = gr.Button('Interrupt') img2img_save_style = gr.Button('Save prompt as style') @@ -689,12 +700,6 @@ def create_ui(txt2img, img2img, run_extras, run_pnginfo): outputs=[img2img_prompt], ) - interrupt.click( - fn=lambda: shared.state.interrupt(), - inputs=[], - outputs=[], - ) - save.click( fn=wrap_gradio_call(save_files), _js="(x, y, z) => [x, y, selected_gallery_index()]", diff --git a/requirements_versions.txt b/requirements_versions.txt index bb88eda9..9372cb69 100644 --- a/requirements_versions.txt +++ b/requirements_versions.txt @@ -1,18 +1,17 @@ transformers==4.19.2 -diffusers==0.2.4 -basicsr==1.3.5 -gfpgan +diffusers==0.3.0 +basicsr==1.4.2 +gfpgan==1.3.8 gradio==3.3.1 numpy==1.23.3 Pillow==9.2.0 -realesrgan==0.2.5.0 +realesrgan==0.3.0 torch -transformers==4.19.2 -omegaconf==2.1.1 -pytorch_lightning==1.7.2 +omegaconf==2.2.3 +pytorch_lightning==1.7.6 scikit-image==0.19.2 fonts font-roboto -timm==0.4.12 -fairscale==0.4.4 +timm==0.6.7 +fairscale==0.4.9 piexif==1.1.3 \ No newline at end of file diff --git a/style.css b/style.css index 96f1402b..655a00e5 100644 --- a/style.css +++ b/style.css @@ -86,7 +86,7 @@ } #style_pos_col, #style_neg_col{ - min-width: 4em !important; + min-width: 8em !important; } #style_index, #style2_index{ @@ -208,11 +208,19 @@ input[type="range"]{ position: absolute; z-index: 1000; right: 0; + padding-left: 5px; + padding-right: 5px; + display: block; +} + +#progressRow{ + margin-bottom: 10px; + margin-top: -18px; } .progressDiv{ width: 100%; - height: 30px; + height: 20px; background: #b4c0cc; border-radius: 8px; } @@ -223,11 +231,11 @@ input[type="range"]{ .progressDiv .progress{ width: 0%; - height: 30px; + height: 20px; background: #0060df; color: white; font-weight: bold; - line-height: 30px; + line-height: 20px; padding: 0 8px 0 0; text-align: right; border-radius: 8px; @@ -337,5 +345,14 @@ input[type="range"]{ background:rgba(255, 0, 0, 0.3); z-index: 900; pointer-events:none; - display:none; + display:none +} + +#interrupt{ + position: absolute; + width: 100%; + height: 72px; + background: #b4c0cc; + border-radius: 8px; + display: none; }