├── Output └── Optional.txt ├── run.bat ├── faces └── Detected faces are extracted here.txt ├── Scripts ├── wildcards │ ├── Descompress all txt files(or only needed) from zip file.txt │ └── wilcards_text_files.zip ├── GFPGan_model │ └── Download model.txt ├── deepdanbooru_onnx_data │ ├── tags_log.json │ ├── Need to download model from here │ ├── Model download from here.txt │ ├── categories.json │ └── project.json ├── facedetector │ ├── version-RFB-320.onnx │ ├── version-RFB-640.onnx │ └── box_utils.py ├── super-resolution │ └── super-resolution-10.onnx ├── superresolution.py ├── wildcards.py ├── utils.py ├── facedetector_onnx.py ├── codeformer_onnx.py ├── deepdanbooru_onnx.py └── image_slicer.py ├── Engine ├── config_files │ ├── TextEnc_Preferences.json │ ├── VAE_Preferences.json │ ├── Styles.json │ ├── UIConfig.json │ ├── EngineConfig.json │ └── ControlNet.json ├── engine_common_funcs.py ├── General_parameters.py ├── txt2img_pipe_sub.py └── Pipelines │ └── txt2img_hires.py ├── latents ├── witch.npy ├── witch.png ├── woman_dancing.npy └── woman_dancing.png ├── Images ├── UIOptions.png ├── CMD-Example.png ├── VAEOptions.png ├── EngineOptions.png ├── ExampleOfSums.png └── ExampleControlNetModel.png ├── CommonModels ├── VaeDec │ └── Optional.txt ├── VaeEnc │ └── Optional.txt ├── TextEnc │ └── Optional.txt ├── VaeDec2 │ └── Optional.txt ├── Controlnet │ ├── Canny │ │ └── Optional.txt │ ├── depth │ │ └── Optional.txt │ └── mlsd │ │ └── Optional.txt └── Optional.txt ├── models └── Download a ONNX model and put its directory here.txt ├── requirements-linux.txt ├── own_pipes └── __pycache__ │ ├── pipeline_onnx_stable_diffusion_hires_txt2img.cpython-310.opt-1.pyc │ └── pipeline_onnx_stable_diffusion_hires_txt2img.cpython-311.opt-1.pyc ├── requirements.txt ├── UI ├── tools_ui │ └── danbooru_ui.py ├── config_ui_wildcards.py ├── UI_common_funcs.py ├── styles_ui.py ├── README.md ├── config_ui_TextEncoder.py ├── config_ui_general.py ├── edit_styles_ui.py ├── config_ui_ControlNet.py ├── config_ui_engine.py ├── config_ui_Vae.py ├── instructp2p_ui.py ├── Inpaint_ui.py ├── ControlNet_ui.py ├── Img2Img_ui.py ├── HiRes_txt2img_ui.py └── txt2img_ui.py ├── ONNX-StableDiffusion.py └── README.md /Output/Optional.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /run.bat: -------------------------------------------------------------------------------- 1 | py -O ONNX-StableDiffusion.py -------------------------------------------------------------------------------- /faces/Detected faces are extracted here.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Scripts/wildcards/Descompress all txt files(or only needed) from zip file.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Engine/config_files/TextEnc_Preferences.json: -------------------------------------------------------------------------------- 1 | ["model", "./CommonModels/TextEnc"] -------------------------------------------------------------------------------- /Scripts/GFPGan_model/Download model.txt: -------------------------------------------------------------------------------- 1 | Download model from : https://huggingface.co/Neus/GFPGANv1.4/ -------------------------------------------------------------------------------- /latents/witch.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/HEAD/latents/witch.npy -------------------------------------------------------------------------------- /latents/witch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/HEAD/latents/witch.png -------------------------------------------------------------------------------- /Images/UIOptions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/HEAD/Images/UIOptions.png -------------------------------------------------------------------------------- /Images/CMD-Example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/HEAD/Images/CMD-Example.png -------------------------------------------------------------------------------- /Images/VAEOptions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/HEAD/Images/VAEOptions.png -------------------------------------------------------------------------------- /Images/EngineOptions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/HEAD/Images/EngineOptions.png -------------------------------------------------------------------------------- /Images/ExampleOfSums.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/HEAD/Images/ExampleOfSums.png -------------------------------------------------------------------------------- /latents/woman_dancing.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/HEAD/latents/woman_dancing.npy -------------------------------------------------------------------------------- /latents/woman_dancing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/HEAD/latents/woman_dancing.png -------------------------------------------------------------------------------- /Engine/config_files/VAE_Preferences.json: -------------------------------------------------------------------------------- 1 | ["model", "./CommonModels/VaeDec", "./CommonModels/VaeDec2", "model", "./CommonModels/VaeEnc", ""] -------------------------------------------------------------------------------- /Images/ExampleControlNetModel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/HEAD/Images/ExampleControlNetModel.png -------------------------------------------------------------------------------- /Scripts/deepdanbooru_onnx_data/tags_log.json: -------------------------------------------------------------------------------- 1 | { 2 | "date": "2021/11/12 22:30:46", 3 | "limit": 10000, 4 | "minimum_post_count": 500 5 | } -------------------------------------------------------------------------------- /CommonModels/VaeDec/Optional.txt: -------------------------------------------------------------------------------- 1 | I will try to upload and provide url for its downloading during next weeks, if you are in need, please leave a message. 2 | 3 | -------------------------------------------------------------------------------- /CommonModels/VaeEnc/Optional.txt: -------------------------------------------------------------------------------- 1 | I will try to upload and provide url for its downloading during next weeks, if you are in need, please leave a message. 2 | 3 | -------------------------------------------------------------------------------- /Scripts/facedetector/version-RFB-320.onnx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/HEAD/Scripts/facedetector/version-RFB-320.onnx -------------------------------------------------------------------------------- /Scripts/facedetector/version-RFB-640.onnx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/HEAD/Scripts/facedetector/version-RFB-640.onnx -------------------------------------------------------------------------------- /Scripts/wildcards/wilcards_text_files.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/HEAD/Scripts/wildcards/wilcards_text_files.zip -------------------------------------------------------------------------------- /CommonModels/TextEnc/Optional.txt: -------------------------------------------------------------------------------- 1 | I will try to upload and provide url for its downloading during next weeks, if you are in need, please leave a message. 2 | 3 | -------------------------------------------------------------------------------- /CommonModels/VaeDec2/Optional.txt: -------------------------------------------------------------------------------- 1 | I will try to upload and provide url for its downloading during next weeks, if you are in need, please leave a message. 2 | 3 | -------------------------------------------------------------------------------- /models/Download a ONNX model and put its directory here.txt: -------------------------------------------------------------------------------- 1 | I will provide a demo version of an onnx model soon, working on uploadting to huggingface or civitai... 2 | -------------------------------------------------------------------------------- /CommonModels/Controlnet/Canny/Optional.txt: -------------------------------------------------------------------------------- 1 | I will try to upload and provide url for its downloading during next weeks, if you are in need, please leave a message. 2 | 3 | -------------------------------------------------------------------------------- /CommonModels/Controlnet/depth/Optional.txt: -------------------------------------------------------------------------------- 1 | I will try to upload and provide url for its downloading during next weeks, if you are in need, please leave a message. 2 | 3 | -------------------------------------------------------------------------------- /CommonModels/Controlnet/mlsd/Optional.txt: -------------------------------------------------------------------------------- 1 | I will try to upload and provide url for its downloading during next weeks, if you are in need, please leave a message. 2 | 3 | -------------------------------------------------------------------------------- /Scripts/super-resolution/super-resolution-10.onnx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/HEAD/Scripts/super-resolution/super-resolution-10.onnx -------------------------------------------------------------------------------- /requirements-linux.txt: -------------------------------------------------------------------------------- 1 | protobuf < 4.0 2 | numpy 3 | transformers 4 | diffusers 5 | ftfy 6 | spacy 7 | scipy 8 | safetensors 9 | gradio 10 | omegaconf 11 | onnx 12 | onnxconverter-common 13 | opencv-python 14 | -------------------------------------------------------------------------------- /CommonModels/Optional.txt: -------------------------------------------------------------------------------- 1 | You might save here one or two VAEs,Text Encoder and ControlNET models ( not an unet modified model for Controlnet...) 2 | Configure them to be shared between all your models, saving disk. 3 | -------------------------------------------------------------------------------- /own_pipes/__pycache__/pipeline_onnx_stable_diffusion_hires_txt2img.cpython-310.opt-1.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/HEAD/own_pipes/__pycache__/pipeline_onnx_stable_diffusion_hires_txt2img.cpython-310.opt-1.pyc -------------------------------------------------------------------------------- /own_pipes/__pycache__/pipeline_onnx_stable_diffusion_hires_txt2img.cpython-311.opt-1.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/HEAD/own_pipes/__pycache__/pipeline_onnx_stable_diffusion_hires_txt2img.cpython-311.opt-1.pyc -------------------------------------------------------------------------------- /Engine/config_files/Styles.json: -------------------------------------------------------------------------------- 1 | {"None": true, "LineArt": " |game art design sheet, Ink drawing line art, ((matte painting fantasy concept art))", "Comic": " comic,|, masterpiece,(best quality), well drawn, artistic", "StudioPhoto": "RAW photo , HDR, |, daylight "} -------------------------------------------------------------------------------- /Scripts/deepdanbooru_onnx_data/Need to download model from here: -------------------------------------------------------------------------------- 1 | You need to download the model from: https://huggingface.co/chinoll/deepdanbooru/tree/main 2 | Or my own converted version from: https://huggingface.co/Neus/Onnx_DeepDanbooru/tree/main 3 | Name it as deepdanbooru.onnx 4 | -------------------------------------------------------------------------------- /Engine/config_files/UIConfig.json: -------------------------------------------------------------------------------- 1 | {"models_dir": ".\\models", "output_path": ".\\output", "Txt2img_Tab": true, "InPaint_Tab": true, "Img2Img_Tab": true, "InstructP2P_Tab": 1, "ControlNet_Tab": 1, "Tools_Tab": true, "Advanced_Config": true, "GradioPort": "7860", "_UI_Configuration__loaded": true} -------------------------------------------------------------------------------- /Scripts/deepdanbooru_onnx_data/Model download from here.txt: -------------------------------------------------------------------------------- 1 | Base version of deepdanbooru.onnx model obtained from:https://huggingface.co/chinoll/deepdanbooru/tree/main 2 | Or my own converted version from: https://huggingface.co/Neus/Onnx_DeepDanbooru/tree/main 3 | 4 | Rename it as deepdanbooru.onnx and save into this directory -------------------------------------------------------------------------------- /Scripts/deepdanbooru_onnx_data/categories.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "General", 4 | "start_index": 0 5 | }, 6 | { 7 | "name": "Character", 8 | "start_index": 6891 9 | }, 10 | { 11 | "name": "System", 12 | "start_index": 9173 13 | } 14 | ] -------------------------------------------------------------------------------- /Engine/config_files/EngineConfig.json: -------------------------------------------------------------------------------- 1 | {"MAINPipe_provider": "DmlExecutionProvider", "Scheduler_provider": "('DmlExecutionProvider', {'device_id': 1,})", "ControlNet_provider": "CPUExecutionProvider", "VAEDec_provider": "('DmlExecutionProvider', {'device_id': 1,})", "TEXTEnc_provider": "CPUExecutionProvider", "DeepDanBooru_provider": "CPUExecutionProvider"} -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | protobuf < 4.0 2 | numpy == 1.26.1 3 | transformers == 4.35.0 4 | diffusers == 0.14.0 5 | ftfy == 6.1.1 6 | spacy == 3.7.2 7 | scipy == 1.11.3 8 | safetensors == 0.4.0 9 | gradio == 3.50.2 10 | omegaconf == 2.3.0 11 | onnx == 1.15.0 12 | onnxconverter-common == 1.14.0 13 | onnxruntime-directml == 1.16.2 14 | opencv-python == 4.8.1.78 15 | -------------------------------------------------------------------------------- /Engine/config_files/ControlNet.json: -------------------------------------------------------------------------------- 1 | {"canny_active": true, "canny_path": "./CommonModels/Controlnet/Canny", "depth_active": true, "depth_path": "./CommonModels/Controlnet/Depth", "hed_active": true, "hed_path": "./CommonModels/Controlnet/Hed", "mlsd_active": false, "mlsd_path": "./CommonModels/Controlnet/Mlsd", "normal_active": false, "normal_path": "./CommonModels/Controlnet/Normal", "openpose_active": false, "openpose_path": "./CommonModels/Controlnet/Openpose", "seg_active": false, "seg_path": "./CommonModels/Controlnet/Seg"} -------------------------------------------------------------------------------- /UI/tools_ui/danbooru_ui.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | def show_danbooru_area(): 6 | global image_in 7 | with gr.Row(): 8 | with gr.Column(variant="compact"): 9 | image_in = gr.Image(label="input image", type="pil", elem_id="image_init") 10 | with gr.Row(): 11 | apply_btn = gr.Button("Analyze image with Deep DanBooru", variant="primary") 12 | mem_btn = gr.Button("Unload from memory") 13 | with gr.Row(): 14 | results = gr.Textbox(value="", lines=8, label="Results") 15 | 16 | mem_btn.click(fn=unload_DanBooru, inputs=results , outputs=results) 17 | apply_btn.click(fn=analyze_DanBooru, inputs=image_in , outputs=results) -------------------------------------------------------------------------------- /Scripts/deepdanbooru_onnx_data/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "image_width": 512, 3 | "image_height": 512, 4 | "database_path": "F:/Dataset/danbooru-family/danbooru-training-20211112.sqlite", 5 | "minimum_tag_count": 20, 6 | "model": "resnet_custom_v3", 7 | "minibatch_size": 4, 8 | "epoch_count": 32, 9 | "export_model_per_epoch": 4, 10 | "checkpoint_frequency_mb": 1000, 11 | "console_logging_frequency_mb": 50, 12 | "optimizer": "sgd", 13 | "learning_rates": [ 14 | { 15 | "used_epoch": 0, 16 | "learning_rate": 5.0 17 | } 18 | ], 19 | "rotation_range": [ 20 | 0.0, 21 | 360.0 22 | ], 23 | "scale_range": [ 24 | 0.9, 25 | 1.1 26 | ], 27 | "shift_range": [ 28 | -0.1, 29 | 0.1 30 | ], 31 | "mixed_precision": false 32 | } -------------------------------------------------------------------------------- /UI/config_ui_wildcards.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | from Engine import General_parameters as params 3 | 4 | global debug 5 | debug = True 6 | 7 | 8 | def show_wilcards_configuration(): 9 | with gr.Blocks(title="ONNX Difussers UI-2") as test2: 10 | with gr.Accordion(label="Wilcards options",open=False): 11 | gr.Markdown("Still not implemented: Wildcards not recursive & Not working option to change marker of wildcards") 12 | with gr.Row(): 13 | Wilcards_Select=gr.Radio(["Yes","No","Yes,not recursive"], label="Wilcards options", info="Activate Yes/No",value="Yes") 14 | Wilcards_Select.change(fn=Generic_Select_Option,inputs=Wilcards_Select,outputs=None) 15 | 16 | def Generic_Select_Option(Radio_Select): 17 | config=params.UI_Configuration() 18 | if Radio_Select == "Yes": 19 | config.wildcards_activated=True 20 | print(params.UI_Configuration().wildcards_activated) 21 | else: 22 | config.wildcards_activated=False 23 | print(params.UI_Configuration().wildcards_activated) 24 | 25 | -------------------------------------------------------------------------------- /Scripts/superresolution.py: -------------------------------------------------------------------------------- 1 | from PIL import Image 2 | #from resizeimage import resizeimage 3 | import numpy as np 4 | import onnxruntime 5 | 6 | global ort_session 7 | ort_session = None 8 | 9 | def superrsolution_pre_process_img(img_path): 10 | orig_img = Image.open(img_path) 11 | ratio= orig_img.size[0]/orig_img.size[1] 12 | img =orig_img.resize([224, 224], Image.Resampling.LANCZOS) 13 | img_ycbcr = img.convert('YCbCr') 14 | img_y_0, img_cb, img_cr = img_ycbcr.split() 15 | img_ndarray = np.asarray(img_y_0) 16 | img_4 = np.expand_dims(np.expand_dims(img_ndarray, axis=0), axis=0) 17 | img_5 = img_4.astype(np.float32) / 255.0 18 | return img_5, img_cb, img_cr,ratio 19 | 20 | 21 | def superrsolution_post_process_img(img_out_y, img_cb, img_cr,ratio=1): 22 | img_out_y = Image.fromarray(np.uint8((img_out_y[0] * 255.0).clip(0, 255)[0]), mode='L') 23 | # get the output image follow post-processing step from PyTorch implementation 24 | final_img = Image.merge( 25 | "YCbCr", [ 26 | img_out_y, 27 | img_cb.resize(img_out_y.size, Image.BICUBIC), 28 | img_cr.resize(img_out_y.size, Image.BICUBIC), 29 | ]).convert("RGB") 30 | final_img=final_img.resize([int(final_img.size[0]*ratio), final_img.size[1]]) 31 | return final_img 32 | 33 | def superresolution_process(img_path): 34 | img_5, img_cb, img_cr, ratio = superrsolution_pre_process_img(img_path) 35 | global ort_session 36 | if ort_session == None: 37 | ort_session = onnxruntime.InferenceSession("./Scripts/super-resolution/super-resolution-10.onnx",providers=['DmlExecutionProvider']) 38 | ort_inputs = {ort_session.get_inputs()[0].name: img_5} 39 | ort_outs = ort_session.run(None, ort_inputs) 40 | img_out_y = ort_outs[0] 41 | img_out_y = superrsolution_post_process_img(img_out_y, img_cb, img_cr,ratio) 42 | img_out_y.save(img_path) 43 | 44 | def clean_superresolution_memory(): 45 | global ort_session 46 | ort_session == None 47 | 48 | -------------------------------------------------------------------------------- /UI/UI_common_funcs.py: -------------------------------------------------------------------------------- 1 | import gc,os 2 | from Engine import pipelines_engines 3 | 4 | from Engine.General_parameters import running_config 5 | from Engine.General_parameters import UI_Configuration 6 | from Engine.Pipelines.txt2img_hires import txt2img_hires_pipe 7 | 8 | def clean_memory_click(): 9 | print("Cleaning Memory") 10 | pipelines_engines.Vae_and_Text_Encoders().unload_from_memory() 11 | pipelines_engines.txt2img_pipe().unload_from_memory() 12 | pipelines_engines.inpaint_pipe().unload_from_memory() 13 | pipelines_engines.instruct_p2p_pipe().unload_from_memory() 14 | pipelines_engines.img2img_pipe().unload_from_memory() 15 | pipelines_engines.ControlNet_pipe().unload_from_memory() 16 | pipelines_engines.ControlNet_pipe().unload_from_memory() 17 | txt2img_hires_pipe().unload_from_memory() 18 | gc.collect() 19 | 20 | def cancel_iteration(): 21 | running_config().Running_information.update({"cancelled":True}) 22 | print("\nCancelling at the end of the current iteration") 23 | 24 | 25 | 26 | def get_model_list(pipeline=None): 27 | model_list = [] 28 | inpaint_model_list = [] 29 | controlnet_model_list = [] 30 | i2p_model_list = [] 31 | txt2img_model_list = [] 32 | 33 | try: 34 | with os.scandir(UI_Configuration().models_dir) as scan_it: 35 | for entry in scan_it: 36 | if entry.is_dir(): 37 | model_list.append(entry.name) 38 | 39 | except: 40 | model_list.append("Models directory does not exist, configure it") 41 | 42 | if len(model_list)>0: 43 | for model in model_list: 44 | if "inpaint" in model.lower(): inpaint_model_list.append(model) 45 | elif "controlnet" in model.lower(): controlnet_model_list.append(model) 46 | elif "ip2p" in model.lower(): i2p_model_list.append(model) 47 | else: txt2img_model_list.append(model) 48 | 49 | if pipeline=="txt2img": retorno=txt2img_model_list 50 | elif pipeline=="inpaint": retorno=inpaint_model_list 51 | elif pipeline=="controlnet": retorno= controlnet_model_list 52 | elif pipeline=="ip2p": retorno= i2p_model_list 53 | else: retorno= model_list 54 | 55 | return retorno -------------------------------------------------------------------------------- /UI/styles_ui.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | #from Engine.General_parameters import ControlNet_config 3 | from UI import styles_ui 4 | global styles_dict 5 | 6 | def show_styles_ui(): 7 | global styles_dict 8 | styles_dict= get_styles() 9 | styles_keys= list(styles_dict.keys()) 10 | if True: 11 | with gr.Accordion(label="Styles",open=False): 12 | gr.Markdown("Use your preferred Styles") 13 | with gr.Row(): 14 | Style_Select = gr.Radio(styles_keys,value=styles_keys[0],label="Available Styles") 15 | with gr.Row(): 16 | with gr.Accordion(label="Style - modificate running config",open=False): 17 | styletext_pre = gr.Textbox(value="", lines=2, label="Style previous text") 18 | styletext_post = gr.Textbox(value="", lines=2, label="Style posterior text") 19 | with gr.Row(): 20 | apply_btn = gr.Button("Apply Modified Style") 21 | reload_btn = gr.Button("Reload Styles") 22 | 23 | all_inputs=[Style_Select,styletext_pre,styletext_post] 24 | 25 | apply_btn.click(fn=saveto_memory_style, inputs=all_inputs, outputs=None) 26 | reload_btn.click(fn=reload_styles, inputs=None, outputs=Style_Select) 27 | Style_Select.change(fn=apply_styles, inputs=Style_Select, outputs=[styletext_pre,styletext_post]) 28 | 29 | def get_styles(): 30 | import json 31 | """dict={ 32 | "None":True, 33 | "StudioPhoto":"(RAW, 8k) |, studio lights,pseudo-impasto", 34 | "Style1":"(cartoon) |, Ink drawing line art", 35 | "Style2":"unity wallpaper, 8k, high quality, | masterpiece,(masterpiece, top quality, best quality)" 36 | }""" 37 | with open('./Engine/config_files/Styles.json', 'r') as openfile: 38 | jsonStr = json.load(openfile) 39 | 40 | jsonStr.update({"None":True}) 41 | return jsonStr 42 | 43 | def reload_styles(*args): 44 | global styles_dict 45 | styles_dict=get_styles() 46 | styles_keys= list(styles_dict.keys()) 47 | apply_styles("None") 48 | return gr.Radio.update(choices=styles_keys,value="None") 49 | 50 | 51 | def apply_styles(*args): 52 | global styles_dict 53 | dict=styles_dict 54 | style=args[0] 55 | 56 | from Engine.General_parameters import running_config 57 | Running_information= running_config().Running_information 58 | Running_information.update({"Style":False}) 59 | 60 | if style != "None": 61 | Running_information.update({"Style":dict[style]}) 62 | params=dict[style].split("|") 63 | return params[0],params[1] 64 | else: 65 | return "","" 66 | 67 | def saveto_memory_style(*args): 68 | import json 69 | global styles_dict 70 | styles_dict 71 | 72 | style=args[0] 73 | style_pre=args[1] 74 | style_post=args[2] 75 | 76 | 77 | from Engine.General_parameters import running_config 78 | Running_information= running_config().Running_information 79 | Running_information.update({"Style":False}) 80 | 81 | if style != "None": 82 | styles_dict.update({style:f"{style_pre}|{style_post}"}) 83 | Running_information.update({"Style":styles_dict[style]}) 84 | 85 | 86 | -------------------------------------------------------------------------------- /Scripts/wildcards.py: -------------------------------------------------------------------------------- 1 | # Wildcards 2 | #V2 Extended to provide info on status area 3 | #An extension modificated version of a script from https://github.com/jtkelm2/stable-diffusion-webui-1/blob/main/scripts/wildcards.py 4 | #Extracted from module wildcards for AUTOMATIC111 5 | #Allows you to use `__name__` syntax in your prompt to get a random line from a file named `name.txt` in the wildcards directory. 6 | # 1- Create a directory "Scripts" in OnnxDifussersUI, save this file inside as wildcards.py 7 | # 2- Put wildcard files in a directory named "wildcards" inside "Scripts" 8 | # 3- Include following lines in onnxUI.py .Do not forget to delete the comment mark at the beginning of each line to make it work (#) 9 | 10 | # In line 107 (just before:if current_pipe == "txt2img":" 11 | 12 | # # Manage WildCards 13 | # from Scripts import wildcards 14 | # WildCards_Activated= True 15 | # if WildCards_Activated: 16 | # old_prompt=prompt 17 | # wildcard=wildcards.WildcardsScript() 18 | # new_prompt,string_replaced=wildcard.process(prompt) 19 | # prompt=str(new_prompt) 20 | # new_prompts.append(string_replaced) 21 | 22 | # Add this line just before the line 121, before "elif current_pipe == "img2img":" 23 | # prompt=old_prompt 24 | 25 | # Optional: add this two lines between line 271 ")" and 272 (if current_pipe == "img2img":) to save the generated prompt info to the png file. 26 | # if new_prompt != prompt: 27 | # info_png = info_png + f" Wildcards generated change: {string_replaced}" 28 | # Line 604 -- after "global original_steps" 29 | # global new_prompts 30 | # new_prompts=[] 31 | # 32 | # Line 1034 33 | # status=status+"\nWildcards changed:\n"+str(new_prompts) 34 | # 35 | 36 | 37 | import os 38 | import random 39 | import sys 40 | 41 | warned_about_files = {} 42 | wildcard_dir = os.getcwd()+"\Scripts" 43 | #print(wildcard_dir) 44 | 45 | 46 | class WildcardsScript(): 47 | def title(self): 48 | return "Simple wildcards class for OnnxDiffusersUI" 49 | 50 | def replace_wildcard(self, text, gen): 51 | if " " in text or len(text) == 0: 52 | return text,False 53 | 54 | replacement_file = os.path.join(wildcard_dir, "wildcards", f"{text}.txt") 55 | if os.path.exists(replacement_file): 56 | with open(replacement_file, encoding="utf8") as f: 57 | changed_text=gen.choice(f.read().splitlines()) 58 | if "__" in changed_text: 59 | changed_text, not_used = self.process(changed_text) 60 | return changed_text,True 61 | else: 62 | if replacement_file not in warned_about_files: 63 | print(f"File {replacement_file} not found for the __{text}__ wildcard.", file=sys.stderr) 64 | warned_about_files[replacement_file] = 1 65 | 66 | return text,False 67 | 68 | def process(self, original_prompt): 69 | string_replaced="" 70 | new_prompt="" 71 | gen = random.Random() 72 | text_divisions=original_prompt.split("__") 73 | 74 | for chunk in text_divisions: 75 | text,changed=self.replace_wildcard(chunk, gen) 76 | if changed: 77 | string_replaced=string_replaced+"Wildcard:"+chunk+"-->"+text+"," 78 | new_prompt=new_prompt+text 79 | else: 80 | new_prompt=new_prompt+text 81 | 82 | return new_prompt, string_replaced 83 | -------------------------------------------------------------------------------- /UI/README.md: -------------------------------------------------------------------------------- 1 | Only for Txt2Img: Latent Experimentals: 2 | 3 | 4 | The idea is to use previous generated images to create new ones, using their generated latents. 5 | This works as a good ControlNet variation, but also allows to create completely new compositions, additional benefit i observed: using same generated info with same prompt might provide you with an excellent result in definition and in the final composition as the model will focus faster on the right elements.... 6 | 7 | This works (for only one latent or many) saving first their generated latents as numpy files and then applying them during first steps of a new generation. 8 | Working with one latent it's easy, but using many might be tricky. 9 | 10 | Schedulers: Only Euler Ancestral and DDIM (with ETA=1), good results could start to be observed from 16-20 steps, multiplier:0'4 to 0'55 and strengh:0'8 - 0'9, guidance: 8-14 11 | 12 | -Checkbox: "Save generated latents": save the latent for the current on-going generation (or while it is activated) into the latents subdir, to re-use them on further generations 13 | -Checkbox: "Load latent from generation" load the generated latent and apply it to the next generation 14 | -Name of Numpy latens: the latents are saved as .npy or numpy files, here you introducte the name (or names) of the numpy files to be applied (more on this later). 15 | -Formula, when using more than one latent you will need to define here how they will create the final image (more on this later).. 16 | ![ExampleOfSums](https://github.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/assets/94193584/a5c3ec9b-d94d-4c4f-a342-919b95cdf433) 17 | 18 | IMPORTANT: you are only allowed to use latents (or sumatory of them) of same size to the output image. If you are using a latent for a generated 512x512 img you will be only allowed to use it for a new 512x512 image, or ir you use two latents, you will be able to sum them alongside the dimension that is equal in both files, adding the total size of the other dimension:one is 384x640 latent and another 256x640, result 640x640), trying to create a composition with complete diferent sizes will not work. ( cannot add 256x512 to 384x640....), be careful with this. Try first with only one file and get used to how it works prior trying to work with many latents.... 19 | Example: one latent (width X height) 256x704 is added horizontally to a vertical composition of two other latents 384x192 and 384x512 (sum=384x640) : 640x704. 20 | 21 | 22 | Name of Numpy- Latent: 23 | You must write down here all the names of the latents files (.npy) you will use, comma separated, you need to assign them an index to be use a reference at the formula area. 24 | Format is: "index":"name of numpy file with extension.npy", "index2":"name of same or different numpy file with extension.npy", etc...(the max index allowed needs to be the equal to the total of files to name, if not it will not work) 25 | 26 | 27 | The formula indicating the order and the direction for the sumatory of the files, use parentesis marks for enclosing operations according to their relevancy 28 | For the previous example:one latent (width X height) 256x704 is added horizontally to a vertical composition of two other latents 384x192 and 384x512 (sum=384x640) : 640x704. 29 | This will be need to be write down this way: 30 | 31 | Files: "1:file1.npy,2:file2.npy,3:another.npy" 32 | Formula: 1w(2h3) 33 | w: witdh, horizontal sumatory (remember, their height must be the same) 34 | h: height, vertical sumatory (remember, their width must be the same) 35 | This is a horizontal sumatory of 1 with the vertical sumatory of 2 and 3 : 36 | Yoy might complicate it as much as you want, as the dimension of the files is ok and the total size obtained is the same as the proposed generation size. 37 | i.e. 1h((2w4)h3) 1 vertical(height) sum of the result of ((2 horizonal sum with 4) and vertical sum of 3) 38 | Formula for one file : only provided index of the file ("1:file1.npy,2:file2.npy,3:another.npy" index are 1,2 or 3) 39 | -------------------------------------------------------------------------------- /ONNX-StableDiffusion.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | from Engine.General_parameters import Engine_Configuration 3 | from Engine.General_parameters import UI_Configuration 4 | from Engine.General_parameters import running_config 5 | 6 | def init_ui(): 7 | ui_config=UI_Configuration() 8 | with gr.Blocks(title="ONNX Difussers Modular UI",css= css1) as demo: 9 | if True: 10 | with gr.Tab(label="Testing HiRes Txt2img Pipeline") as tab10: 11 | from UI import HiRes_txt2img_ui as HiRes_txt2img_ui 12 | HiRes_txt2img_ui.show_HiRes_txt2img_ui() 13 | if ui_config.Txt2img_Tab: 14 | with gr.Tab(label="Txt2img Pipelines & Inferences") as tab0: 15 | from UI import txt2img_ui as txt2img_ui 16 | txt2img_ui.show_txt2img_ui() 17 | if ui_config.Img2Img_Tab: 18 | with gr.Tab(label="Img2Img") as tab1: 19 | from UI import Img2Img_ui 20 | Img2Img_ui.show_Img2Img_ui() 21 | if ui_config.InPaint_Tab: 22 | with gr.Tab(label="InPaint") as tab2: 23 | from UI import Inpaint_ui 24 | Inpaint_ui.show_Inpaint_ui() 25 | if ui_config.Tools_Tab: 26 | with gr.Tab(label="Image Tools") as tab3: 27 | from UI import ui_image_tools 28 | ui_image_tools.show_input_image_area() 29 | ui_image_tools.show_danbooru_area() 30 | ui_image_tools.show_image_resolution_area() 31 | if ui_config.InstructP2P_Tab: 32 | with gr.Tab(label="Instruct Pix2Pix") as tab4: 33 | from UI import instructp2p_ui 34 | instructp2p_ui.show_instructp2p_ui() 35 | if ui_config.ControlNet_Tab: 36 | with gr.Tab(label="ControlNet") as tab5: 37 | from UI import ControlNet_ui 38 | ControlNet_ui.show_ControlNet_ui() 39 | with gr.Tab(label="Configuration") as tab6: 40 | from UI import config_ui_general 41 | from UI import config_ui_ControlNet 42 | from UI import config_ui_Vae 43 | from UI import config_ui_TextEncoder 44 | 45 | config_ui_general.show_general_configuration() 46 | if ui_config.Advanced_Config: 47 | from UI import config_ui_engine as config_ui_engine 48 | config_ui_engine.show_providers_configuration() 49 | config_ui_ControlNet.show_controlnet_models_configuration() 50 | config_ui_TextEncoder.show_textenc_models_configuration() 51 | config_ui_Vae.show_vae_models_configuration() 52 | #from UI import config_ui_wildcards as wilcards_ui_config 53 | #wilcards_ui_config.show_wilcards_configuration() 54 | 55 | return demo 56 | 57 | 58 | css1 = """ 59 | #title1 {background-color: #00ACAA;text-transform: uppercase; font-weight: bold;} 60 | .feedback textarea {font-size: 24px !important} 61 | """ 62 | 63 | Running_information= running_config().Running_information 64 | Running_information.update({"cancelled":False}) 65 | Running_information.update({"model":""}) 66 | Running_information.update({"ControlNetModel":""}) #check if used 67 | Running_information.update({"tab":""}) 68 | Running_information.update({"Running":False}) 69 | Running_information.update({"Save_Latents":False}) 70 | Running_information.update({"Load_Latents":False}) 71 | Running_information.update({"Latent_Name":""}) 72 | Running_information.update({"Latent_Formula":""}) 73 | Running_information.update({"Callback_Steps":2}) 74 | Running_information.update({"Vae_Config":["model"]*6}) 75 | Running_information.update({"Textenc_Config":["model"]*2}) 76 | Running_information.update({"offset":1}) 77 | Running_information.update({"Style":False}) 78 | 79 | 80 | Engine_Configuration().load_config_json() 81 | demo =init_ui() 82 | demo.launch(server_port=UI_Configuration().GradioPort) 83 | 84 | 85 | -------------------------------------------------------------------------------- /UI/config_ui_TextEncoder.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | from Engine.General_parameters import running_config 3 | from Engine.General_parameters import TextEnc_config 4 | 5 | def show_textenc_models_configuration(): 6 | TextencConfig=load_textenc_preferences__ui() 7 | apply_textenc_config_ui(list(TextencConfig.values())) 8 | 9 | 10 | if True: 11 | with gr.Accordion(label="Text Encoder Models Order & Directories",open=False): 12 | gr.Markdown("""Saving disk space\n 13 | Instead of saving one duplicate for every model as they are usually the same one(two options available only), save one instance of a Text Encoder model\n 14 | into a directory and write down their path.\n 15 | The system will try to apply your 1st option, if not found it will go for 2nd.""") 16 | with gr.Row(): 17 | gr.Markdown("Options for Text Encoder.",elem_id="title1") 18 | with gr.Row(): 19 | with gr.Column(scale=8): 20 | gr.Markdown("Selected model own Text encoder.") 21 | with gr.Column(scale=2): 22 | model1_textenc_order=gr.Slider(1, 2, value=TextencConfig["model1_textenc_order"], step=1, label="Own model Text Encoder search order", interactive=True) 23 | with gr.Row(): 24 | with gr.Column(scale=8): 25 | model2_textenc_path=gr.Textbox(label="Text Encoder model full path",lines=1, value=TextencConfig["model2_textenc_path"], visible=True, interactive=True) 26 | with gr.Column(scale=2): 27 | model2_textenc_order=gr.Slider(1, 2, value=TextencConfig["model2_textenc_order"], step=1, label="This Text Encoder search order", interactive=True) 28 | 29 | 30 | save_btn = gr.Button("Apply & Save Text Encoder models config") 31 | load_btn = gr.Button("Load Text Encoder models config") 32 | 33 | all_inputs=[model1_textenc_order,model2_textenc_order,model2_textenc_path] 34 | save_btn.click(fn=save_textenc_config_ui, inputs=all_inputs, outputs=None) 35 | load_btn.click(fn=load_textenc_preferences__ui2, inputs=None , outputs=all_inputs) 36 | 37 | def load_textenc_preferences__ui2(): 38 | return list(load_textenc_preferences__ui().values()) 39 | 40 | def load_textenc_preferences__ui(): 41 | config=TextEnc_config() 42 | textenc_config=config.load_config_from_disk() 43 | #Fast parse,hard-coded, as they are only 2 elements each, for more, do another approach. -recursive funtion 44 | 45 | if textenc_config[0]=="model": 46 | model1_textenc_order=1 47 | model2_textenc_order=2 48 | model2_textenc_path=textenc_config[1] 49 | elif textenc_config[1]=="model": 50 | model2_textenc_order=1 51 | model2_textenc_path=textenc_config[0] 52 | model1_textenc_order=2 53 | 54 | 55 | all_inputs={ 56 | "model1_textenc_order":model1_textenc_order, 57 | "model2_textenc_order":model2_textenc_order, 58 | "model2_textenc_path":model2_textenc_path} 59 | 60 | return dict(all_inputs) 61 | 62 | 63 | def apply_textenc_config_ui(*args): 64 | _save_textenc_config_ui(False, *args) 65 | 66 | def save_textenc_config_ui(*args): 67 | _save_textenc_config_ui(True, *args) 68 | 69 | def _save_textenc_config_ui(save=True,*args): 70 | if not save: 71 | args=args[0] #is tupla, select the list of args. 72 | model1_textenc_order=int(args[0]) 73 | model2_textenc_order=int(args[1]) 74 | model2_textenc_path=args[2] 75 | 76 | textenc_config =[None] * 2 77 | textenc_config[model1_textenc_order-1]="model" 78 | textenc_config[model2_textenc_order-1]=model2_textenc_path 79 | 80 | 81 | Running_information= running_config().Running_information 82 | Running_information.update({"Textenc_Config":textenc_config}) 83 | if save: 84 | TextEnc_config().save_TextEnc_config(textenc_config) 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /UI/config_ui_general.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | import os 3 | 4 | from Engine.General_parameters import UI_Configuration as UI_Configuration 5 | 6 | 7 | models_dir=f"{os.getcwd()}"+'\\models' 8 | 9 | def show_general_configuration(): 10 | ui_config=UI_Configuration() 11 | with gr.Blocks(title="General Config") as ConfigUI: 12 | with gr.Accordion(label="UI Options",open=False): 13 | gr.Markdown("Directories configuracion") 14 | with gr.Row(): 15 | Models_Dir_Select=gr.Textbox(label="Models Directory",lines=1, value=ui_config.models_dir, visible=True, interactive=True) 16 | Output_Path_Select=gr.Textbox(label="Output Directory",lines=1, value=ui_config.output_path, visible=True, interactive=True) 17 | with gr.Row(): 18 | Txt2img_Tab = gr.Checkbox(label="Txt2img Tab",value=ui_config.Txt2img_Tab, info="De/Activate TAB(Applied in next run)") 19 | InPaint_Tab = gr.Checkbox(label="In-Paint Tab",value=ui_config.InPaint_Tab, info="De/Activate TAB(Applied in next run)") 20 | Img2Img_Tab = gr.Checkbox(label="Img2Img Tab",value=ui_config.Img2Img_Tab, info="De/Activate TAB(Applied in next run)") 21 | InstructP2P_Tab = gr.Checkbox(label="InstructP2P Tab",value=ui_config.InstructP2P_Tab, info="De/Activate TAB(Applied in next run)") 22 | Tools_Tab = gr.Checkbox(label="Image Tools Tab",value=ui_config.Tools_Tab, info="De/Activate TAB(Applied in next run)") 23 | ControlNet_Tab = gr.Checkbox(label="ControlNet Tab",value=ui_config.ControlNet_Tab, info="De/Activate TAB(Applied in next run)") 24 | 25 | with gr.Row(): 26 | Advanced_Config = gr.Checkbox(label="Advanced Config",value=ui_config.Advanced_Config, info="Deactivate Avanced Options(Applied in next run)") 27 | UI_NetworkPort=gr.Textbox(label="Gradio Server Port",lines=1, value=ui_config.GradioPort, visible=True, interactive=True) 28 | with gr.Row(): 29 | apply_btn = gr.Button("Apply & Save config", variant="primary") 30 | #loadconfig_btn = gr.Button("Load saved config") 31 | with gr.Row(): 32 | from UI import edit_styles_ui 33 | edit_styles_ui.show_edit_styles_ui() 34 | 35 | UI_options=[Models_Dir_Select,Output_Path_Select,Txt2img_Tab, InPaint_Tab, Img2Img_Tab,InstructP2P_Tab,Tools_Tab, ControlNet_Tab, Advanced_Config,UI_NetworkPort] 36 | apply_btn.click(fn=applyandsave, inputs=UI_options,outputs=None) 37 | #loadconfig_btn.click(fn=loadconfig, inputs=Img2Img_Tab,outputs=Img2Img_Tab) 38 | 39 | def loadconfig(grImg2Img_Tab): 40 | ui_config=UI_Configuration() 41 | ui_config.Txt2img_Tab=False 42 | return gr.Checkbox.update(value=ui_config.Txt2img_Tab) 43 | 44 | 45 | 46 | def applyandsave(Models_Dir_Select,Output_Path_Select,Txt2img_Tab, 47 | InPaint_Tab, Img2Img_Tab,InstructP2P_Tab,Tools_Tab, 48 | ControlNet_Tab, Advanced_Config,UI_NetworkPort): 49 | 50 | ui_config=UI_Configuration() 51 | ui_config.models_dir=Models_Dir_Select 52 | ui_config.output_path=Output_Path_Select 53 | ui_config.Txt2img_Tab=Txt2img_Tab 54 | ui_config.InPaint_Tab=InPaint_Tab 55 | ui_config.Img2Img_Tab=Img2Img_Tab 56 | ui_config.ControlNet_Tab=ControlNet_Tab 57 | ui_config.Tools_Tab=Tools_Tab 58 | ui_config.Advanced_Config=Advanced_Config 59 | ui_config.InstructP2P_Tab = int(InstructP2P_Tab) 60 | ui_config.GradioPort = UI_NetworkPort 61 | 62 | #print(ui_config) 63 | print("Applied and saved, to work with these settings: clean memory and run any pipeline") 64 | ui_config.save_config_json() 65 | 66 | 67 | def Generic_Select_Option(Radio_Select): 68 | config=UI_Configuration() 69 | if Radio_Select == "Yes": 70 | config.wildcards_activated=True 71 | print(params.UI_Configuration().wildcards_activated) 72 | else: 73 | config.wildcards_activated=False 74 | print(params.UI_Configuration().wildcards_activated) 75 | 76 | def load_values(): 77 | return 78 | 79 | -------------------------------------------------------------------------------- /UI/edit_styles_ui.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | from UI import styles_ui 3 | global styles_dict 4 | 5 | def show_edit_styles_ui(): 6 | global styles_dict 7 | styles_dict= get_styles() 8 | styles_keys= list(styles_dict.keys()) 9 | if True: 10 | with gr.Accordion(label="Styles",open=False): 11 | gr.Markdown("Edit your preferred Styles") 12 | with gr.Row(): 13 | with gr.Column(scale=1): 14 | Style_Select = gr.Radio(styles_keys,value=styles_keys[0],label="Available Styles") 15 | with gr.Column(scale=8): 16 | styletext_name = gr.Textbox(value="", lines=1, label="Style name") 17 | styletext_pre = gr.Textbox(value="", lines=2, label="Style previous text") 18 | styletext_post = gr.Textbox(value="", lines=2, label="Style posterior text") 19 | with gr.Row(): 20 | save_btn = gr.Button("Save this Style") 21 | new_style_btn = gr.Button("Create New Blank Style") 22 | del_style_btn = gr.Button("Delete Selected Style") 23 | 24 | all_inputs=[Style_Select,styletext_pre,styletext_post,styletext_name] 25 | del_style_btn.click(fn=delete_style, inputs=Style_Select, outputs=Style_Select) 26 | save_btn.click(fn=save_styles, inputs=all_inputs, outputs=Style_Select) 27 | Style_Select.change(fn=select_style, inputs=Style_Select, outputs=[styletext_name,styletext_pre,styletext_post,Style_Select]) 28 | new_style_btn.click(fn=add_new_style, inputs=all_inputs, outputs=Style_Select) 29 | 30 | def add_new_style(*args): 31 | global styles_dict 32 | styles_dict.update({'NewStyle':" | "}) 33 | return Update_StyleSelect() 34 | 35 | def get_styles(): 36 | import json 37 | """dict={ 38 | "None":True, 39 | "StudioPhoto":"(RAW, 8k) |, studio lights,pseudo-impasto", 40 | "Style1":"(cartoon) |, Ink drawing line art", 41 | "Style2":"unity wallpaper, 8k, high quality, | masterpiece,(masterpiece, top quality, best quality)" 42 | }""" 43 | with open('./Engine/config_files/Styles.json', 'r') as openfile: 44 | jsonStr = json.load(openfile) 45 | #Aqui añadir por si es la primera ejecucion y no existe el fichero") 46 | 47 | jsonStr.update({"None":True}) 48 | return jsonStr 49 | 50 | 51 | def select_style(*args): 52 | global styles_dict 53 | dict=styles_dict 54 | style=args[0] 55 | 56 | if style != "None": 57 | params=dict[style].split("|") 58 | return style,params[0],params[1], Update_StyleSelect() 59 | else: 60 | return "None","","",gr.Radio.update(visible=True) 61 | 62 | def delete_style(*args): 63 | import json 64 | global styles_dict 65 | styles_dict 66 | 67 | style=args[0] 68 | 69 | if style != "None": 70 | styles_dict.pop(style) 71 | jsonStr = json.dumps(styles_dict) 72 | with open("./Engine/config_files/Styles.json", "w") as outfile: 73 | outfile.write(jsonStr) 74 | print("Saving Styles without this Style") 75 | else: 76 | print("Cannot Delete the empty Style") 77 | 78 | return Update_StyleSelect() 79 | 80 | def Update_StyleSelect(*args): 81 | global styles_dict 82 | styles_keys= list(styles_dict.keys()) 83 | return gr.Radio.update(choices=styles_keys) 84 | 85 | def save_styles(*args): 86 | import json 87 | global styles_dict 88 | styles_dict 89 | 90 | style=args[0] 91 | style_pre=args[1] 92 | style_post=args[2] 93 | style_name=args[3] 94 | 95 | if style != "None": 96 | styles_dict.pop(style) 97 | styles_dict.update({style_name:f"{style_pre}|{style_post}"}) 98 | jsonStr = json.dumps(styles_dict) 99 | with open("./Engine/config_files/Styles.json", "w") as outfile: 100 | outfile.write(jsonStr) 101 | print("Saving Style") 102 | 103 | return Update_StyleSelect() 104 | 105 | 106 | -------------------------------------------------------------------------------- /Engine/engine_common_funcs.py: -------------------------------------------------------------------------------- 1 | 2 | ##Here only common functions who does not call UI variables 3 | import os,re,PIL 4 | from PIL import Image, PngImagePlugin 5 | 6 | 7 | 8 | 9 | 10 | def numpy_to_pil(images): 11 | """ 12 | Convert a numpy image or a batch of images to a PIL image. 13 | """ 14 | from PIL import Image 15 | if images.ndim == 3: 16 | images = images[None, ...] 17 | images = (images * 255).round().astype("uint8") 18 | if images.shape[-1] == 1: 19 | # special case for grayscale (single channel) images 20 | pil_images = [Image.fromarray(image.squeeze(), mode="L") for image in images] 21 | else: 22 | pil_images = [Image.fromarray(image) for image in images] 23 | return pil_images 24 | 25 | 26 | 27 | def get_next_save_index(output_path): 28 | #output_path=UI_Configuration().output_path 29 | dir_list = os.listdir(output_path) 30 | if len(dir_list): 31 | pattern = re.compile(r"([0-9][0-9][0-9][0-9][0-9][0-9])-([0-9][0-9])\..*") 32 | match_list = [pattern.match(f) for f in dir_list] 33 | next_index = max([int(m[1]) if m else -1 for m in match_list]) + 1 34 | else: 35 | next_index = 0 36 | return next_index 37 | 38 | 39 | def save_image(batch_images,info,next_index,output_path,style=None,save_textfile=False): 40 | #output_path=UI_Configuration().output_path 41 | info_png = f"{info}" 42 | metadata = PngImagePlugin.PngInfo() 43 | metadata.add_text("parameters",info_png) 44 | prompt=info["prompt"] 45 | 46 | style_pre ="" 47 | style_post="" 48 | style_pre_len=0 49 | style_post_len=0 50 | 51 | if style: 52 | styles=style.split("|") 53 | style_pre =styles[0] 54 | style_pre_len=len(style_pre) 55 | style_post=styles[1] 56 | style_post_len=len(style_post) 57 | prompt=prompt[style_pre_len-1:-style_post_len] 58 | """print(f"prompt:{prompt}") 59 | else: 60 | print("No tocado el prompt")""" 61 | 62 | short_prompt1 = prompt.strip("(){}<>:\"/\\|?*\n\t") 63 | short_prompt1 = re.sub(r'[\\/*?:"<>()|\n\t]', "", short_prompt1) 64 | short_prompt = short_prompt1[:49] if len(short_prompt1) > 50 else short_prompt1 65 | os.makedirs(output_path, exist_ok=True) 66 | 67 | i=0 68 | process_tags=True 69 | if not process_tags: 70 | for image in batch_images: 71 | image.save(os.path.join(output_path,f"{next_index:06}-0{i}_{short_prompt}.png",),optimize=True,pnginfo=metadata,) 72 | i+=1 73 | else: 74 | for image in batch_images: 75 | image.save(os.path.join(output_path,f"{next_index:06}-0{i}.png",),optimize=True,pnginfo=metadata,) 76 | if save_textfile and (i==0): 77 | with open(os.path.join(output_path,f"{next_index:06}-0{i}.txt"), 'w',encoding='utf8') as txtfile: 78 | txtfile.write(f"{short_prompt1} \nstyle_pre:{style_pre}\nstyle_post:{style_post}") 79 | i+=1 80 | 81 | def PIL_resize_and_crop(input_image: PIL.Image.Image, height: int, width: int): 82 | input_width, input_height = input_image.size 83 | 84 | # nearest neighbor for upscaling 85 | if (input_width * input_height) < (width * height): 86 | resample_type = Image.NEAREST 87 | # lanczos for downscaling 88 | else: 89 | resample_type = Image.LANCZOS 90 | 91 | if height / width > input_height / input_width: 92 | adjust_width = int(input_width * height / input_height) 93 | input_image = input_image.resize((adjust_width, height), 94 | resample=resample_type) 95 | left = (adjust_width - width) // 2 96 | right = left + width 97 | input_image = input_image.crop((left, 0, right, height)) 98 | else: 99 | adjust_height = int(input_height * width / input_width) 100 | input_image = input_image.resize((width, adjust_height), 101 | resample=resample_type) 102 | top = (adjust_height - height) // 2 103 | bottom = top + height 104 | input_image = input_image.crop((0, top, width, bottom)) 105 | return input_image 106 | 107 | -------------------------------------------------------------------------------- /Scripts/facedetector/box_utils.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | import numpy as np 4 | 5 | def area_of(left_top, right_bottom): 6 | """ 7 | Compute the areas of rectangles given two corners. 8 | Args: 9 | left_top (N, 2): left top corner. 10 | right_bottom (N, 2): right bottom corner. 11 | Returns: 12 | area (N): return the area. 13 | """ 14 | hw = np.clip(right_bottom - left_top, 0.0, None) 15 | return hw[..., 0] * hw[..., 1] 16 | 17 | def iou_of(boxes0, boxes1, eps=1e-5): 18 | """ 19 | Return intersection-over-union (Jaccard index) of boxes. 20 | Args: 21 | boxes0 (N, 4): ground truth boxes. 22 | boxes1 (N or 1, 4): predicted boxes. 23 | eps: a small number to avoid 0 as denominator. 24 | Returns: 25 | iou (N): IoU values. 26 | """ 27 | overlap_left_top = np.maximum(boxes0[..., :2], boxes1[..., :2]) 28 | overlap_right_bottom = np.minimum(boxes0[..., 2:], boxes1[..., 2:]) 29 | 30 | overlap_area = area_of(overlap_left_top, overlap_right_bottom) 31 | area0 = area_of(boxes0[..., :2], boxes0[..., 2:]) 32 | area1 = area_of(boxes1[..., :2], boxes1[..., 2:]) 33 | return overlap_area / (area0 + area1 - overlap_area + eps) 34 | 35 | def hard_nms(box_scores, iou_threshold, top_k=-1, candidate_size=200): 36 | """ 37 | Perform hard non-maximum-supression to filter out boxes with iou greater 38 | than threshold 39 | Args: 40 | box_scores (N, 5): boxes in corner-form and probabilities. 41 | iou_threshold: intersection over union threshold. 42 | top_k: keep top_k results. If k <= 0, keep all the results. 43 | candidate_size: only consider the candidates with the highest scores. 44 | Returns: 45 | picked: a list of indexes of the kept boxes 46 | """ 47 | scores = box_scores[:, -1] 48 | boxes = box_scores[:, :-1] 49 | picked = [] 50 | indexes = np.argsort(scores) 51 | indexes = indexes[-candidate_size:] 52 | while len(indexes) > 0: 53 | current = indexes[-1] 54 | picked.append(current) 55 | if 0 < top_k == len(picked) or len(indexes) == 1: 56 | break 57 | current_box = boxes[current, :] 58 | indexes = indexes[:-1] 59 | rest_boxes = boxes[indexes, :] 60 | iou = iou_of( 61 | rest_boxes, 62 | np.expand_dims(current_box, axis=0), 63 | ) 64 | indexes = indexes[iou <= iou_threshold] 65 | 66 | return box_scores[picked, :] 67 | 68 | def predict(width, height, confidences, boxes, prob_threshold, iou_threshold=0.5, top_k=-1): 69 | """ 70 | Select boxes that contain human faces 71 | Args: 72 | width: original image width 73 | height: original image height 74 | confidences (N, 2): confidence array 75 | boxes (N, 4): boxes array in corner-form 76 | iou_threshold: intersection over union threshold. 77 | top_k: keep top_k results. If k <= 0, keep all the results. 78 | Returns: 79 | boxes (k, 4): an array of boxes kept 80 | labels (k): an array of labels for each boxes kept 81 | probs (k): an array of probabilities for each boxes being in corresponding labels 82 | """ 83 | boxes = boxes[0] 84 | confidences = confidences[0] 85 | #print(boxes) 86 | #print(confidences) 87 | 88 | picked_box_probs = [] 89 | picked_labels = [] 90 | for class_index in range(1, confidences.shape[1]): 91 | #print(confidences.shape[1]) 92 | probs = confidences[:, class_index] 93 | #print(probs) 94 | mask = probs > prob_threshold 95 | probs = probs[mask] 96 | 97 | if probs.shape[0] == 0: 98 | continue 99 | subset_boxes = boxes[mask, :] 100 | #print(subset_boxes) 101 | box_probs = np.concatenate([subset_boxes, probs.reshape(-1, 1)], axis=1) 102 | box_probs = hard_nms(box_probs, 103 | iou_threshold=iou_threshold, 104 | top_k=top_k, 105 | ) 106 | picked_box_probs.append(box_probs) 107 | picked_labels.extend([class_index] * box_probs.shape[0]) 108 | if not picked_box_probs: 109 | return np.array([]), np.array([]), np.array([]) 110 | picked_box_probs = np.concatenate(picked_box_probs) 111 | picked_box_probs[:, 0] *= width 112 | picked_box_probs[:, 1] *= height 113 | picked_box_probs[:, 2] *= width 114 | picked_box_probs[:, 3] *= height 115 | return picked_box_probs[:, :4].astype(np.int32), np.array(picked_labels), picked_box_probs[:, 4] 116 | -------------------------------------------------------------------------------- /UI/config_ui_ControlNet.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | from Engine.General_parameters import ControlNet_config 3 | 4 | def show_controlnet_models_configuration(): 5 | ControlNetConfig=ControlNet_config().config 6 | 7 | if True: 8 | with gr.Accordion(label="Select ControlNet Models Directories",open=False): 9 | gr.Markdown("Instead of saving duplicates of each ControlNet model for every model, save one instance of generic ControlNet models into a directory and write down here their path.Include full name and extension") 10 | with gr.Row(): 11 | with gr.Column(scale=1): 12 | canny_active = gr.Checkbox(label="Canny Model Activated?", value=ControlNetConfig["canny_active"], interactive=True) 13 | with gr.Column(scale=8): 14 | canny_path=gr.Textbox(label="Canny model full path",lines=1, value=ControlNetConfig["canny_path"], visible=True, interactive=True) 15 | with gr.Row(): 16 | with gr.Column(scale=1): 17 | depth_active = gr.Checkbox(label="Depth Model Activated?", value=ControlNetConfig["depth_active"], interactive=True) 18 | with gr.Column(scale=8): 19 | depth_path=gr.Textbox(label="Depth Model full path",lines=1, value=ControlNetConfig["depth_path"], visible=True, interactive=True) 20 | with gr.Row(): 21 | with gr.Column(scale=1): 22 | hed_active = gr.Checkbox(label="Hed Model Activated?", value=ControlNetConfig["hed_active"], interactive=True) 23 | with gr.Column(scale=8): 24 | hed_path=gr.Textbox(label="Hed Model full path",lines=1, value=ControlNetConfig["hed_path"], visible=True, interactive=True) 25 | with gr.Row(): 26 | with gr.Column(scale=1): 27 | mlsd_active = gr.Checkbox(label="Mlsd Model Activated?", value=ControlNetConfig["mlsd_active"], interactive=True) 28 | with gr.Column(scale=8): 29 | mlsd_path=gr.Textbox(label="Mlsd Model full path",lines=1, value=ControlNetConfig["mlsd_path"], visible=True, interactive=True) 30 | with gr.Row(): 31 | with gr.Column(scale=1): 32 | normal_active = gr.Checkbox(label="Normal Model Activated?", value=ControlNetConfig["normal_active"], interactive=True) 33 | with gr.Column(scale=8): 34 | normal_path=gr.Textbox(label="Normal Model full path",lines=1, value=ControlNetConfig["normal_path"], visible=True, interactive=True) 35 | with gr.Row(): 36 | with gr.Column(scale=1): 37 | openpose_active = gr.Checkbox(label="Openpose Model Activated?", value=ControlNetConfig["openpose_active"], interactive=True) 38 | with gr.Column(scale=8): 39 | openpose_path=gr.Textbox(label="Openpose full path",lines=1, value=ControlNetConfig["openpose_path"], visible=True, interactive=True) 40 | with gr.Row(): 41 | with gr.Column(scale=1): 42 | seg_active = gr.Checkbox(label="Seg Model Activated?", value=ControlNetConfig["seg_active"], interactive=True) 43 | with gr.Column(scale=8): 44 | seg_path=gr.Textbox(label="Seg Model full path",lines=1, value=ControlNetConfig["seg_path"], visible=True, interactive=True) 45 | with gr.Row(): 46 | save_btn = gr.Button("Apply & Save ControlNet models config") 47 | load_btn = gr.Button("Load ControlNet models config") 48 | 49 | all_inputs=[ 50 | canny_active,canny_path,depth_active,depth_path,hed_active,hed_path,mlsd_active,mlsd_path, 51 | normal_active, normal_path,openpose_active,openpose_path,seg_active,seg_path] 52 | 53 | save_btn.click(fn=save_controlnet_config_ui, inputs=all_inputs, outputs=None) 54 | load_btn.click(fn=load_controlnet_config_ui, inputs=None , outputs=all_inputs) 55 | 56 | 57 | def load_controlnet_config_ui(): 58 | config=ControlNet_config() 59 | config.load_config_from_disk() 60 | return list(ControlNet_config().config.values()) 61 | 62 | 63 | def save_controlnet_config_ui(*args): 64 | controlnet_config= { 65 | "canny_active":args[0], 66 | "canny_path":args[1], 67 | "depth_active":args[2], 68 | "depth_path":args[3], 69 | "hed_active":args[4], 70 | "hed_path":args[5], 71 | "mlsd_active":args[6], 72 | "mlsd_path":args[7], 73 | "normal_active":args[8], 74 | "normal_path":args[9], 75 | "openpose_active":args[10], 76 | "openpose_path":args[11], 77 | "seg_active":args[12], 78 | "seg_path":args[13], 79 | } 80 | ControlNet_config().save_controlnet_config(controlnet_config) 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /Scripts/utils.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import numpy as np 3 | 4 | 5 | def get_names(inp, prefix): 6 | if not isinstance(inp, (tuple, list)): 7 | inp = [inp] 8 | 9 | names = [] 10 | for i, sub_inp in enumerate(inp): 11 | sub_prefix = '{}.{}'.format(prefix, i) 12 | if isinstance(sub_inp, (list, tuple)): 13 | names.extend(get_names(sub_inp, sub_prefix)) 14 | else: 15 | names.append(sub_prefix) 16 | 17 | return names 18 | 19 | 20 | def get_forms(inp): 21 | if not isinstance(inp, (tuple, list)): 22 | return 'x' 23 | 24 | forms = [] 25 | for sub_inp in inp: 26 | forms.append(get_forms(sub_inp)) 27 | 28 | return forms 29 | 30 | 31 | def to(inp, device_or_dtype): 32 | if not isinstance(inp, (tuple, list)): 33 | if type(inp).__module__ == torch.__name__: 34 | if device_or_dtype == 'torch': 35 | pass 36 | elif device_or_dtype == 'numpy': 37 | inp = inp.detach().cpu().numpy() 38 | else: 39 | inp = inp.to(device_or_dtype) 40 | elif type(inp).__module__ == np.__name__: 41 | if not isinstance(inp, np.ndarray): 42 | inp = np.array(inp) 43 | 44 | if device_or_dtype == 'torch': 45 | inp = torch.from_numpy(inp) 46 | elif device_or_dtype == 'numpy': 47 | pass 48 | else: 49 | inp = inp.astype(device_or_dtype) 50 | elif isinstance(inp, (int, float)): 51 | if device_or_dtype == 'torch': 52 | inp = torch.tensor(inp) 53 | elif device_or_dtype == 'numpy': 54 | inp = np.array(inp) 55 | else: 56 | raise TypeError(('Unsupported type {}, expect int, float, ' 57 | 'np.ndarray or torch.Tensor').format(type(inp))) 58 | 59 | return inp 60 | 61 | out = [] 62 | for sub_inp in inp: 63 | out.append(to(sub_inp, device_or_dtype)) 64 | 65 | return out 66 | 67 | 68 | def flatten(inp): 69 | if not isinstance(inp, (tuple, list)): 70 | return [inp] 71 | 72 | out = [] 73 | for sub_inp in inp: 74 | out.extend(flatten(sub_inp)) 75 | 76 | return out 77 | 78 | 79 | def reconstruct(inp, forms): 80 | assert len(flatten(inp)) == len(flatten(forms)) 81 | 82 | if not isinstance(forms, (tuple, list)): 83 | if isinstance(inp, (tuple, list)): 84 | assert len(inp) == 1 85 | return inp[0] 86 | else: 87 | return inp 88 | 89 | out = [] 90 | index = 0 91 | for sub_form in forms: 92 | if isinstance(sub_form, (tuple, list)): 93 | sub_form_len = len(flatten(sub_form)) 94 | out.append(reconstruct(inp[:sub_form_len], sub_form)) 95 | index += sub_form_len 96 | else: 97 | out.append(inp[index]) 98 | index += 1 99 | 100 | return out 101 | 102 | 103 | def add_batch_dim(inp): 104 | if not isinstance(inp, (list, tuple)): 105 | return inp[None, ...] 106 | 107 | out = [] 108 | for sub_inp in inp: 109 | out.append(add_batch_dim(sub_inp)) 110 | 111 | return out 112 | 113 | 114 | def cat(x, y, dim=0): 115 | x = list(x) if isinstance(x, (tuple, list)) else x 116 | y = list(y) if isinstance(y, (tuple, list)) else y 117 | 118 | assert type(x) == type(y) 119 | 120 | if isinstance(x, list): 121 | assert len(x) == len(y) 122 | 123 | out = [] 124 | for sub_x, sub_y in zip(x, y): 125 | out.append(cat(sub_x, sub_y, dim)) 126 | return out 127 | elif isinstance(x, torch.Tensor): 128 | return torch.cat([x, y], dim=dim) 129 | elif isinstance(x, np.ndarray): 130 | return np.concatenate([x, y], axis=dim) 131 | else: 132 | raise TypeError(('Unsupported data type {}, ' 133 | 'expect np.ndarray or torch.Tensor').format(type(x))) 134 | 135 | 136 | def gen_ones(shape): 137 | if isinstance(shape[0], int): 138 | return torch.ones(*shape) 139 | 140 | data = [] 141 | for sub_shape in shape: 142 | data.append(gen_ones(sub_shape)) 143 | 144 | return data 145 | 146 | 147 | def fetch_batch(data, start, end): 148 | if isinstance(data, torch.Tensor): 149 | return data[start:end] 150 | 151 | assert not isinstance(data, (tuple, list)), ( 152 | 'Unsupported data type {}, only torch.Tensor, ' 153 | 'tuple or list are supported').format(type(data)) 154 | 155 | batch = [] 156 | for sub_data in data: 157 | batch.append(fetch_batch(sub_data, start, end)) 158 | 159 | return batch -------------------------------------------------------------------------------- /Scripts/facedetector_onnx.py: -------------------------------------------------------------------------------- 1 | #Class code, initial model and tags obtained from https://huggingface.co/chinoll 2 | #new model version converted on: 3 | 4 | 5 | """import onnxruntime as ort 6 | from PIL import Image 7 | import numpy as np 8 | import os 9 | from tqdm import tqdm 10 | import requests 11 | import hashlib 12 | from typing import List, Union 13 | import shutil 14 | from pathlib import Path 15 | import hashlib""" 16 | 17 | import cv2 18 | import onnxruntime as ort 19 | #import argparse #no utilizaremos args, vendra una imagen directamente. 20 | import numpy as np 21 | from Scripts.facedetector.box_utils import predict 22 | from Engine.General_parameters import Engine_Configuration 23 | 24 | 25 | 26 | """def download_model(): 27 | return "./Scripts/deepdanbooru_onnx_data/deepdanbooru.onnx", "./Scripts/deepdanbooru_onnx_data/tags.txt" 28 | """ 29 | 30 | 31 | class FaceDetector: 32 | face_detector = None 33 | def __init__(self): 34 | if self.face_detector == None: 35 | exec_provider=Engine_Configuration().DeepDanBooru_provider 36 | providers = { 37 | "gpu":"DMLExecutionProvider", 38 | "cpu":"CPUExecutionProvider", 39 | "gpu":"CUDAExecutionProvider", 40 | "auto":exec_provider, 41 | } 42 | face_detector_onnx = "./Scripts/facedetector/version-RFB-320.onnx" 43 | face_detector_onnx = "./Scripts/facedetector/version-RFB-640.onnx" 44 | import onnxruntime as ort 45 | sess_options = ort.SessionOptions() 46 | sess_options.log_severity_level=3 47 | self.face_detector = ort.InferenceSession(face_detector_onnx,providers=providers,sess_options=sess_options) 48 | 49 | def face_restore(self,face): 50 | print("Face restoring do not implemented yet") 51 | return face 52 | 53 | def paste_face(self,img_with_boxes,box,restored_face): 54 | from PIL import Image 55 | #print("Entrando en pegar cara") 56 | Image.Image.paste(img_with_boxes, restored_face, (box[0],box[1])) 57 | return img_with_boxes 58 | 59 | def run_session(self,image): 60 | #convert to cv2 61 | image = image.convert('RGB') 62 | image = np.array(image) 63 | # Convert RGB to BGR 64 | orig_image = image[:, :, ::-1].copy() 65 | orig_image2 = orig_image.copy() 66 | color = (255, 128, 0) #rectangle color 67 | boxes, labels, probs = self.faceDetector(orig_image2) 68 | faces=[] 69 | height, width, _ = image.shape 70 | for i in range(boxes.shape[0]): 71 | box = self.scale(boxes[i, :],width,height) 72 | #box =boxes[i, :] 73 | face_img=self.cropImage(orig_image,box) 74 | faces.append(face_img) 75 | cv2.rectangle(orig_image2, (box[0], box[1]), (box[2], box[3]), color, 1) 76 | #cv2.imwrite(f"./faces/face{i}.png",face_img) 77 | boxes[i]=box 78 | 79 | from PIL import Image 80 | orig_image2 = cv2.cvtColor(orig_image2, cv2.COLOR_BGR2RGB) 81 | orig_image2 = Image.fromarray(orig_image2) 82 | faces2=[] 83 | for face in faces: 84 | face2 = cv2.cvtColor(face, cv2.COLOR_BGR2RGB) 85 | faces2.append(Image.fromarray(face2)) 86 | 87 | return orig_image2,faces2,boxes 88 | 89 | def __str__(self) -> str: 90 | return f"FaceDetector" #(mode={self.mode}, threshold={self.threshold}, pin_memory={self.pin_memory}, batch_size={self.batch_size})" 91 | 92 | def __repr__(self) -> str: 93 | return self.__str__() 94 | 95 | def __call__(self, image): 96 | return self.run_session(image) 97 | 98 | # scale current rectangle to box 99 | def scale(self,box,width0,height0): 100 | normal=((width0+height0)/4)/10 101 | box[0]= 0 if (box[0]-normal)<0 else (box[0]-normal) 102 | box[1]= 0 if (box[1]-normal)<0 else (box[1]-normal) 103 | box[2]= width0-1 if (box[2]+normal)>width0-1 else (box[2]+normal) 104 | box[3]= height0-1 if (box[3]+normal)>height0-1 else (box[3]+normal) 105 | 106 | 107 | width = box[2] - box[0] 108 | height = box[3] - box[1] 109 | #print(f"width:{width}") 110 | #print(f"height:{height}") 111 | maximum = max(width, height) 112 | if (box[2]+maximum)>width0-1:maximum = width 113 | if (box[3]+maximum)>height0-1:maximum = height 114 | #dx = int((maximum - width)/2) 115 | #dy = int((maximum - height)/2) 116 | #print(box) 117 | bboxes = [box[0], box[1], box[0]+maximum , box[1]+maximum] 118 | #bboxes = [box[0] - dx, box[1] - dy, box[2] + dx, box[3] + dy] 119 | return bboxes 120 | 121 | # crop image 122 | def cropImage(self,image, box): 123 | num = image[box[1]:box[3], box[0]:box[2]] 124 | return num 125 | 126 | # face detection method 127 | def faceDetector(self,orig_image, threshold = 0.7): 128 | image = orig_image 129 | image = cv2.cvtColor(orig_image, cv2.COLOR_BGR2RGB) 130 | #image = cv2.resize(image, (320, 240)) 131 | image = cv2.resize(image, (640, 480)) 132 | image_mean = np.array([127, 127, 127]) 133 | image = (image - image_mean) / 128 134 | image = np.transpose(image, [2, 0, 1]) 135 | image = np.expand_dims(image, axis=0) 136 | image = image.astype(np.float32) 137 | 138 | input_name = self.face_detector.get_inputs()[0].name 139 | confidences, boxes = self.face_detector.run(None, {input_name: image}) 140 | boxes, labels, probs = predict(orig_image.shape[1], orig_image.shape[0], confidences, boxes, threshold) 141 | return boxes, labels, probs 142 | 143 | def select_box(self,orig_image,point_x, point_y,side_size=128): 144 | import numpy as np 145 | import cv2 146 | 147 | width, height = orig_image.size 148 | orig_image = orig_image.convert('RGB') 149 | orig_image = np.array(orig_image) 150 | # Convert RGB to BGR 151 | orig_image2 = orig_image[:, :, ::-1].copy() 152 | orig_image3 = orig_image2.copy() 153 | color = (255, 128, 0) #rectangle color 154 | 155 | point_x= point_x if (point_x+side_size 0 else None), label="model folder", interactive=True) 21 | prompt_t0 = gr.Textbox(value="", lines=2, label="prompt") 22 | sch_t0 = gr.Radio(sched_list, value=sched_list[0], label="scheduler") 23 | image_t0 = gr.Image(source="upload",label="input image", type="pil", elem_id="image_p2p") 24 | iter_t0 = gr.Slider(1, 100, value=1, step=1, label="iteration count", visible=False) 25 | steps_t0 = gr.Slider(1, 300, value=16, step=1, label="steps") 26 | guid_t0 = gr.Slider(0, 50, value=7.5, step=0.1, label="guidance") 27 | height_t0 = gr.Slider(256, 2048, value=512, step=64, label="height") 28 | width_t0 = gr.Slider(256, 2048, value=512, step=64, label="width") 29 | eta_t0 = gr.Slider(0, 1, value=0.0, step=0.01, label="DDIM eta", interactive=True) 30 | seed_t0 = gr.Textbox(value="", max_lines=1, label="seed") 31 | fmt_t0 = gr.Radio(["png", "jpg"], value="png", label="image format", visible=False) 32 | with gr.Column(scale=11, min_width=550): 33 | with gr.Row(): 34 | gen_btn = gr.Button("Process", variant="primary", elem_id="gen_button") 35 | #cancel_btn = gr.Button("Cancel",info="Cancel at end of current iteration",variant="stop", elem_id="gen_button") 36 | memory_btn = gr.Button("Release memory", elem_id="mem_button") 37 | with gr.Row(): 38 | image_out = gr.Gallery(value=None, label="output images") 39 | 40 | with gr.Row(): 41 | status_out = gr.Textbox(value="", label="status") 42 | 43 | 44 | #cancel_btn.click(fn=UI_common.cancel_iteration,inputs=None,outputs=None) 45 | 46 | list_of_All_Parameters=[model_drop,prompt_t0,sch_t0,image_t0,iter_t0,steps_t0,guid_t0,height_t0,width_t0,eta_t0,seed_t0,fmt_t0] 47 | gen_btn.click(fn=generate_click, inputs=list_of_All_Parameters, outputs=[image_out,status_out]) 48 | #sch_t0.change(fn=select_scheduler, inputs=sch_t0, outputs= None) #Atencion cambiar el DDIM ETA si este se activa 49 | memory_btn.click(fn=UI_common.clean_memory_click, inputs=None, outputs=None) 50 | 51 | 52 | def get_model_list(): 53 | model_list = [] 54 | try: 55 | with os.scandir(UI_Configuration().models_dir) as scan_it: 56 | for entry in scan_it: 57 | if entry.is_dir(): 58 | model_list.append(entry.name) 59 | except: 60 | model_list.append("Models directory does not exist, configure it") 61 | return model_list 62 | 63 | def get_schedulers_list(): 64 | sched_config = pipelines_engines.SchedulersConfig() 65 | sched_list =sched_config.available_schedulers 66 | return sched_list 67 | 68 | def select_scheduler(sched_name,model_path): 69 | return pipelines_engines.SchedulersConfig().scheduler(sched_name,model_path) 70 | 71 | def generate_click(model_drop,prompt_t0,sch_t0,image_t0,iter_t0,steps_t0,guid_t0,height_t0,width_t0,eta_t0,seed_t0,fmt_t0): 72 | from Engine.pipelines_engines import instruct_p2p_pipe 73 | 74 | Running_information= running_config().Running_information 75 | Running_information.update({"Running":True}) 76 | model_path=ui_config=UI_Configuration().models_dir+"\\"+model_drop 77 | 78 | if (Running_information["model"] != model_drop or Running_information["tab"] != "instruct_p2p"): 79 | UI_common.clean_memory_click() 80 | Running_information.update({"model":model_drop}) 81 | Running_information.update({"tab":"instruct_p2p"}) 82 | 83 | instruct_p2p_pipe().initialize(model_path,sch_t0) 84 | input_image = resize_and_crop(image_t0, height_t0, width_t0) 85 | 86 | # generate seed for iteration 87 | instruct_p2p_pipe().create_seed(seed_t0) 88 | #images= [] 89 | #information=[] 90 | #counter=1 91 | #img_index=get_next_save_index() 92 | images,info = instruct_p2p_pipe().run_inference( 93 | prompt=prompt_t0, 94 | input_image=input_image, 95 | steps=steps_t0, 96 | guid=guid_t0, 97 | eta=eta_t0) 98 | Running_information.update({"Running":False}) 99 | info=dict(info) 100 | info['Sched:']=sch_t0 101 | #information.append(info) 102 | return images,info 103 | 104 | 105 | def get_next_save_index(): 106 | output_path=UI_Configuration().output_path 107 | dir_list = os.listdir(output_path) 108 | if len(dir_list): 109 | pattern = re.compile(r"([0-9][0-9][0-9][0-9][0-9][0-9])-([0-9][0-9])\..*") 110 | match_list = [pattern.match(f) for f in dir_list] 111 | next_index = max([int(m[1]) if m else -1 for m in match_list]) + 1 112 | else: 113 | next_index = 0 114 | return next_index 115 | 116 | 117 | def save_image(batch_images,info,next_index): 118 | output_path=UI_Configuration().output_path 119 | 120 | info_png = f"{info}" 121 | metadata = PngImagePlugin.PngInfo() 122 | metadata.add_text("parameters",info_png) 123 | prompt=info["prompt"] 124 | short_prompt = prompt.strip("<>:\"/\\|?*\n\t") 125 | short_prompt = re.sub(r'[\\/*?:"<>|\n\t]', "", short_prompt) 126 | short_prompt = short_prompt[:49] if len(short_prompt) > 50 else short_prompt 127 | 128 | os.makedirs(output_path, exist_ok=True) 129 | """dir_list = os.listdir(output_path) 130 | if len(dir_list): 131 | pattern = re.compile(r"([0-9][0-9][0-9][0-9][0-9][0-9])-([0-9][0-9])\..*") 132 | match_list = [pattern.match(f) for f in dir_list] 133 | next_index = max([int(m[1]) if m else -1 for m in match_list]) + 1 134 | else: 135 | next_index = 0""" 136 | for image in batch_images: 137 | image.save(os.path.join(output_path,f"{next_index:06}-00.{short_prompt}.png",),optimize=True,pnginfo=metadata,) 138 | 139 | 140 | 141 | 142 | 143 | def resize_and_crop(input_image: PIL.Image.Image, height: int, width: int): 144 | input_width, input_height = input_image.size 145 | 146 | # nearest neighbor for upscaling 147 | if (input_width * input_height) < (width * height): 148 | resample_type = Image.NEAREST 149 | # lanczos for downscaling 150 | else: 151 | resample_type = Image.LANCZOS 152 | 153 | if height / width > input_height / input_width: 154 | adjust_width = int(input_width * height / input_height) 155 | input_image = input_image.resize((adjust_width, height), 156 | resample=resample_type) 157 | left = (adjust_width - width) // 2 158 | right = left + width 159 | input_image = input_image.crop((left, 0, right, height)) 160 | else: 161 | adjust_height = int(input_height * width / input_width) 162 | input_image = input_image.resize((width, adjust_height), 163 | resample=resample_type) 164 | top = (adjust_height - height) // 2 165 | bottom = top + height 166 | input_image = input_image.crop((0, top, width, bottom)) 167 | return input_image -------------------------------------------------------------------------------- /Scripts/codeformer_onnx.py: -------------------------------------------------------------------------------- 1 | #Class code, initial model and tags obtained from 2 | 3 | 4 | import onnxruntime as ort 5 | from PIL import Image 6 | import numpy as np 7 | import os 8 | from tqdm import tqdm 9 | import requests 10 | import hashlib 11 | from typing import List, Union 12 | import shutil 13 | from pathlib import Path 14 | import hashlib 15 | from Engine.General_parameters import Engine_Configuration 16 | import cv2 17 | 18 | def download_model(): 19 | #return "./Scripts/correction-codeformer.onnx" 20 | #return "./Scripts/correction-gfpgan-v1-3.onnx" 21 | #Download model from : https://huggingface.co/Neus/GFPGANv1.4/ 22 | return "./Scripts/GFPGan_model/GFPGANv1.4.onnx" 23 | 24 | class CodeFormer: 25 | def __init__(self, mode: str = "auto"): 26 | ''' 27 | Initialize the class. 28 | ''' 29 | exec_provider=Engine_Configuration().DeepDanBooru_provider 30 | providers = { 31 | "cpu":"CPUExecutionProvider", 32 | #"gpu":"CUDAExecutionProvider", 33 | "gpu":'DmlExecutionProvider', 34 | #"tensorrt": "TensorrtExecutionProvider", 35 | #"auto":"CUDAExecutionProvider" if "CUDAExecutionProvider" in ort.get_available_providers() else "CPUExecutionProvider", 36 | "auto":exec_provider, 37 | #"auto":'DmlExecutionProvider', 38 | } 39 | 40 | if mode not in providers: 41 | raise ValueError("Mode not supported. Please choose from: cpu, gpu, tensorrt") 42 | if providers[mode] not in ort.get_available_providers(): 43 | raise ValueError(f"Your device is not supported {mode}. Please choose from: cpu") 44 | 45 | model_path = download_model() 46 | 47 | #self.session = ort.InferenceSession(model_path, providers=[providers[mode]]) 48 | #self.session = ort.InferenceSession(model_path, providers=['DmlExecutionProvider', 'CPUExecutionProvider']) 49 | self.session = ort.InferenceSession(model_path, providers=['CPUExecutionProvider']) 50 | 51 | self.input_name = self.session.get_inputs()[0].name 52 | """print(f"Shape:{self.session.get_inputs()[0].shape}") 53 | print(f"Long salida:{len(self.session.get_outputs())}") 54 | print("Sessiones inicio") 55 | print(self.session.get_inputs()[0]) 56 | print(self.session.get_inputs())""" 57 | self.output_name = [output.name for output in self.session.get_outputs()] 58 | self.mode = mode 59 | self.cache = {} 60 | 61 | def __str__(self) -> str: 62 | return f"Codeformer(mode={self.mode}, inputs:{self.input_name }, outputs:{self.output_name})" 63 | 64 | def __repr__(self) -> str: 65 | return self.__str__() 66 | 67 | def __call__(self, image): 68 | return self.process_image(image) 69 | 70 | def preprocess(self,img): 71 | #import cv2 72 | import numpy as np 73 | newsize = (512, 512) 74 | img = img.resize(newsize) 75 | img = np.asarray(img)#.astype('float32') 76 | #img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) 77 | img = img / 255.0 78 | 79 | img[:,:,0] = (img[:,:,0]-0.5)/0.5 80 | img[:,:,1] = (img[:,:,1]-0.5)/0.5 81 | img[:,:,2] = (img[:,:,2]-0.5)/0.5 82 | 83 | img = np.float32(img[np.newaxis,:,:,:]) 84 | img = img.transpose(0, 3, 1, 2) 85 | return img 86 | 87 | 88 | def process_image(self,image): 89 | import cv2 90 | image = self.preprocess(image) 91 | #dict={self.input_name:torch_img.cpu().numpy()} 92 | dict={self.input_name:image} 93 | output=self.session.run(self.output_name, dict)[0] 94 | 95 | output = output[0] 96 | output = output.clip(0,1) 97 | output = output.transpose(1, 2, 0) 98 | 99 | #output = cv2.cvtColor(output, cv2.COLOR_BGR2RGB) 100 | output = (output + 1) / 2 101 | output = (output * 255.0).round() 102 | img = output.astype(np.uint8) 103 | #print(output) 104 | return img 105 | 106 | def numpy_to_pil(images): 107 | """ 108 | Convert a numpy image or a batch of images to a PIL image. 109 | """ 110 | from PIL import Image 111 | if images.ndim == 3: 112 | images = images[None, ...] 113 | images = (images * 255).round().astype("uint8") 114 | if images.shape[-1] == 1: 115 | # special case for grayscale (single channel) images 116 | pil_images = [Image.fromarray(image.squeeze(), mode="L") for image in images] 117 | else: 118 | pil_images = [Image.fromarray(image) for image in images] 119 | return pil_images 120 | 121 | 122 | 123 | 124 | 125 | class GFPGANFaceAugment: 126 | def __init__(self, model_path, use_gpu = False): 127 | self.ort_session = ort.InferenceSession(model_path,providers=('DmlExecutionProvider', 'CPUExecutionProvider')) 128 | self.net_input_name = self.ort_session.get_inputs()[0].name 129 | _,self.net_input_channels,self.net_input_height,self.net_input_width = self.ort_session.get_inputs()[0].shape 130 | self.net_output_count = len(self.ort_session.get_outputs()) 131 | self.face_size = 512 132 | self.face_template = np.array([[192, 240], [319, 240], [257, 371]]) * (self.face_size / 512.0) 133 | self.upscale_factor = 2 134 | self.affine = False 135 | self.affine_matrix = None 136 | 137 | def pre_process(self, img): 138 | img = cv2.resize(img, (int(img.shape[1] / 2), int(img.shape[0] / 2))) 139 | img = cv2.resize(img, (self.face_size, self.face_size)) 140 | img = img / 255.0 141 | img = img.astype('float32') 142 | img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 143 | img[:,:,0] = (img[:,:,0]-0.5)/0.5 144 | img[:,:,1] = (img[:,:,1]-0.5)/0.5 145 | img[:,:,2] = (img[:,:,2]-0.5)/0.5 146 | img = np.float32(img[np.newaxis,:,:,:]) 147 | img = img.transpose(0, 3, 1, 2) 148 | return img 149 | 150 | def post_process(self, output, height, width): 151 | output = output.clip(-1,1) 152 | output = (output + 1) / 2 153 | output = output.transpose(1, 2, 0) 154 | output = cv2.cvtColor(output, cv2.COLOR_RGB2BGR) 155 | output = (output * 255.0).round() 156 | if self.affine: 157 | inverse_affine = cv2.invertAffineTransform(self.affine_matrix) 158 | inverse_affine *= self.upscale_factor 159 | if self.upscale_factor > 1: 160 | extra_offset = 0.5 * self.upscale_factor 161 | else: 162 | extra_offset = 0 163 | inverse_affine[:, 2] += extra_offset 164 | inv_restored = cv2.warpAffine(output, inverse_affine, (width, height)) 165 | mask = np.ones((self.face_size, self.face_size), dtype=np.float32) 166 | inv_mask = cv2.warpAffine(mask, inverse_affine, (width, height)) 167 | inv_mask_erosion = cv2.erode( 168 | inv_mask, np.ones((int(2 * self.upscale_factor), int(2 * self.upscale_factor)), np.uint8)) 169 | pasted_face = inv_mask_erosion[:, :, None] * inv_restored 170 | total_face_area = np.sum(inv_mask_erosion) 171 | # compute the fusion edge based on the area of face 172 | w_edge = int(total_face_area**0.5) // 20 173 | erosion_radius = w_edge * 2 174 | inv_mask_center = cv2.erode(inv_mask_erosion, np.ones((erosion_radius, erosion_radius), np.uint8)) 175 | blur_size = w_edge * 2 176 | inv_soft_mask = cv2.GaussianBlur(inv_mask_center, (blur_size + 1, blur_size + 1), 0) 177 | inv_soft_mask = inv_soft_mask[:, :, None] 178 | output = pasted_face 179 | else: 180 | inv_soft_mask = np.ones((height, width, 1), dtype=np.float32) 181 | output = cv2.resize(output, (width, height)) 182 | return output, inv_soft_mask 183 | 184 | def forward(self, img): 185 | import PIL 186 | img = np.asarray(img) 187 | img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) 188 | 189 | height, width = img.shape[0], img.shape[1] 190 | img = self.pre_process(img) 191 | ort_inputs = {self.ort_session.get_inputs()[0].name: img} 192 | ort_outs = self.ort_session.run(None, ort_inputs) 193 | output = ort_outs[0][0] 194 | output, inv_soft_mask = self.post_process(output, height, width) 195 | output = output.astype(np.uint8) 196 | 197 | output = cv2.cvtColor(output, cv2.COLOR_BGR2RGB) 198 | output = PIL.Image.fromarray(output) 199 | return output#, inv_soft_mask 200 | 201 | 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deprecated, for new version go to: https://github.com/NeusZimmer/ONNX-ModularUI-StableDiffusion 2 | 3 | # ONNX-ModularUI 4 | **New version available** 5 | **This version only works until diffusers version 14.0, does not work with new versions of diffusers, may need to modify the requirements.txt to make it run** 6 | 7 | 8 | 9 | Hello, I'm a **AMD user with a low-profile 4gb GPU**.... i gone crazy trying to find a solution to make stable diffusion work as fast as possible. I only found a viable option: ONNX on Windows, found it and started to try and test models while trying to optimize the in-memory model comsumption and performance/balance on the available UIs. 10 | 11 | This version might work for CUDA, TensorRT, DirectML engines of ONNX, on windows and linux ( for linux you will need to modify the requirements.txt file and delete "onnxruntime-directml" line to make it work, NOTE: Maybe AMD cards not supported in linux implementation?) 12 | 13 | I've just decided to apply some of them into this UI to allow a granular approach on how the models are loaded and how they consum memory & disk, while adding some options that other versions already had. Current version avoids the necessity of storing repeated Vae's, Text Encoders and ControlNet models... saving at least 0'5Gb per model 14 | 15 | The Stable Diffusion pipelines classes are encapsulated in new ones to allow them an easier management while adding other options around. 16 | 17 | **Current version:** 18 | 19 | **-New Hi-Res pipeline & approach, plus latents experimentals ---A must try!** 20 | 21 | **-Main basic pipelines: Txt2Img, Img2Img, Pix2Pix, Inpaint, ControlNet** 22 | 23 | **-Additional tools: 2 upscalers, deepdanbooru tagging, face detection, wildcards support,styles** 24 | 25 | **-Experimental feature: latents experimentals & image composition: creating one image using a sumatory of previous outputs, (one or many), working as something inbetween outpainting, img2img and controlnet.(works so good with the hi-res pipeline** 26 | 27 | **Working features: You decide where to run&load each feature (model, vae, textenc...) based on your hardware , CUDA, DML, CPU... whithout the need of reseting the UI, only loading a new model or reloading current model. Avoid reusing of repeated models for VAE's, TextEncoder & ControlNet...** 28 | **And: wilcards (for one or multiple iterations, also you could inclue wildcards inside other wildcards...), styles (examples available, editing in config tab.** 29 | 30 | Next version update: clip-skip 31 | 32 | ## Set up 33 | 34 | To make this works follow the (shown below) install steps for ONNX Stabble diffusion. 35 | 36 | ``` 37 | git clone https://github.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI.git 38 | ``` 39 | 40 | Then : 41 | ``` 42 | pip install virtualenv 43 | python -m venv sd_env 44 | sd_env\scripts\activate 45 | python -m pip install --upgrade pip 46 | pip install torch --extra-index-url https://download.pytorch.org/whl/nightly/cpu --pre 47 | pip install -r requirements.txt 48 | ``` 49 | 50 | PD: to install in linux, use **pip install -r requirements-linux.txt** instead "pip install -r requirements.txt". 51 | 52 | Activate python virtual environment and run the UI 53 | ``` 54 | ./sd_env/scripts/activate.bat 55 | cd ONNX-Stable-Diffusion-ModularUI 56 | run.bat 57 | or 58 | py -O ONNX-StableDiffusion.py 59 | ``` 60 | 61 | **Model Download** 62 | I've uploaded an initial set of files for testing the UI into https://civitai.com/models/125580 63 | 64 | **For Model conversion, please, use this repository:** 65 | https://github.com/Amblyopius/Stable-Diffusion-ONNX-FP16 66 | 67 | PD: do not need to create a new python environment, as this UI uses the same environment and there's no need to duplicate. 68 | 69 | 70 | ## Configuration 71 | At first run, you may need to configure the path for some options: output & models, 72 | ![UIOptions](https://github.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/assets/94193584/a160aacd-39ca-4ab4-b75b-3e7f4d0ff82c) 73 | 74 | and have a look into the Engine Configuration to see available option for running the different modules & pipelines. 75 | ![EngineOptions](https://github.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/assets/94193584/08d40866-d472-40b2-a001-5cf7a9d8513b) 76 | 77 | 78 | Currently, you may also select to use a specific VAE model to use for the inferences, saving some space on disk, as many models use the same versions, it will search for the first option, if not found, it will go for 2nd and then for 3rd... 79 | This also applies for ControlNet models, you could use the same one for all instances of your model, saving a lot of space on your disk.Note: you will still need the adapted Unet in the ControlNet directory. 80 | ![VAEOptions](https://github.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/assets/94193584/6232335f-9442-482b-ba0d-eca79c2bc09a) 81 | 82 | 83 | 84 | You may check the info on the cmd window about what and where a model have been loaded: 85 | 86 | ![CMD-Example](https://github.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/assets/94193584/4151131a-5fe3-43a8-bb52-9360ed471127) 87 | 88 | 89 | As you may see in the directory tree of one of my models, there's no need for VAE directories and also for the generic ControlNet Models: 90 | ![ExampleControlNetModel](https://github.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/assets/94193584/431144a8-77e9-41b3-8525-d8c3388e9f22) 91 | 92 | 93 | From previous readme: to be edited: 94 | 95 | It provides support for: txt2img, img2img, Inpaint, instruct pix2pix.(control net in test phase), and NEW: reingest of a previous generation into a new one, it works closely to ControlNet without models ... 96 | 97 | Allows modification of the pipeline providers without re-running the UI., also, you may want to run some pipeline in one graphic card, another card for VAE and CPU to the rest... this UI allows such granularity for :main model, schedulers, VAE, text encoder... 98 | 99 | Allows the use of a different VAE for a model (many models got the same VAE , then, why keep storing them on disk? 100 | 101 | Add a clean memory option: changing resolution for the inferences keep garbage in memory, and it ends making an impact on the time needed for the inferences. 102 | 103 | Also: wildcards, live prompts while running multiple iterations, a working deepdanbooru interrogator and resolution increase option. (one of my first tests with a ONNX model, a MS Model ([super-resolution-10.onnx]) to increase file resolution up to crazy sizes by slicing & joining (working , but not for professional uses) (todo: implement stable diffusion 4x upscale...) 104 | 105 | For DeepDanbooru model, download it from: https://huggingface.co/Neus/Onnx_DeepDanbooru/tree/main 106 | 107 | PD: first python code I did... sure it could be improved, but working fine and easy understand its workflows and to modify in future releases. 108 | 109 | 1st-Mayor updated, i was looking for an option to re-use previous generated latents of images, and i did one approach, basing on their generated latents, available under txt2img tab, accordion: Latent Experimentals. For one or sumatories of latens... look and the info txt file to make it work. 110 | 111 | Works fairly well, but only with DDIMM (ETA=1) and Euler Ancestral, while other schedulers are hard to find the right conbination (or impossible), with DDIM & Euler A you may get pretty good definition and images based on a previous generation, from same model or from a different model. 112 | 113 | 1st. find a prompt, model and a generation that you want to save and use 114 | ![SaveLatents1](https://github.com/NeusZimmer/ONNX-ModularUI/assets/94193584/5778f303-d9ef-4dcb-8cd6-74a7c8998359) 115 | 116 | It will save a .npy file in the latents directory, one file each two steps, and the final one. 117 | ![latents2](https://github.com/NeusZimmer/ONNX-ModularUI/assets/94193584/5fef7606-ba1e-4e43-ab19-04f0aeb3ee8e) 118 | 119 | If you want to check the result image of each .npy file, you may click on "Convert npy to img" and it will save .png alongside the numpy files. (I always use the last one, but feel free to try with previous...) 120 | ![Optional-CheckImages](https://github.com/NeusZimmer/ONNX-ModularUI/assets/94193584/76e610cd-64b7-4121-a53d-56ece339e6e3) 121 | ![latents+imgs](https://github.com/NeusZimmer/ONNX-ModularUI/assets/94193584/8cb7ffff-15be-4aa5-b6b9-93b9834eae1f) 122 | 123 | 124 | Write down the name of the numpy to use for the next generated image and a different promt, (having in mind the constrains of the previously generated image) 125 | For only one latent use: "index:name.npy" formula: index (ie: "1:file.npy" formula:"1") 126 | PD: Make sure the size (width and height) of the previous image is according to the new generation 127 | ![ExampleOfSums](https://github.com/NeusZimmer/ONNX-Stable-Diffusion-ModularUI/assets/94193584/e572d604-e7f0-4343-b4dc-f1322169bb47) 128 | 129 | Here is the tricky part, Select Euler A, a multiplier ( best results range between 0.35 to 0.6 ) and a Strengh, strengh is the total steps you will be applying the numpy file, if you go fot 36 steps a good number could range from 28 to 32 (from 70% to 90%). 130 | Steps: you could have a good approach of the final image from 14-16 steps, and good results around 30 to 40 steps. 131 | ![re-generate](https://github.com/NeusZimmer/ONNX-ModularUI/assets/94193584/03afe051-ec35-438a-abcd-2e401f1bd4e6) 132 | Guid: depends on the model,for some I found the need to increase it a little bit, 10 to 18 could be a good starting point (12-14 the usual), but others works as usual... 133 | 134 | 135 | -------------------------------------------------------------------------------- /UI/Inpaint_ui.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | import os,gc,re,PIL 3 | 4 | from Engine.General_parameters import Engine_Configuration 5 | from Engine.General_parameters import running_config 6 | from Engine.General_parameters import UI_Configuration 7 | from UI import UI_common_funcs as UI_common 8 | from Engine import pipelines_engines 9 | 10 | from PIL import Image, PngImagePlugin 11 | 12 | def show_Inpaint_ui(): 13 | ui_config=UI_Configuration() 14 | model_list = UI_common.get_model_list("inpaint") 15 | sched_list = get_schedulers_list() 16 | gr.Markdown("Start typing below and then click **Process** to produce the output.") 17 | with gr.Row(): 18 | with gr.Column(scale=13, min_width=650): 19 | model_drop = gr.Dropdown(model_list, value=(model_list[0] if len(model_list) > 0 else None), label="model folder", interactive=True) 20 | prompt_t0 = gr.Textbox(value="", lines=2, label="prompt") 21 | neg_prompt_t0 = gr.Textbox(value="", lines=2, label="negative prompt") 22 | sch_t0 = gr.Radio(sched_list, value=sched_list[0], label="scheduler") 23 | legacy_t0 = gr.Checkbox(value=False, label="legacy inpaint") 24 | 25 | image_t0 = gr.Image( 26 | source="upload", tool="sketch", label="input image", type="pil", elem_id="image_inpaint") 27 | mask_t0 = gr.Image( 28 | source="upload", 29 | label="input mask", 30 | type="pil", 31 | invert_colors=True, 32 | elem_id="mask_inpaint", 33 | ) 34 | with gr.Row(): 35 | iter_t0 = gr.Slider(1, 100, value=1, step=1, label="iteration count") 36 | batch_t0 = gr.Slider(1, 4, value=1, step=1, label="batch size",visible=False) 37 | steps_t0 = gr.Slider(1, 300, value=16, step=1, label="steps") 38 | guid_t0 = gr.Slider(0, 50, value=7.5, step=0.1, label="guidance") 39 | height_t0 = gr.Slider(256, 2048, value=512, step=64, label="height") 40 | width_t0 = gr.Slider(256, 2048, value=512, step=64, label="width") 41 | eta_t0 = gr.Slider(0, 1, value=0.0, step=0.01, label="DDIM eta", interactive=True) 42 | seed_t0 = gr.Textbox(value="", max_lines=1, label="seed") 43 | fmt_t0 = gr.Radio(["png", "jpg"], value="png", label="image format") 44 | with gr.Column(scale=11, min_width=550): 45 | with gr.Row(): 46 | gen_btn = gr.Button("Process", variant="primary", elem_id="gen_button") 47 | clear_btn = gr.Button("Cancel",info="Cancel at end of current iteration",variant="stop", elem_id="gen_button") 48 | memory_btn = gr.Button("Release memory", elem_id="mem_button") 49 | 50 | with gr.Row(): 51 | image_out = gr.Gallery(value=None, label="output images") 52 | 53 | with gr.Row(): 54 | status_out = gr.Textbox(value="", label="status") 55 | 56 | 57 | list_of_All_Parameters=[model_drop,prompt_t0,neg_prompt_t0,legacy_t0,sch_t0,image_t0,mask_t0,iter_t0,batch_t0,steps_t0,guid_t0,height_t0,width_t0,eta_t0,seed_t0,fmt_t0] 58 | gen_btn.click(fn=generate_click, inputs=list_of_All_Parameters, outputs=[image_out,status_out]) 59 | #sch_t0.change(fn=select_scheduler, inputs=sch_t0, outputs= None) #Atencion cambiar el DDIM ETA si este se activa 60 | memory_btn.click(fn=UI_common.clean_memory_click, inputs=None, outputs=None) 61 | clear_btn.click(fn=UI_common.cancel_iteration,inputs=None,outputs=None) 62 | 63 | 64 | 65 | def gallery_view(images,dict_statuses): 66 | return images[0] 67 | 68 | def get_schedulers_list(): 69 | sched_config = pipelines_engines.SchedulersConfig() 70 | sched_list =sched_config.available_schedulers 71 | return sched_list 72 | 73 | def select_scheduler(sched_name,model_path): 74 | return pipelines_engines.SchedulersConfig().scheduler(sched_name,model_path) 75 | 76 | def generate_click(model_drop,prompt_t0,neg_prompt_t0,legacy_t0,sch_t0,image_t0,mask_t0,iter_t0,batch_t0,steps_t0,guid_t0,height_t0,width_t0,eta_t0,seed_t0,fmt_t0): 77 | from Engine.pipelines_engines import inpaint_pipe 78 | 79 | Running_information= running_config().Running_information 80 | Running_information.update({"Running":True}) 81 | 82 | #input_image = resize_and_crop(input_image, height_t0, width_t0) 83 | input_image = resize_and_crop(image_t0["image"], height_t0, width_t0) 84 | 85 | if mask_t0 is not None: 86 | print("using uploaded mask") 87 | input_mask = mask_t0.convert("RGB") 88 | input_mask = resize_and_crop(input_mask, height_t0, width_t0) 89 | else: 90 | print("using painted mask") 91 | input_mask = image_t0["mask"].convert("RGB") 92 | input_mask = resize_and_crop(input_mask, height_t0, width_t0) 93 | 94 | #if legacy_t0 is True: 95 | #steps_t0 = step_adjustment(steps_t0, 0, "inpaint") 96 | 97 | if (Running_information["model"] != model_drop or Running_information["tab"] != "inpaint"): 98 | UI_common.clean_memory_click() 99 | Running_information.update({"model":model_drop}) 100 | Running_information.update({"tab":"inpaint"}) 101 | 102 | model_path=ui_config=UI_Configuration().models_dir+"\\"+model_drop 103 | pipe=inpaint_pipe().initialize(model_path,sch_t0,legacy_t0) 104 | 105 | inpaint_pipe().create_seeds(seed_t0,iter_t0,False) 106 | images= [] 107 | information=[] 108 | counter=1 109 | img_index=get_next_save_index() 110 | 111 | for seed in inpaint_pipe().seeds: 112 | if running_config().Running_information["cancelled"]: 113 | running_config().Running_information.update({"cancelled":False}) 114 | break 115 | 116 | print(f"Iteration:{counter}/{iter_t0}") 117 | counter+=1 118 | batch_images,info = inpaint_pipe().run_inference( 119 | prompt_t0, 120 | neg_prompt_t0, 121 | input_image, 122 | input_mask, 123 | height_t0, 124 | width_t0, 125 | steps_t0, 126 | guid_t0, 127 | eta_t0, 128 | batch_t0, 129 | seed, 130 | legacy_t0) 131 | images.extend(batch_images) 132 | info=dict(info) 133 | info['Sched:']=sch_t0 134 | information.append(info) 135 | save_image(batch_images,info,img_index) 136 | img_index+=1 137 | Running_information.update({"Running":False}) 138 | return images,information 139 | 140 | 141 | def get_next_save_index(): 142 | output_path=UI_Configuration().output_path 143 | dir_list = os.listdir(output_path) 144 | if len(dir_list): 145 | pattern = re.compile(r"([0-9][0-9][0-9][0-9][0-9][0-9])-([0-9][0-9])\..*") 146 | match_list = [pattern.match(f) for f in dir_list] 147 | next_index = max([int(m[1]) if m else -1 for m in match_list]) + 1 148 | else: 149 | next_index = 0 150 | return next_index 151 | 152 | 153 | def save_image(batch_images,info,next_index): 154 | output_path=UI_Configuration().output_path 155 | 156 | info_png = f"{info}" 157 | metadata = PngImagePlugin.PngInfo() 158 | metadata.add_text("parameters",info_png) 159 | prompt=info["prompt"] 160 | short_prompt = prompt.strip("<>:\"/\\|?*\n\t") 161 | short_prompt = re.sub(r'[\\/*?:"<>|\n\t]', "", short_prompt) 162 | short_prompt = short_prompt[:49] if len(short_prompt) > 50 else short_prompt 163 | 164 | os.makedirs(output_path, exist_ok=True) 165 | """dir_list = os.listdir(output_path) 166 | if len(dir_list): 167 | pattern = re.compile(r"([0-9][0-9][0-9][0-9][0-9][0-9])-([0-9][0-9])\..*") 168 | match_list = [pattern.match(f) for f in dir_list] 169 | next_index = max([int(m[1]) if m else -1 for m in match_list]) + 1 170 | else: 171 | next_index = 0""" 172 | for image in batch_images: 173 | image.save(os.path.join(output_path,f"{next_index:06}-00.{short_prompt}.png",),optimize=True,pnginfo=metadata,) 174 | 175 | 176 | 177 | def resize_and_crop(input_image: PIL.Image.Image, height: int, width: int): 178 | input_width, input_height = input_image.size 179 | 180 | # nearest neighbor for upscaling 181 | if (input_width * input_height) < (width * height): 182 | resample_type = Image.NEAREST 183 | # lanczos for downscaling 184 | else: 185 | resample_type = Image.LANCZOS 186 | 187 | if height / width > input_height / input_width: 188 | adjust_width = int(input_width * height / input_height) 189 | input_image = input_image.resize((adjust_width, height), 190 | resample=resample_type) 191 | left = (adjust_width - width) // 2 192 | right = left + width 193 | input_image = input_image.crop((left, 0, right, height)) 194 | else: 195 | adjust_height = int(input_height * width / input_width) 196 | input_image = input_image.resize((width, adjust_height), 197 | resample=resample_type) 198 | top = (adjust_height - height) // 2 199 | bottom = top + height 200 | input_image = input_image.crop((0, top, width, bottom)) 201 | return input_image -------------------------------------------------------------------------------- /UI/ControlNet_ui.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | import os,gc,re,PIL 3 | 4 | from Engine.General_parameters import Engine_Configuration 5 | from Engine.General_parameters import running_config 6 | from Engine.General_parameters import UI_Configuration 7 | from Engine.General_parameters import ControlNet_config 8 | from UI import UI_common_funcs as UI_common 9 | from Engine import pipelines_engines 10 | 11 | from PIL import Image, PngImagePlugin 12 | 13 | 14 | def show_ControlNet_ui(): 15 | ui_config=UI_Configuration() 16 | model_list = UI_common.get_model_list("controlnet") 17 | sched_list = get_schedulers_list() 18 | controlnet_models= dict(ControlNet_config().available_controlnet_models()) 19 | gr.Markdown("Start typing below and then click **Process** to produce the output.") 20 | with gr.Row(): 21 | with gr.Column(scale=13, min_width=650): 22 | model_drop = gr.Dropdown(model_list, value=(model_list[0] if len(model_list) > 0 else None), label="model folder", interactive=True) 23 | with gr.Row(): 24 | ControlNET_drop = gr.Dropdown(controlnet_models.keys(),value=next(iter(controlnet_models)), label="ControNET Type", interactive=True) 25 | reload_controlnet_btn = gr.Button("Reload ControlNet model list") 26 | 27 | prompt_t0 = gr.Textbox(value="", lines=2, label="prompt") 28 | neg_prompt_t0 = gr.Textbox(value="", lines=2, label="negative prompt") 29 | sch_t0 = gr.Radio(sched_list, value=sched_list[0], label="scheduler", interactive=True) 30 | 31 | image_t0 = gr.Image(source="upload",label="input image", type="pil", elem_id="image_inpaint", visible=True) 32 | #pose_image_t0 = gr.Image(source="upload",label="ControlNET Image",type="pil") 33 | 34 | with gr.Row(): 35 | iter_t0 = gr.Slider(1, 100, value=1, step=1, label="iteration count") 36 | batch_t0 = gr.Slider(1, 4, value=1, step=1, label="batch size",visible=False) 37 | steps_t0 = gr.Slider(1, 300, value=16, step=1, label="steps") 38 | guid_t0 = gr.Slider(0, 50, value=7.5, step=0.1, label="guidance") 39 | controlnet_conditioning_scale_t0= gr.Slider(0, 1, value=0.5, step=0.1, label="controlnet_conditioning_scale") 40 | with gr.Row(): 41 | height_t0 = gr.Slider(256, 2048, value=512, step=64, label="height") 42 | width_t0 = gr.Slider(256, 2048, value=512, step=64, label="width") 43 | eta_t0 = gr.Slider(0, 1, value=0.0, step=0.01, label="DDIM eta", interactive=False) 44 | seed_t0 = gr.Textbox(value="", max_lines=1, label="seed") 45 | #fmt_t0 = gr.Radio(["png", "jpg"], value="png", label="image format") 46 | with gr.Column(scale=11, min_width=550): 47 | with gr.Row(): 48 | gen_btn = gr.Button("Process Full from Image", variant="primary", elem_id="gen_button") 49 | #gen_btn2 = gr.Button("Process to get pose image", variant="primary", elem_id="gen_button") 50 | #gen_btn3 = gr.Button("Process from pose image", variant="primary", elem_id="gen_button") 51 | with gr.Row(): 52 | clear_btn = gr.Button("Cancel",info="Cancel at end of current iteration",variant="stop", elem_id="gen_button") 53 | memory_btn = gr.Button("Release memory", elem_id="mem_button") 54 | with gr.Row(): 55 | image_out = gr.Gallery(value=None, label="output images") 56 | with gr.Row(): 57 | status_out = gr.Textbox(value="", label="status") 58 | 59 | list_of_All_Parameters=[model_drop,prompt_t0,neg_prompt_t0,sch_t0,image_t0,iter_t0,batch_t0,steps_t0,guid_t0,height_t0,width_t0,eta_t0,seed_t0,ControlNET_drop,controlnet_conditioning_scale_t0] 60 | gen_btn.click(fn=generate_click, inputs=list_of_All_Parameters, outputs=[image_out,status_out]) 61 | #gen_btn1.click(fn=generate_click, inputs=list_of_All_Parameters, outputs=[pose_image_t0,status_out]) 62 | #gen_btn2.click(fn=generate_click, inputs=list_of_All_Parameters, outputs=[image_out,status_out]) 63 | #sch_t0.change(fn=select_scheduler, inputs=sch_t0, outputs= None) #Atencion cambiar el DDIM ETA si este se activa 64 | memory_btn.click(fn=UI_common.clean_memory_click, inputs=None, outputs=None) 65 | clear_btn.click(fn=UI_common.cancel_iteration,inputs=None,outputs=None) 66 | reload_controlnet_btn.click(fn=ReloadControlNet, inputs=None , outputs=ControlNET_drop) 67 | 68 | 69 | def ReloadControlNet(): 70 | ControlNet_config().load_config_from_disk() 71 | controlnet_models= dict(ControlNet_config().available_controlnet_models()) 72 | controlnet_models_keys= list(controlnet_models.keys()) 73 | return gr.Dropdown.update(choices=controlnet_models_keys, value=controlnet_models_keys[0]) 74 | 75 | 76 | def get_schedulers_list(): 77 | sched_config = pipelines_engines.SchedulersConfig() 78 | sched_list =sched_config.schedulers_controlnet_list() 79 | return sched_list 80 | 81 | def select_scheduler(sched_name,model_path): 82 | return pipelines_engines.SchedulersConfig().scheduler(sched_name,model_path) 83 | 84 | 85 | def generate_click(model_drop,prompt_t0,neg_prompt_t0,sch_t0,image_t0,iter_t0,batch_t0,steps_t0,guid_t0,height_t0,width_t0,eta_t0,seed_t0,ControlNET_drop,controlnet_conditioning_scale): 86 | from Engine.pipelines_engines import ControlNet_pipe 87 | 88 | Running_information= running_config().Running_information 89 | Running_information.update({"Running":True}) 90 | 91 | input_image = resize_and_crop(image_t0, height_t0, width_t0) 92 | #pose_image_t0 = resize_and_crop(pose_image_t0, height_t0, width_t0) 93 | 94 | model_path=UI_Configuration().models_dir+"\\"+model_drop 95 | 96 | if (Running_information["model"] != model_drop or Running_information["tab"] != "controlnet"): 97 | UI_common.clean_memory_click() 98 | Running_information.update({"model":model_drop}) 99 | Running_information.update({"tab":"controlnet"}) 100 | ControlNet_pipe().initialize(model_path,sch_t0,ControlNET_drop) 101 | 102 | 103 | ControlNet_pipe().create_seeds(seed_t0,iter_t0,False) 104 | images= [] 105 | information=[] 106 | counter=1 107 | img_index=get_next_save_index() 108 | 109 | for seed in ControlNet_pipe().seeds: 110 | if running_config().Running_information["cancelled"]: 111 | running_config().Running_information.update({"cancelled":False}) 112 | break 113 | 114 | print(f"Iteration:{counter}/{iter_t0}") 115 | counter+=1 116 | batch_images,info = ControlNet_pipe().run_inference( 117 | prompt_t0, 118 | neg_prompt=neg_prompt_t0, 119 | input_image=input_image, 120 | #pose_image=pose_image_t0, 121 | height=height_t0, 122 | width=width_t0, 123 | steps=steps_t0, 124 | guid=guid_t0, 125 | eta=eta_t0, 126 | controlnet_conditioning_scale=controlnet_conditioning_scale, 127 | seed=seed) 128 | 129 | images.extend([batch_images]) 130 | info=dict(info) 131 | info['Sched:']=sch_t0 132 | info['CnetModel:']=ControlNET_drop 133 | information.append(info) 134 | save_image([batch_images],info,img_index) 135 | img_index+=1 136 | Running_information.update({"Running":False}) 137 | return images,information 138 | 139 | 140 | def get_next_save_index(): 141 | output_path=UI_Configuration().output_path 142 | dir_list = os.listdir(output_path) 143 | if len(dir_list): 144 | pattern = re.compile(r"([0-9][0-9][0-9][0-9][0-9][0-9])-([0-9][0-9])\..*") 145 | match_list = [pattern.match(f) for f in dir_list] 146 | next_index = max([int(m[1]) if m else -1 for m in match_list]) + 1 147 | else: 148 | next_index = 0 149 | return next_index 150 | 151 | 152 | def save_image(batch_images,info,next_index): 153 | output_path=UI_Configuration().output_path 154 | 155 | info_png = f"{info}" 156 | metadata = PngImagePlugin.PngInfo() 157 | metadata.add_text("parameters",info_png) 158 | prompt=info["prompt"] 159 | short_prompt = prompt.strip("<>:\"/\\|?*\n\t") 160 | short_prompt = re.sub(r'[\\/*?:"<>|\n\t]', "", short_prompt) 161 | short_prompt = short_prompt[:49] if len(short_prompt) > 50 else short_prompt 162 | 163 | os.makedirs(output_path, exist_ok=True) 164 | 165 | for image in batch_images: 166 | image.save(os.path.join(output_path,f"{next_index:06}-00.{short_prompt}.png",),optimize=True,pnginfo=metadata,) 167 | 168 | 169 | def resize_and_crop(input_image: PIL.Image.Image, height: int, width: int): 170 | input_width, input_height = input_image.size 171 | 172 | # nearest neighbor for upscaling 173 | if (input_width * input_height) < (width * height): 174 | resample_type = Image.NEAREST 175 | # lanczos for downscaling 176 | else: 177 | resample_type = Image.LANCZOS 178 | 179 | if height / width > input_height / input_width: 180 | adjust_width = int(input_width * height / input_height) 181 | input_image = input_image.resize((adjust_width, height), 182 | resample=resample_type) 183 | left = (adjust_width - width) // 2 184 | right = left + width 185 | input_image = input_image.crop((left, 0, right, height)) 186 | else: 187 | adjust_height = int(input_height * width / input_width) 188 | input_image = input_image.resize((width, adjust_height), 189 | resample=resample_type) 190 | top = (adjust_height - height) // 2 191 | bottom = top + height 192 | input_image = input_image.crop((0, top, width, bottom)) 193 | return input_image -------------------------------------------------------------------------------- /UI/Img2Img_ui.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | import os,gc,re,PIL 3 | from PIL import Image, PngImagePlugin 4 | 5 | from Engine.General_parameters import Engine_Configuration 6 | from Engine.General_parameters import running_config 7 | from Engine.General_parameters import UI_Configuration 8 | from UI import UI_common_funcs as UI_common 9 | from Engine import pipelines_engines 10 | 11 | 12 | 13 | def show_Img2Img_ui(): 14 | ui_config=UI_Configuration() 15 | model_list = UI_common.get_model_list("txt2img") 16 | sched_list = get_schedulers_list() 17 | gr.Markdown("Start typing below and then click **Process** to produce the output.") 18 | with gr.Row(): 19 | with gr.Column(scale=13, min_width=650): 20 | model_drop = gr.Dropdown(model_list, value=(model_list[0] if len(model_list) > 0 else None), label="model folder", interactive=True) 21 | prompt_t0 = gr.Textbox(value="", lines=2, label="prompt") 22 | neg_prompt_t0 = gr.Textbox(value="", lines=2, label="negative prompt") 23 | sch_t0 = gr.Radio(sched_list, value=sched_list[0], label="scheduler") 24 | 25 | image_t0 = gr.Image( 26 | source="upload", tool="sketch", label="input image", type="pil", elem_id="image2image") 27 | 28 | with gr.Row(): 29 | iter_t0 = gr.Slider(1, 100, value=1, step=1, label="iteration count") 30 | batch_t0 = gr.Slider(1, 4, value=1, step=1, label="batch size",visible=False) 31 | steps_t0 = gr.Slider(1, 300, value=16, step=1, label="steps") 32 | guid_t0 = gr.Slider(0, 50, value=7.5, step=0.1, label="guidance") 33 | strengh_t0 = gr.Slider(0, 1, value=0.75, step=0.05, label="Strengh") 34 | height_t0 = gr.Slider(256, 2048, value=512, step=64, label="Height") 35 | width_t0 = gr.Slider(256, 2048, value=512, step=64, label="Width") 36 | eta_t0 = gr.Slider(0, 1, value=0.0, step=0.01, label="DDIM eta", interactive=True) 37 | seed_t0 = gr.Textbox(value="", max_lines=1, label="seed") 38 | fmt_t0 = gr.Radio(["png", "jpg"], value="png", label="image format",visible=False) 39 | with gr.Column(scale=11, min_width=550): 40 | with gr.Row(): 41 | gen_btn = gr.Button("Process", variant="primary", elem_id="gen_button") 42 | cancel_btn = gr.Button("Cancel",info="Cancel at end of current iteration",variant="stop", elem_id="gen_button") 43 | memory_btn = gr.Button("Release memory", elem_id="mem_button") 44 | with gr.Row(): 45 | image_out = gr.Gallery(value=None, label="output images") 46 | with gr.Row(): 47 | status_out = gr.Textbox(value="", label="status") 48 | 49 | 50 | list_of_All_Parameters=[model_drop,prompt_t0,neg_prompt_t0,sch_t0,image_t0,iter_t0,batch_t0,steps_t0,guid_t0,height_t0,width_t0,eta_t0,seed_t0,fmt_t0,strengh_t0] 51 | gen_btn.click(fn=generate_click, inputs=list_of_All_Parameters, outputs=[image_out,status_out]) 52 | #sch_t0.change(fn=select_scheduler, inputs=sch_t0, outputs= None) #Atencion cambiar el DDIM ETA si este se activa 53 | memory_btn.click(fn=UI_common.clean_memory_click, inputs=None, outputs=None) 54 | cancel_btn.click(fn=UI_common.cancel_iteration,inputs=None,outputs=None) 55 | 56 | 57 | 58 | def gallery_view(images,dict_statuses): 59 | return images[0] 60 | 61 | 62 | def get_schedulers_list(): 63 | sched_config = pipelines_engines.SchedulersConfig() 64 | sched_list =sched_config.available_schedulers 65 | return sched_list 66 | 67 | def select_scheduler(sched_name,model_path): 68 | return pipelines_engines.SchedulersConfig().scheduler(sched_name,model_path) 69 | 70 | def generate_click(model_drop,prompt_t0,neg_prompt_t0,sch_t0,image_t0,iter_t0,batch_t0,steps_t0,guid_t0,height_t0,width_t0,eta_t0,seed_t0,fmt_t0,strengh_t0): 71 | from Engine.pipelines_engines import img2img_pipe 72 | 73 | Running_information= running_config().Running_information 74 | Running_information.update({"Running":True}) 75 | 76 | input_image = resize_and_crop(image_t0["image"], height_t0, width_t0) 77 | 78 | if (Running_information["model"] != model_drop or Running_information["tab"] != "img2img"): 79 | #if (Running_information["model"] != model_drop or Running_information["tab"] != "txt2img"): 80 | UI_common.clean_memory_click() 81 | Running_information.update({"model":model_drop}) 82 | Running_information.update({"tab":"img2img"}) 83 | 84 | model_path=ui_config=UI_Configuration().models_dir+"\\"+model_drop 85 | pipe=img2img_pipe().initialize(model_path,sch_t0) 86 | img2img_pipe().create_seeds(seed_t0,iter_t0,False) 87 | images= [] 88 | information=[] 89 | counter=1 90 | img_index=get_next_save_index() 91 | 92 | for seed in img2img_pipe().seeds: 93 | if running_config().Running_information["cancelled"]: 94 | running_config().Running_information.update({"cancelled":False}) 95 | break 96 | 97 | print(f"Iteration:{counter}/{iter_t0}") 98 | counter+=1 99 | loopback =False 100 | if loopback is True: 101 | try: 102 | loopback_image 103 | except UnboundLocalError: 104 | loopback_image = None 105 | 106 | if loopback_image is not None: 107 | batch_images,info = img2img_pipe().run_inference( 108 | prompt_t0, 109 | negative_prompt=neg_prompt_t0, 110 | image=loopback_image, #Check this data, 111 | num_inference_steps=steps_t0, 112 | guidance_scale=guid_t0, 113 | eta=eta_t0, 114 | strength=strengh_t0, 115 | num_images_per_prompt=batch_t0, 116 | seed=seed).images 117 | elif loopback_image is None: 118 | batch_images,info = img2img_pipe().run_inference( 119 | prompt_t0, 120 | negative_prompt=neg_prompt_t0, 121 | image=input_image, 122 | strength=strengh_t0, 123 | num_inference_steps=steps_t0, 124 | guidance_scale=guid_t0, 125 | eta=eta_t0, 126 | num_images_per_prompt=batch_t0, 127 | seed=seed).images 128 | elif loopback is False: 129 | batch_images,info = img2img_pipe().run_inference( 130 | prompt_t0, 131 | neg_prompt=neg_prompt_t0, 132 | init_image=input_image, 133 | strength=strengh_t0, 134 | steps=steps_t0, 135 | guid=guid_t0, 136 | eta=eta_t0, 137 | batch=batch_t0, 138 | seed=seed) #Cambio gen por su seed.cuidado falta eta pero podria hacer falta 139 | 140 | images.extend(batch_images) 141 | info=dict(info) 142 | info['Sched:']=sch_t0 143 | information.append(info) 144 | save_image(batch_images,info,img_index) 145 | img_index+=1 146 | Running_information.update({"Running":False}) 147 | return images,information 148 | 149 | 150 | def get_next_save_index(): 151 | output_path=UI_Configuration().output_path 152 | dir_list = os.listdir(output_path) 153 | if len(dir_list): 154 | pattern = re.compile(r"([0-9][0-9][0-9][0-9][0-9][0-9])-([0-9][0-9])\..*") 155 | match_list = [pattern.match(f) for f in dir_list] 156 | next_index = max([int(m[1]) if m else -1 for m in match_list]) + 1 157 | else: 158 | next_index = 0 159 | return next_index 160 | 161 | 162 | def save_image(batch_images,info,next_index): 163 | output_path=UI_Configuration().output_path 164 | 165 | info_png = f"{info}" 166 | metadata = PngImagePlugin.PngInfo() 167 | metadata.add_text("parameters",info_png) 168 | prompt=info["Img2ImgPrompt"] 169 | short_prompt = prompt.strip("<>:\"/\\|?*\n\t") 170 | short_prompt = re.sub(r'[\\/*?:"<>|\n\t]', "", short_prompt) 171 | short_prompt = short_prompt[:49] if len(short_prompt) > 50 else short_prompt 172 | 173 | os.makedirs(output_path, exist_ok=True) 174 | """dir_list = os.listdir(output_path) 175 | if len(dir_list): 176 | pattern = re.compile(r"([0-9][0-9][0-9][0-9][0-9][0-9])-([0-9][0-9])\..*") 177 | match_list = [pattern.match(f) for f in dir_list] 178 | next_index = max([int(m[1]) if m else -1 for m in match_list]) + 1 179 | else: 180 | next_index = 0""" 181 | for image in batch_images: 182 | image.save(os.path.join(output_path,f"{next_index:06}-00.{short_prompt}.png",),optimize=True,pnginfo=metadata,) 183 | 184 | 185 | def resize_and_crop(input_image: PIL.Image.Image, height: int, width: int): 186 | input_width, input_height = input_image.size 187 | 188 | # nearest neighbor for upscaling 189 | if (input_width * input_height) < (width * height): 190 | resample_type = Image.NEAREST 191 | # lanczos for downscaling 192 | else: 193 | resample_type = Image.LANCZOS 194 | 195 | if height / width > input_height / input_width: 196 | adjust_width = int(input_width * height / input_height) 197 | input_image = input_image.resize((adjust_width, height), 198 | resample=resample_type) 199 | left = (adjust_width - width) // 2 200 | right = left + width 201 | input_image = input_image.crop((left, 0, right, height)) 202 | else: 203 | adjust_height = int(input_height * width / input_width) 204 | input_image = input_image.resize((width, adjust_height), 205 | resample=resample_type) 206 | top = (adjust_height - height) // 2 207 | bottom = top + height 208 | input_image = input_image.crop((0, top, width, bottom)) 209 | return input_image -------------------------------------------------------------------------------- /Scripts/deepdanbooru_onnx.py: -------------------------------------------------------------------------------- 1 | #Class code, initial model and tags obtained from https://huggingface.co/chinoll 2 | #new model version converted on: 3 | 4 | 5 | import onnxruntime as ort 6 | from PIL import Image 7 | import numpy as np 8 | import os 9 | from tqdm import tqdm 10 | import requests 11 | import hashlib 12 | from typing import List, Union 13 | import shutil 14 | from pathlib import Path 15 | import hashlib 16 | from Engine.General_parameters import Engine_Configuration 17 | 18 | def process_image(image:Image.Image) -> np.ndarray: 19 | ''' 20 | Convert an image to a numpy array. 21 | :param image: the image to convert 22 | :return: the numpy array 23 | ''' 24 | 25 | image = image.convert("RGB").resize((512,512)) 26 | image = np.array(image).astype(np.float32) / 255 27 | image = image.transpose((2,0,1)).reshape(1,3,512,512).transpose((0,2,3,1)) 28 | return image 29 | 30 | def download(url:str, save_path:str, md5:str, length:str) -> bool: 31 | ''' 32 | Download a file from url to save_path. 33 | If the file already exists, check its md5. 34 | If the md5 matches, return True,if the md5 doesn't match, return False. 35 | :param url: the url of the file to download 36 | :param save_path: the path to save the file 37 | :param md5: the md5 of the file 38 | :param length: the length of the file 39 | :return: True if the file is downloaded successfully, False otherwise 40 | ''' 41 | 42 | try: 43 | response = requests.get(url=url, stream=True) 44 | with open(save_path, "wb") as f: 45 | with tqdm.wrapattr(response.raw, "read", total=length, desc="Downloading") as r_raw: 46 | shutil.copyfileobj(r_raw, f) 47 | return True if hashlib.md5(open(save_path, "rb").read()).hexdigest() == md5 else False 48 | except Exception as e: 49 | print(e) 50 | return False 51 | 52 | def download_model(): 53 | return "./Scripts/deepdanbooru_onnx_data/deepdanbooru.onnx", "./Scripts/deepdanbooru_onnx_data/tags.txt" 54 | 55 | def download_model1(): 56 | ''' 57 | Download the model and tags file from the server. 58 | :return: the path to the model and tags file 59 | ''' 60 | 61 | model_url = "https://huggingface.co/chinoll/deepdanbooru/resolve/main/deepdanbooru.onnx" 62 | tags_url = "https://huggingface.co/chinoll/deepdanbooru/resolve/main/tags.txt" 63 | model_md5 = "16be4e40ebcc0b1d1915bbf31f00969f" 64 | tags_md5 = "a3f764de985cdeba89f1d232a4204402" 65 | model_length = 643993025 66 | tags_length = 133810 67 | 68 | #home = str(Path.home()) + "/.deepdanbooru_onnx/" 69 | home = "./Scripts/deepdanbooru_onnx_data/" 70 | if not os.path.exists(home): 71 | os.mkdir(home) 72 | model_name = "deepdanbooru.onnx" 73 | tags_name = "tags.txt" 74 | 75 | model_path = home + model_name 76 | print(model_path) 77 | tags_path = home + tags_name 78 | if os.path.exists(model_path): 79 | if hashlib.md5(open(model_path, "rb").read()).hexdigest() != model_md5: 80 | os.remove(model_path) 81 | if not download(model_url, model_path, model_md5, model_length): 82 | raise ValueError("Model download failed") 83 | else: 84 | if not download(model_url, model_path, model_md5, model_length): 85 | raise ValueError("Model download failed") 86 | 87 | if os.path.exists(tags_path): 88 | if hashlib.md5(open(tags_path, "rb").read()).hexdigest() != tags_md5: 89 | os.remove(tags_path) 90 | if not download(tags_url, tags_path, tags_md5, tags_length): 91 | raise ValueError("Tags download failed") 92 | else: 93 | if not download(tags_url, tags_path, tags_md5, tags_length): 94 | raise ValueError("Tags download failed") 95 | print(model_path, tags_path) 96 | return model_path, tags_path 97 | 98 | 99 | class DeepDanbooru: 100 | def __init__(self, mode: str = "auto", model_path: Union[str, None] =None, tags_path: Union[str, None] = None, threshold: Union[float, int] = 0.6, pin_memory: bool = False, batch_size: int = 1): 101 | ''' 102 | Initialize the DeepDanbooru class. 103 | :param mode: the mode of the model, "cpu" or "gpu" or "auto" 104 | :param model_path: the path to the model file 105 | :param tags_path: the path to the tags file 106 | :param threshold: the threshold of the model 107 | :param pin_memory: whether to use pin memory 108 | :param batch_size: the batch size of the model 109 | ''' 110 | exec_provider=Engine_Configuration().DeepDanBooru_provider 111 | providers = { 112 | "cpu":"CPUExecutionProvider", 113 | "gpu":"CUDAExecutionProvider", 114 | #"tensorrt": "TensorrtExecutionProvider", 115 | #"auto":"CUDAExecutionProvider" if "CUDAExecutionProvider" in ort.get_available_providers() else "CPUExecutionProvider", 116 | "auto":exec_provider, 117 | } 118 | if not (isinstance(threshold,float) or isinstance(threshold,int)): 119 | raise TypeError("threshold must be float or int") 120 | if threshold < 0 or threshold > 1: 121 | raise ValueError("threshold must be between 0 and 1") 122 | if mode not in providers: 123 | raise ValueError("Mode not supported. Please choose from: cpu, gpu, tensorrt") 124 | if providers[mode] not in ort.get_available_providers(): 125 | raise ValueError(f"Your device is not supported {mode}. Please choose from: cpu") 126 | if model_path is not None and not os.path.exists(model_path): 127 | raise FileNotFoundError("Model file not found") 128 | if tags_path is not None and not os.path.exists(tags_path): 129 | raise FileNotFoundError("Tags file not found") 130 | 131 | if model_path is None or tags_path is None: 132 | model_path, tags_path = download_model() 133 | 134 | self.session = ort.InferenceSession(model_path, providers=[providers[mode]]) 135 | self.tags = [i.replace("\n","") for i in open(tags_path, "r").readlines()] 136 | 137 | self.input_name = self.session.get_inputs()[0].name 138 | self.output_name = [output.name for output in self.session.get_outputs()] 139 | self.threshold = threshold 140 | self.pin_memory = pin_memory 141 | self.batch_size = batch_size 142 | self.mode = mode 143 | self.cache = {} 144 | 145 | def __str__(self) -> str: 146 | return f"DeepDanbooru(mode={self.mode}, threshold={self.threshold}, pin_memory={self.pin_memory}, batch_size={self.batch_size})" 147 | 148 | def __repr__(self) -> str: 149 | return self.__str__() 150 | 151 | def from_image_inference(self,image:Image.Image) -> dict: 152 | image = process_image(image) 153 | return self.predict(image) 154 | 155 | def from_ndarray_inferece(self, image:np.ndarray) -> dict: 156 | if image.shape != (1,512,512,3): 157 | raise ValueError(f"Image must be {(1,512,512,3)}") 158 | return self.predict(image) 159 | 160 | def from_file_inference(self,image:str) -> dict: 161 | return self.from_image_inference(Image.open(image)) 162 | 163 | def from_list_inference(self,image:Union[list,tuple]) -> List[dict]: 164 | if self.pin_memory: 165 | image = [process_image(Image.open(i)) for i in image] 166 | for i in [image[i:i + self.batch_size] for i in range(0, len(image), self.batch_size)]: 167 | imagelist = i 168 | bs = len(i) 169 | _imagelist, idx, hashlist = [], [], [] 170 | for j in range(len(i)): 171 | img = Image.open(i[j]) if not self.pin_memory else imagelist[j] 172 | image_hash = hashlib.md5(np.array(img).astype(np.uint8)).hexdigest() 173 | hashlist.append(image_hash) 174 | if image_hash in self.cache: 175 | continue 176 | if not self.pin_memory: 177 | _imagelist.append(process_image(img)) 178 | else: 179 | _imagelist.append(imagelist[j]) 180 | idx.append(j) 181 | 182 | imagelist = _imagelist 183 | if len(imagelist) != 0: 184 | _image = np.vstack(imagelist) 185 | results = self.inference(_image) 186 | results_idx = 0 187 | else: 188 | results = [] 189 | 190 | for i in range(bs): 191 | image_tag = {} 192 | if i in idx: 193 | hash = hashlist[i] 194 | for tag, score in zip(self.tags, results[results_idx]): 195 | if score >= self.threshold: 196 | image_tag[tag] = score 197 | results_idx += 1 198 | self.cache[hash] = image_tag 199 | yield image_tag 200 | else: 201 | yield self.cache[hashlist[i]] 202 | def inference(self,image): 203 | return self.session.run(self.output_name, {self.input_name:image})[0] 204 | 205 | def predict(self,image): 206 | result = self.inference(image) 207 | image_tag = {} 208 | for tag, score in zip(self.tags, result[0]): 209 | if score >= self.threshold: 210 | image_tag[tag] = score 211 | return image_tag 212 | 213 | def __call__(self, image) -> Union[dict, List[dict]]: 214 | if isinstance(image,str): 215 | return self.from_file_inference(image) 216 | elif isinstance(image,np.ndarray): 217 | return self.from_ndarray_inferece(image) 218 | elif isinstance(image,list) or isinstance(image,tuple): 219 | return self.from_list_inference(image) 220 | elif isinstance(image,Image.Image): 221 | return self.from_image_inference(image) 222 | else: 223 | raise ValueError("Image must be a file path or a numpy array or list/tuple") 224 | 225 | -------------------------------------------------------------------------------- /Engine/General_parameters.py: -------------------------------------------------------------------------------- 1 | 2 | # Singleton/BorgSingleton.py 3 | # Alex Martelli's 'Borg' 4 | # https://python-3-patterns-idioms-test.readthedocs.io/en/latest/Singleton.html 5 | import json 6 | 7 | class Borg: 8 | _shared_state = {} 9 | def __init__(self): 10 | self.__dict__ = self._shared_state 11 | 12 | class Borg1: 13 | _shared_state = {} 14 | def __init__(self): 15 | self.__dict__ = self._shared_state 16 | 17 | class Borg2: 18 | _shared_state = {} 19 | def __init__(self): 20 | self.__dict__ = self._shared_state 21 | 22 | class Borg3: 23 | _shared_state = {} 24 | def __init__(self): 25 | self.__dict__ = self._shared_state 26 | class Borg4: 27 | _shared_state = {} 28 | def __init__(self): 29 | self.__dict__ = self._shared_state 30 | 31 | class Borg5: 32 | _shared_state = {} 33 | def __init__(self): 34 | self.__dict__ = self._shared_state 35 | 36 | 37 | class Engine_Configuration(Borg): 38 | MAINPipe_provider="Not Selected" 39 | Scheduler_provider="Not Selected" 40 | ControlNet_provider="Not Selected" 41 | VAEDec_provider="Not Selected" 42 | TEXTEnc_provider="Not Selected" 43 | DeepDanBooru_provider="Not Selected" 44 | 45 | #MAINPipe_provider,Scheduler_provider,ControlNet_provider,VAEDec_provider,TEXTEnc_provider 46 | def __init__(self): 47 | Borg.__init__(self) 48 | 49 | def __str__(self): return json.dumps(self.__dict__) 50 | 51 | def save_config_json(self): 52 | jsonStr = json.dumps(self.__dict__) 53 | #print(type(jsonStr)) 54 | with open("./Engine/config_files/EngineConfig.json", "w") as outfile: 55 | outfile.write(jsonStr) 56 | print("Saving information for pipelines providers") 57 | print(jsonStr) 58 | return jsonStr 59 | 60 | def load_default_values(self): 61 | print("Loading default provider values:CPU") 62 | self.MAINPipe_provider="CPUExecutionProvider" 63 | self.Scheduler_provider="CPUExecutionProvider" 64 | self.ControlNet_provider="CPUExecutionProvider" 65 | self.VAEDec_provider="CPUExecutionProvider" 66 | self.TEXTEnc_provider="CPUExecutionProvider" 67 | self.DeepDanBooru_provider="CPUExecutionProvider" 68 | 69 | def load_config_json(self): 70 | try: 71 | with open('./Engine/config_files/EngineConfig.json', 'r') as openfile: 72 | jsonStr = json.load(openfile) 73 | self.MAINPipe_provider = jsonStr["MAINPipe_provider"] 74 | self.Scheduler_provider = jsonStr["Scheduler_provider"] 75 | self.ControlNet_provider = jsonStr["ControlNet_provider"] 76 | self.VAEDec_provider = jsonStr["VAEDec_provider"] 77 | self.TEXTEnc_provider = jsonStr["TEXTEnc_provider"] 78 | self.DeepDanBooru_provider = jsonStr["DeepDanBooru_provider"] 79 | except OSError: 80 | self.load_default_values() 81 | return self 82 | 83 | class UI_Configuration(Borg1): 84 | __loaded= False 85 | models_dir="" 86 | output_path = "" 87 | wildcards_activated=True 88 | Txt2img_Tab = None 89 | InPaint_Tab = None 90 | Img2Img_Tab = None 91 | InstructP2P_Tab = None 92 | ControlNet_Tab = None 93 | Tools_Tab = None 94 | Advanced_Config = None 95 | GradioPort = 7860 96 | 97 | def __init__(self): 98 | Borg1.__init__(self) 99 | if not self.__loaded: 100 | self.load_config() 101 | 102 | def __str__(self): return json.dumps(self.__dict__) 103 | 104 | def __load_default_values(self): 105 | import os 106 | self.models_dir=os.getcwd()+"\\models" 107 | self.output_path=os.getcwd()+"\\output" 108 | self.Txt2img_Tab = True 109 | self.InPaint_Tab = True 110 | self.Img2Img_Tab = True 111 | self.Tools_Tab = True 112 | self.InstructP2P_Tab = True 113 | self.ControlNet_Tab = True 114 | self.Advanced_Config = True 115 | self.GradioPort = 7860 116 | 117 | def save_config_json(self): 118 | jsonStr = json.dumps(self.__dict__) 119 | with open("./Engine/config_files/UIConfig.json", "w") as outfile: 120 | outfile.write(jsonStr) 121 | print("Saving UI Config") 122 | print(jsonStr) 123 | return jsonStr 124 | 125 | def __load_config_json(self): 126 | try: 127 | with open('./Engine/config_files/UIConfig.json', 'r') as openfile: 128 | jsonStr = json.load(openfile) 129 | self.models_dir = jsonStr["models_dir"] 130 | self.output_path= jsonStr["output_path"] 131 | self.Txt2img_Tab = jsonStr["Txt2img_Tab"] 132 | self.InPaint_Tab = jsonStr["InPaint_Tab"] 133 | self.Img2Img_Tab = jsonStr["Img2Img_Tab"] 134 | self.InstructP2P_Tab = jsonStr["InstructP2P_Tab"] 135 | self.ControlNet_Tab = int(jsonStr["ControlNet_Tab"]) 136 | self.Tools_Tab = jsonStr["Tools_Tab"] 137 | self.Advanced_Config = jsonStr["Advanced_Config"] 138 | self.GradioPort = int(jsonStr["GradioPort"]) 139 | 140 | except OSError: 141 | self.__load_default_values() 142 | return self 143 | 144 | def load_config(self): 145 | self.__load_config_json() 146 | self.__loaded=True 147 | 148 | 149 | class running_config(Borg2): 150 | Running_information= dict({"loaded":False}) 151 | 152 | def __init__(self): 153 | Borg2.__init__(self) 154 | if not self.Running_information["loaded"]==True: 155 | self.Running_information.update({"loaded":True}) 156 | 157 | def __str__(self): return json.dumps(self.__dict__) 158 | 159 | class VAE_config(Borg4): 160 | config = None 161 | def __init__(self): 162 | Borg4.__init__(self) 163 | if self.config == None: 164 | self.config = self.__load_VAE_config() 165 | 166 | def __load_VAE_config(self): 167 | import json 168 | standard_config = None 169 | try: 170 | with open('./Engine/config_files/VAE_Preferences.json', 'r') as openfile: 171 | jsonStr = json.load(openfile) 172 | vae_config = list(jsonStr) 173 | except OSError: 174 | standard_config = ["model"]*6 175 | self.config=vae_config 176 | return vae_config 177 | 178 | def save_VAE_config(self,vae_config): 179 | print("Saving VAE Config") 180 | 181 | import json 182 | json_data=jsonstr=json.dumps(vae_config) 183 | with open("./Engine/config_files/VAE_Preferences.json", "w") as outfile: 184 | outfile.write(json_data) 185 | 186 | def load_config_from_disk(self): 187 | self.config = self.__load_VAE_config() 188 | return self.config 189 | 190 | class TextEnc_config(Borg5): 191 | config = None 192 | def __init__(self): 193 | Borg5.__init__(self) 194 | if self.config == None: 195 | self.config = self.__load_TextEnc_config() 196 | 197 | def __load_TextEnc_config(self): 198 | import json 199 | TextEnc_config = None 200 | try: 201 | with open('./Engine/config_files/TextEnc_Preferences.json', 'r') as openfile: 202 | jsonStr = json.load(openfile) 203 | TextEnc_config = list(jsonStr) 204 | except OSError: 205 | TextEnc_config = ["model"]*2 206 | self.config=TextEnc_config 207 | return TextEnc_config 208 | 209 | def save_TextEnc_config(self,TextEnc_config): 210 | print("Salvando??") 211 | print(type(TextEnc_config)) 212 | 213 | import json 214 | json_data=json.dumps(TextEnc_config) 215 | with open("./Engine/config_files/TextEnc_Preferences.json", "w") as outfile: 216 | outfile.write(json_data) 217 | 218 | def load_config_from_disk(self): 219 | self.config = self.__load_TextEnc_config() 220 | return self.config 221 | 222 | class ControlNet_config(Borg3): 223 | config = None 224 | def __init__(self): 225 | Borg3.__init__(self) 226 | if self.config == None: 227 | self.config = self.__load_controlnet_config() 228 | 229 | def __str__(self): return json.dumps(self.__dict__) 230 | 231 | def __load_controlnet_config(self): 232 | import json 233 | standard_config = None 234 | try: 235 | with open('./Engine/config_files/ControlNet.json', 'r') as openfile: 236 | jsonStr = json.load(openfile) 237 | standard_config = dict(jsonStr) 238 | except OSError: 239 | standard_config = { 240 | "canny_active":False, 241 | "canny_path":"", 242 | "depth_active":False, 243 | "depth_path":"", 244 | "hed_active":False, 245 | "hed_path":"", 246 | "mlsd_active":False, 247 | "mlsd_path":"", 248 | "normal_active":False, 249 | "normal_path":"", 250 | "openpose_active":False, 251 | "openpose_path":"", 252 | "seg_active":False, 253 | "seg_path":"", 254 | } 255 | return standard_config 256 | 257 | def load_config_from_disk(self): 258 | self.config = self.__load_controlnet_config() 259 | self.available_controlnet_models() 260 | 261 | def save_controlnet_config(self,controlnet_config): 262 | print("Saving ControlNet models config") 263 | 264 | import json 265 | json_data=jsonstr=json.dumps(controlnet_config) 266 | with open("./Engine/config_files/ControlNet.json", "w") as outfile: 267 | outfile.write(json_data) 268 | 269 | def available_controlnet_models(self): 270 | available=[] 271 | for key, value in self.config.items(): 272 | if "active" in key and value == True: 273 | model=key.split('_')[0] 274 | available.append((model,self.config[model+"_path"])) 275 | return available #a list of tuples (model, path) 276 | 277 | 278 | -------------------------------------------------------------------------------- /Engine/txt2img_pipe_sub.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This code is copied from original pipeline, adding only the break option for callback''' 3 | 4 | import inspect 5 | from typing import Callable, List, Optional, Union 6 | 7 | import numpy as np 8 | import torch 9 | from transformers import CLIPImageProcessor, CLIPTokenizer 10 | 11 | from diffusers.configuration_utils import FrozenDict 12 | from diffusers.schedulers import DDIMScheduler, LMSDiscreteScheduler, PNDMScheduler 13 | from diffusers.utils import deprecate, logging 14 | from diffusers.pipelines.onnx_utils import ORT_TO_NP_TYPE, OnnxRuntimeModel 15 | from diffusers.pipelines.pipeline_utils import DiffusionPipeline 16 | from diffusers.pipelines.stable_diffusion import StableDiffusionPipelineOutput 17 | 18 | def __call__( 19 | self, 20 | prompt: Union[str, List[str]] = None, 21 | height: Optional[int] = 512, 22 | width: Optional[int] = 512, 23 | num_inference_steps: Optional[int] = 50, 24 | guidance_scale: Optional[float] = 7.5, 25 | negative_prompt: Optional[Union[str, List[str]]] = None, 26 | num_images_per_prompt: Optional[int] = 1, 27 | eta: Optional[float] = 0.0, 28 | generator: Optional[np.random.RandomState] = None, 29 | latents: Optional[np.ndarray] = None, 30 | prompt_embeds: Optional[np.ndarray] = None, 31 | negative_prompt_embeds: Optional[np.ndarray] = None, 32 | output_type: Optional[str] = "pil", 33 | return_dict: bool = True, 34 | callback: Optional[Callable[[int, int, np.ndarray], None]] = None, 35 | callback_steps: int = 1, 36 | ): 37 | r""" 38 | Function invoked when calling the pipeline for generation. 39 | 40 | Args: 41 | prompt (`str` or `List[str]`, *optional*): 42 | The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`. 43 | instead. 44 | image (`PIL.Image.Image` or List[`PIL.Image.Image`] or `torch.FloatTensor`): 45 | `Image`, or tensor representing an image batch which will be upscaled. * 46 | num_inference_steps (`int`, *optional*, defaults to 50): 47 | The number of denoising steps. More denoising steps usually lead to a higher quality image at the 48 | expense of slower inference. 49 | guidance_scale (`float`, *optional*, defaults to 7.5): 50 | Guidance scale as defined in [Classifier-Free Diffusion Guidance](https://arxiv.org/abs/2207.12598). 51 | `guidance_scale` is defined as `w` of equation 2. of [Imagen 52 | Paper](https://arxiv.org/pdf/2205.11487.pdf). Guidance scale is enabled by setting `guidance_scale > 53 | 1`. Higher guidance scale encourages to generate images that are closely linked to the text `prompt`, 54 | usually at the expense of lower image quality. 55 | negative_prompt (`str` or `List[str]`, *optional*): 56 | The prompt or prompts not to guide the image generation. If not defined, one has to pass 57 | `negative_prompt_embeds`. instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` 58 | is less than `1`). 59 | num_images_per_prompt (`int`, *optional*, defaults to 1): 60 | The number of images to generate per prompt. 61 | eta (`float`, *optional*, defaults to 0.0): 62 | Corresponds to parameter eta (η) in the DDIM paper: https://arxiv.org/abs/2010.02502. Only applies to 63 | [`schedulers.DDIMScheduler`], will be ignored for others. 64 | generator (`np.random.RandomState`, *optional*): 65 | One or a list of [numpy generator(s)](TODO) to make generation deterministic. 66 | latents (`np.ndarray`, *optional*): 67 | Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image 68 | generation. Can be used to tweak the same generation with different prompts. If not provided, a latents 69 | tensor will ge generated by sampling using the supplied random `generator`. 70 | prompt_embeds (`np.ndarray`, *optional*): 71 | Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not 72 | provided, text embeddings will be generated from `prompt` input argument. 73 | negative_prompt_embeds (`np.ndarray`, *optional*): 74 | Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt 75 | weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input 76 | argument. 77 | output_type (`str`, *optional*, defaults to `"pil"`): 78 | The output format of the generate image. Choose between 79 | [PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`. 80 | return_dict (`bool`, *optional*, defaults to `True`): 81 | Whether or not to return a [`~pipelines.stable_diffusion.StableDiffusionPipelineOutput`] instead of a 82 | plain tuple. 83 | callback (`Callable`, *optional*): 84 | A function that will be called every `callback_steps` steps during inference. The function will be 85 | called with the following arguments: `callback(step: int, timestep: int, latents: torch.FloatTensor)`. 86 | callback_steps (`int`, *optional*, defaults to 1): 87 | The frequency at which the `callback` function will be called. If not specified, the callback will be 88 | called at every step. 89 | 90 | Returns: 91 | [`~pipelines.stable_diffusion.StableDiffusionPipelineOutput`] or `tuple`: 92 | [`~pipelines.stable_diffusion.StableDiffusionPipelineOutput`] if `return_dict` is True, otherwise a `tuple. 93 | When returning a tuple, the first element is a list with the generated images, and the second element is a 94 | list of `bool`s denoting whether the corresponding generated image likely represents "not-safe-for-work" 95 | (nsfw) content, according to the `safety_checker`. 96 | """ 97 | #print("Using substituted pipe to allow inferences cancel") 98 | # check inputs. Raise error if not correct 99 | self.check_inputs( 100 | prompt, height, width, callback_steps, negative_prompt, prompt_embeds, negative_prompt_embeds 101 | ) 102 | 103 | # define call parameters 104 | if prompt is not None and isinstance(prompt, str): 105 | batch_size = 1 106 | elif prompt is not None and isinstance(prompt, list): 107 | batch_size = len(prompt) 108 | else: 109 | batch_size = prompt_embeds.shape[0] 110 | 111 | if generator is None: 112 | generator = np.random 113 | 114 | # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2) 115 | # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1` 116 | # corresponds to doing no classifier free guidance. 117 | do_classifier_free_guidance = guidance_scale > 1.0 118 | 119 | prompt_embeds = self._encode_prompt( 120 | prompt, 121 | num_images_per_prompt, 122 | do_classifier_free_guidance, 123 | negative_prompt, 124 | prompt_embeds=prompt_embeds, 125 | negative_prompt_embeds=negative_prompt_embeds, 126 | ) 127 | 128 | # get the initial random noise unless the user supplied it 129 | latents_dtype = prompt_embeds.dtype 130 | latents_shape = (batch_size * num_images_per_prompt, 4, height // 8, width // 8) 131 | if latents is None: 132 | latents = generator.randn(*latents_shape).astype(latents_dtype) 133 | elif latents.shape != latents_shape: 134 | raise ValueError(f"Unexpected latents shape, got {latents.shape}, expected {latents_shape}") 135 | 136 | # set timesteps 137 | self.scheduler.set_timesteps(num_inference_steps) 138 | 139 | latents = latents * np.float64(self.scheduler.init_noise_sigma) 140 | 141 | # prepare extra kwargs for the scheduler step, since not all schedulers have the same signature 142 | # eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers. 143 | # eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502 144 | # and should be between [0, 1] 145 | accepts_eta = "eta" in set(inspect.signature(self.scheduler.step).parameters.keys()) 146 | extra_step_kwargs = {} 147 | if accepts_eta: 148 | extra_step_kwargs["eta"] = eta 149 | 150 | timestep_dtype = next( 151 | (input.type for input in self.unet.model.get_inputs() if input.name == "timestep"), "tensor(float)" 152 | ) 153 | timestep_dtype = ORT_TO_NP_TYPE[timestep_dtype] 154 | 155 | for i, t in enumerate(self.progress_bar(self.scheduler.timesteps)): 156 | # expand the latents if we are doing classifier free guidance 157 | latent_model_input = np.concatenate([latents] * 2) if do_classifier_free_guidance else latents 158 | latent_model_input = self.scheduler.scale_model_input(torch.from_numpy(latent_model_input), t) 159 | latent_model_input = latent_model_input.cpu().numpy() 160 | 161 | # predict the noise residual 162 | timestep = np.array([t], dtype=timestep_dtype) 163 | noise_pred = self.unet(sample=latent_model_input, timestep=timestep, encoder_hidden_states=prompt_embeds) 164 | noise_pred = noise_pred[0] 165 | 166 | # perform guidance 167 | if do_classifier_free_guidance: 168 | noise_pred_uncond, noise_pred_text = np.split(noise_pred, 2) 169 | noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond) 170 | 171 | # compute the previous noisy sample x_t -> x_t-1 172 | scheduler_output = self.scheduler.step( 173 | torch.from_numpy(noise_pred), t, torch.from_numpy(latents), **extra_step_kwargs 174 | ) 175 | latents = scheduler_output.prev_sample.numpy() 176 | 177 | # call the callback, if provided 178 | 179 | if callback is not None and i % callback_steps == 0: 180 | cancel=callback(i, t, latents) 181 | if cancel: break 182 | 183 | latents = 1 / 0.18215 * latents 184 | #latents = 1 / 0.08333 * latents 185 | #print(type(latents)) 186 | #print(type(latents.shape)) 187 | #print(latents.shape) 188 | # image = self.vae_decoder(latent_sample=latents)[0] 189 | # it seems likes there is a strange result for using half-precision vae decoder if batchsize>1 190 | image = np.concatenate( 191 | [self.vae_decoder(latent_sample=latents[i : i + 1])[0] for i in range(latents.shape[0])] 192 | ) 193 | image = np.clip(image / 2 + 0.5, 0, 1) 194 | image = image.transpose((0, 2, 3, 1)) 195 | 196 | 197 | 198 | if self.safety_checker is not None: 199 | safety_checker_input = self.feature_extractor( 200 | self.numpy_to_pil(image), return_tensors="np" 201 | ).pixel_values.astype(image.dtype) 202 | 203 | images, has_nsfw_concept = [], [] 204 | for i in range(image.shape[0]): 205 | image_i, has_nsfw_concept_i = self.safety_checker( 206 | clip_input=safety_checker_input[i : i + 1], images=image[i : i + 1] 207 | ) 208 | images.append(image_i) 209 | has_nsfw_concept.append(has_nsfw_concept_i[0]) 210 | image = np.concatenate(images) 211 | else: 212 | has_nsfw_concept = None 213 | 214 | if output_type == "pil": 215 | image = self.numpy_to_pil(image) 216 | 217 | if not return_dict: 218 | return (image, has_nsfw_concept) 219 | 220 | #image.extend(images_saved) 221 | return StableDiffusionPipelineOutput(images=image, nsfw_content_detected=has_nsfw_concept) 222 | -------------------------------------------------------------------------------- /Scripts/image_slicer.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | from PIL import Image 4 | from math import sqrt, ceil, floor 5 | 6 | 7 | 8 | class Tile(object): 9 | """Represents a single tile.""" 10 | def __init__(self, image, number, position, coords, filename=None): 11 | self.image = image 12 | self.number = number 13 | self.position = position 14 | self.coords = coords 15 | self.filename = filename 16 | 17 | @property 18 | def row(self): 19 | return self.position[0] 20 | 21 | @property 22 | def column(self): 23 | return self.position[1] 24 | 25 | @property 26 | def basename(self): 27 | """Strip path and extension. Return base filename.""" 28 | return get_basename(self.filename) 29 | 30 | def generate_filename( 31 | self, directory=os.getcwd(), prefix="tile", format="png", path=True 32 | ): 33 | """Construct and return a filename for this tile.""" 34 | filename = prefix + "_{col:02d}_{row:02d}.{ext}".format( 35 | col=self.column, row=self.row, ext=format.lower().replace("jpeg", "jpg") 36 | ) 37 | if not path: 38 | return filename 39 | return os.path.join(directory, filename) 40 | 41 | def save(self, filename=None, format="png"): 42 | if not filename: 43 | filename = self.generate_filename(format=format) 44 | self.image.save(filename, format) 45 | self.filename = filename 46 | 47 | def __repr__(self): 48 | """Show tile number, and if saved to disk, filename.""" 49 | if self.filename: 50 | return "".format( 51 | self.number, os.path.basename(self.filename) 52 | ) 53 | return "".format(self.number) 54 | 55 | 56 | def calc_columns_rows(n): 57 | """ 58 | Calculate the number of columns and rows required to divide an image 59 | into ``n`` parts. 60 | 61 | Return a tuple of integers in the format (num_columns, num_rows) 62 | """ 63 | num_columns = int(ceil(sqrt(n))) 64 | num_rows = int(ceil(n / float(num_columns))) 65 | return (num_columns, num_rows) 66 | 67 | 68 | def get_combined_size(tiles): 69 | """Calculate combined size of tiles.""" 70 | # TODO: Refactor calculating layout to avoid repetition. 71 | columns, rows = calc_columns_rows(len(tiles)) 72 | tile_size = tiles[0].image.size 73 | return (tile_size[0] * columns, tile_size[1] * rows) 74 | 75 | 76 | def join(tiles,row=0,col=0, width=0, height=0): 77 | """ 78 | @param ``tiles`` - Tuple of ``Image`` instances. 79 | @param ``width`` - Optional, width of combined image. 80 | @param ``height`` - Optional, height of combined image. 81 | @return ``Image`` instance. 82 | """ 83 | # Don't calculate size if width and height are provided 84 | # this allows an application that knows what the 85 | # combined size should be to construct an image when 86 | # pieces are missing. 87 | 88 | if width > 0 and height > 0: 89 | im = Image.new("RGBA", (width, height), None) 90 | else: 91 | #im = Image.new("RGBA", get_combined_size(tiles), None) 92 | total_size = tiles[0].image.size[0] * col, tiles[0].image.size[1] * row #(width*cols , height*rows) 93 | im = Image.new("RGBA", total_size, None) 94 | 95 | #columns, rows = calc_columns_rows(len(tiles)) 96 | columns=col 97 | rows=row 98 | for tile in tiles: 99 | try: 100 | im.paste(tile.image, tile.coords) 101 | except IOError: 102 | # do nothing, blank out the image 103 | continue 104 | return im 105 | 106 | 107 | def validate_image(image, number_tiles): 108 | """Basic sanity checks prior to performing a split.""" 109 | TILE_LIMIT = 99 * 99 110 | 111 | try: 112 | number_tiles = int(number_tiles) 113 | except BaseException: 114 | raise ValueError("number_tiles could not be cast to integer.") 115 | 116 | if number_tiles > TILE_LIMIT or number_tiles < 2: 117 | raise ValueError( 118 | "Number of tiles must be between 2 and {} (you \ 119 | asked for {}).".format( 120 | TILE_LIMIT, number_tiles 121 | ) 122 | ) 123 | 124 | 125 | def validate_image_col_row(image, col, row): 126 | """Basic checks for columns and rows values""" 127 | SPLIT_LIMIT = 99 128 | 129 | try: 130 | col = int(col) 131 | row = int(row) 132 | except BaseException: 133 | raise ValueError("columns and rows values could not be cast to integer.") 134 | 135 | if col < 1 or row < 1 or col > SPLIT_LIMIT or row > SPLIT_LIMIT: 136 | raise ValueError( 137 | f"Number of columns and rows must be between 1 and" 138 | f"{SPLIT_LIMIT} (you asked for rows: {row} and col: {col})." 139 | ) 140 | if col == 1 and row == 1: 141 | raise ValueError("There is nothing to divide. You asked for the entire image.") 142 | 143 | 144 | 145 | def slice( 146 | image, 147 | number_tiles=None, 148 | col=None, 149 | row=None, 150 | save=True, 151 | DecompressionBombWarning=True, 152 | ): 153 | """ 154 | Split an image into a specified number of tiles. 155 | 156 | Args: 157 | filename (str): The filename of the image to split. 158 | number_tiles (int): The number of tiles required. 159 | 160 | Kwargs: 161 | save (bool): Whether or not to save tiles to disk. 162 | DecompressionBombWarning (bool): Whether to suppress 163 | Pillow DecompressionBombWarning 164 | 165 | Returns: 166 | Tuple of :class:`Tile` instances. 167 | """ 168 | if DecompressionBombWarning is False: 169 | Image.MAX_IMAGE_PIXELS = None 170 | 171 | #im = Image.open(filename) 172 | im = image 173 | im_w, im_h = im.size 174 | 175 | columns = 0 176 | rows = 0 177 | if number_tiles: 178 | validate_image(im, number_tiles) 179 | columns, rows = calc_columns_rows(number_tiles) 180 | else: 181 | validate_image_col_row(im, col, row) 182 | columns = col 183 | rows = row 184 | 185 | tile_w, tile_h = int(floor(im_w / columns)), int(floor(im_h / rows)) 186 | 187 | tiles = [] 188 | number = 1 189 | for pos_y in range(0, im_h - rows, tile_h): # -rows for rounding error. 190 | for pos_x in range(0, im_w - columns, tile_w): # as above. 191 | area = (pos_x, pos_y, pos_x + tile_w, pos_y + tile_h) 192 | image = im.crop(area) 193 | position = (int(floor(pos_x / tile_w)) + 1, int(floor(pos_y / tile_h)) + 1) 194 | coords = (pos_x, pos_y) 195 | tile = Tile(image, number, position, coords) 196 | tiles.append(tile) 197 | number += 1 198 | prefix="TemporalImage" 199 | directory=create_temp_dir() 200 | if save: 201 | save_tiles( 202 | tiles, prefix, directory 203 | ) 204 | return tuple(tiles) 205 | 206 | def create_temp_dir(): 207 | TempDir = "./Temp" 208 | try: 209 | os.mkdir(TempDir) 210 | finally: 211 | return TempDir 212 | 213 | def delete_temp_dir(): 214 | TempDir = "./Temp" 215 | for files in sorted(os.listdir(TempDir)): 216 | os.remove(TempDir+"/"+files) 217 | try: 218 | os.rmdir(TempDir) 219 | finally: 220 | return 221 | 222 | def save_tiles(tiles, prefix="", directory=os.getcwd(), format="png"): 223 | """ 224 | Write image files to disk. Create specified folder(s) if they 225 | don't exist. Return list of :class:`Tile` instance. 226 | 227 | Args: 228 | tiles (list): List, tuple or set of :class:`Tile` objects to save. 229 | prefix (str): Filename prefix of saved tiles. 230 | 231 | Kwargs: 232 | directory (str): Directory to save tiles. Created if non-existant. 233 | 234 | Returns: 235 | Tuple of :class:`Tile` instances. 236 | """ 237 | for tile in tiles: 238 | tile.save( 239 | filename=tile.generate_filename( 240 | prefix=prefix, directory=directory, format=format 241 | ), 242 | format=format, 243 | ) 244 | return tuple(tiles) 245 | 246 | 247 | def get_image_column_row(filename): 248 | """Determine column and row position for filename.""" 249 | row, column = os.path.splitext(filename)[0][-5:].split("_") 250 | return (int(column) - 1, int(row) - 1) 251 | 252 | 253 | def open_images_in(directory): 254 | """Open all images in a directory. Return tuple of Tile instances.""" 255 | 256 | files = [ 257 | filename 258 | for filename in os.listdir(directory) 259 | if "_" in filename and not filename.startswith("joined") 260 | ] 261 | tiles = [] 262 | if len(files) > 0: 263 | i = 0 264 | for file in files: 265 | pos = get_image_column_row(file) 266 | im = Image.open(os.path.join(directory, file)) 267 | 268 | position_xy = [0, 0] 269 | count = 0 270 | for a, b in zip(pos, im.size): 271 | position_xy[count] = a * b 272 | count = count + 1 273 | tiles.append( 274 | Tile( 275 | image=im, 276 | position=pos, 277 | number=i + 1, 278 | coords=position_xy, 279 | filename=file, 280 | ) 281 | ) 282 | i = i + 1 283 | return tiles 284 | 285 | 286 | def get_basename(filename): 287 | """Strip path and extension. Return basename.""" 288 | return os.path.splitext(os.path.basename(filename))[0] 289 | 290 | def adjust_image_size(img,rows,cols): 291 | new_width=(img.size[0] // cols)*cols 292 | if (new_width % 2) == 1: 293 | new_width=(1+(img.size[0] // cols))*cols 294 | else: 295 | new_width==(img.size[0] // cols)*cols 296 | 297 | new_height=(img.size[1] // rows)*rows 298 | if (new_height % 2) == 1: 299 | new_height=(1+(img.size[1] // rows))*rows 300 | else: 301 | new_height==(img.size[1] // rows)*rows 302 | 303 | return img.resize([new_width, new_height]) 304 | 305 | 306 | def open_images(directory): 307 | """Open all images in a directory. Return tuple of Image instances.""" 308 | return [Image.open(os.path.join(directory, file)) for file in os.listdir(directory)] 309 | 310 | 311 | def get_columns_rows(filenames): 312 | """Derive number of columns and rows from filenames.""" 313 | tiles = [] 314 | for filename in filenames: 315 | row, column = os.path.splitext(filename)[0][-5:].split("_") 316 | tiles.append((int(row), int(column))) 317 | rows = [pos[0] for pos in tiles] 318 | columns = [pos[1] for pos in tiles] 319 | num_rows = max(rows) 320 | num_columns = max(columns) 321 | return (num_columns, num_rows) 322 | 323 | 324 | def create_slice_of_substitute_for_column_joint(file,pixels=8): 325 | img = Image.open(file) 326 | img_middle=img.size[0]/2 327 | img = img.crop((img_middle-pixels,0,img_middle+pixels,img.size[1])) 328 | img.save(file) 329 | 330 | def create_slice_of_substitute_for_row_joint(file,pixels=8): 331 | img = Image.open(file) 332 | img_middle=img.size[1]/2 333 | img = img.crop((0,img_middle-pixels,img.size[0],img_middle+pixels)) 334 | img.save(file) 335 | 336 | 337 | def substitute_image_joint_vertical_marks(img, tiles, pixels, cols): 338 | pixels=8 339 | for tile_mark in tiles: 340 | try: 341 | coord=get_image_column_row(tile_mark.filename) 342 | position_x=((coord[0]+1)*(int(img.size[0]/cols)))-pixels 343 | img.paste(tile_mark.image, [position_x,tile_mark.coords[1]]) 344 | except IOError: 345 | # do nothing, blank out the image 346 | continue 347 | 348 | return img 349 | 350 | 351 | def substitute_image_joint_horizontal_marks(img, tiles, pixels, rows): 352 | pixels=8 353 | for tile_mark in tiles: 354 | try: 355 | coord=get_image_column_row(tile_mark.filename) 356 | position_y=((coord[1]+1)*(int(img.size[1]/rows)))-pixels 357 | img.paste(tile_mark.image, [tile_mark.coords[0],position_y,]) 358 | except IOError: 359 | # do nothing, blank out the image 360 | continue 361 | 362 | return img 363 | 364 | 365 | 366 | -------------------------------------------------------------------------------- /UI/HiRes_txt2img_ui.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | import os,gc,re 3 | from Engine.General_parameters import Engine_Configuration as Engine_config 4 | from Engine.General_parameters import running_config 5 | from Engine.General_parameters import UI_Configuration 6 | from Engine import pipelines_engines 7 | from UI import UI_common_funcs as UI_common 8 | from Engine import engine_common_funcs as Engine_common 9 | from PIL import Image, PngImagePlugin 10 | 11 | global next_prompt 12 | global processed_images 13 | processed_images=[] 14 | next_prompt=None 15 | global number_of_passes 16 | 17 | 18 | def show_HiRes_txt2img_ui(): 19 | model_list = UI_common.get_model_list("txt2img") 20 | sched_list = get_schedulers_list() 21 | ui_config=UI_Configuration() 22 | gr.Markdown("Start typing below and then click **Generate** to see the output.") 23 | with gr.Row(): 24 | with gr.Column(scale=13, min_width=650): 25 | model_drop = gr.Dropdown(model_list, value=(model_list[0] if len(model_list) > 0 else None), label="model folder", interactive=True) 26 | with gr.Accordion("Partial Reloads",open=False): 27 | reload_vae_btn = gr.Button("VAE Decoder:Apply Changes & Reload") 28 | #reload_model_btn = gr.Button("Model:Apply new model & Fast Reload Pipe") 29 | 30 | sch_t0 = gr.Radio(sched_list, value=sched_list[0], label="scheduler") 31 | prompt_t0 = gr.Textbox(value="", lines=2, label="prompt") 32 | neg_prompt_t0 = gr.Textbox(value="", lines=2, label="negative prompt") 33 | 34 | with gr.Accordion(label="Latents experimentals",open=False): 35 | #multiplier = gr.Slider(0, 1, value=0.18215, step=0.05, label="Multiplier, blurry the ingested latent, 1 to do not modify", interactive=True) 36 | #strengh_t0 = gr.Slider(0, 1, value=0.8, step=0.05, label="Strengh, or % of steps to apply the latent", interactive=True) 37 | #offset_t0 = gr.Slider(0, 100, value=1, step=1, label="Offset Steps for the scheduler", interactive=True) 38 | #latents_experimental1 = gr.Checkbox(label="Save generated latents ", value=False, interactive=True) 39 | latents_experimental2 = gr.Checkbox(label="Load latent from a generation", value=False, interactive=True) 40 | name_of_latent = gr.Textbox(value="", lines=1, label="Names of Numpy File Latents (1:x.npy,2:y.pt,3:noise-(width)xheight())") 41 | latent_formula = gr.Textbox(value="", lines=1, label="Formula for the sumatory of latents") 42 | 43 | 44 | with gr.Row(): 45 | gr.Markdown("Common parameters") 46 | 47 | with gr.Row(): 48 | iter_t0 = gr.Slider(1, 100, value=1, step=1, label="iteration count") 49 | guid_t0 = gr.Slider(0, 50, value=7.5, step=0.1, label="guidance") 50 | batch_t0 = gr.Slider(1, 4, value=1, step=1, label="batch size", interactive=False,visible=False) 51 | with gr.Row(): 52 | eta_t0 = gr.Slider(0, 1, value=0.0, step=0.01, label="DDIM eta", interactive=True) 53 | seed_t0 = gr.Textbox(value="", max_lines=1, label="seed") 54 | fmt_t0 = gr.Radio(["png", "jpg"], value="png", label="image format", interactive=False,visible=False) 55 | 56 | with gr.Row(): 57 | gr.Markdown("HiRes parameters") 58 | with gr.Row(): 59 | height_t1 = gr.Slider(64, 2048, value=512, step=64, label="HiRes height") 60 | width_t1 = gr.Slider(64, 2048, value=512, step=64, label="HiRes width") 61 | with gr.Row(): 62 | steps_t1 = gr.Slider(1, 100, value=16, step=1, label="HiRes steps") 63 | hires_passes_t1 = gr.Slider(1, 10, value=2, step=1, label="HiRes passes") 64 | 65 | with gr.Row(): 66 | gr.Markdown("First pass parameters") 67 | with gr.Row(): 68 | height_t0 = gr.Slider(64, 2048, value=512, step=64, label="height") 69 | width_t0 = gr.Slider(64, 2048, value=512, step=64, label="width") 70 | steps_t0 = gr.Slider(1, 3000, value=16, step=1, label="steps") 71 | 72 | with gr.Row(): 73 | gr.Markdown("Other parameters") 74 | save_textfile=gr.Checkbox(label="Save prompt into a txt file") 75 | save_low_res=gr.Checkbox(label="Save generated Low-Res Img") 76 | 77 | with gr.Column(scale=11, min_width=550): 78 | with gr.Row(): 79 | gen_btn = gr.Button("Generate", variant="primary", elem_id="gen_button") 80 | clear_btn = gr.Button("Cancel",info="Cancel at end of current iteration",variant="stop", elem_id="gen_button") 81 | memory_btn = gr.Button("Release memory", elem_id="mem_button") 82 | 83 | if ui_config.wildcards_activated: 84 | from UI import styles_ui 85 | styles_ui.show_styles_ui() 86 | with gr.Accordion(label="Live Prompt & Wildcards for multiple iterations",open=False): 87 | with gr.Row(): 88 | next_wildcard = gr.Textbox(value="",lines=4, label="Next Prompt", interactive=True) 89 | discard = gr.Textbox(value="", label="Discard", visible=False, interactive=False) 90 | with gr.Row(): 91 | wildcard_show_btn = gr.Button("Show next prompt", elem_id="wildcard_button") 92 | wildcard_gen_btn = gr.Button("Regenerate next prompt", variant="primary", elem_id="wildcard_button") 93 | wildcard_apply_btn = gr.Button("Use edited prompt", elem_id="wildcard_button") 94 | with gr.Row(): 95 | image_out = gr.Gallery(value=None, label="output images") 96 | with gr.Accordion(label="Low Res output images",open=False): 97 | with gr.Row(): 98 | low_res_image_out = gr.Gallery(value=None, label="Low res output images") 99 | with gr.Row(): 100 | status_out = gr.Textbox(value="", label="status") 101 | with gr.Row(): 102 | Selected_image_status= gr.Textbox(value="", label="status",visible=True) 103 | Selected_image_index= gr.Number(show_label=False, visible=False) 104 | 105 | 106 | image_out.select(fn=get_select_index, inputs=[image_out,status_out], outputs=[Selected_image_index,Selected_image_status]) 107 | clear_btn.click(fn=UI_common.cancel_iteration,inputs=None,outputs=None) 108 | reload_vae_btn.click(fn=change_vae,inputs=model_drop,outputs=None) 109 | #reload_model_btn.click(fn=change_model,inputs=model_drop,outputs=None) 110 | 111 | list_of_All_Parameters2=[model_drop,prompt_t0,neg_prompt_t0,sch_t0,iter_t0,batch_t0,steps_t0,steps_t1,guid_t0,height_t0,width_t0,height_t1,width_t1,eta_t0,seed_t0,fmt_t0,hires_passes_t1,save_textfile, save_low_res,latent_formula,name_of_latent,latents_experimental2] 112 | 113 | memory_btn.click(fn=UI_common.clean_memory_click, inputs=None, outputs=None) 114 | 115 | 116 | gen_btn.click(fn=generate_click,inputs=list_of_All_Parameters2,outputs=[image_out,status_out,low_res_image_out]) 117 | 118 | if ui_config.wildcards_activated: 119 | wildcard_gen_btn.click(fn=gen_next_prompt, inputs=prompt_t0, outputs=[discard,next_wildcard]) 120 | wildcard_show_btn.click(fn=show_next_prompt, inputs=None, outputs=next_wildcard) 121 | wildcard_apply_btn.click(fn=apply_prompt, inputs=next_wildcard, outputs=None) 122 | 123 | 124 | 125 | def change_vae(model_drop): 126 | from Engine.pipelines_engines import txt2img_pipe 127 | from Engine.pipelines_engines import Vae_and_Text_Encoders 128 | pipe=txt2img_pipe().txt2img_pipe 129 | vae=Vae_and_Text_Encoders() 130 | pipe.vae_decoder=vae.load_vaedecoder(f"{UI_Configuration().models_dir}\{model_drop}") 131 | return 132 | 133 | 134 | def get_select_index(image_out,status_out, evt:gr.SelectData): 135 | status_out=eval(status_out) 136 | global number_of_passes 137 | number_of_passes 138 | resto=evt.index % number_of_passes 139 | index= (evt.index-resto)/number_of_passes 140 | 141 | return index,status_out[int(index)] 142 | #return evt.index,status_out[int(index)] 143 | 144 | def gallery_view(images,dict_statuses): 145 | return images[0] 146 | 147 | 148 | def get_schedulers_list(): 149 | sched_config = pipelines_engines.SchedulersConfig() 150 | sched_list =sched_config.available_schedulers 151 | return sched_list 152 | 153 | def select_scheduler(sched_name,model_path): 154 | return pipelines_engines.SchedulersConfig().scheduler(sched_name,model_path) 155 | 156 | def gen_next_prompt(prompt_t0,initial=False): 157 | global next_prompt 158 | Running_information= running_config().Running_information 159 | style=Running_information["Style"] 160 | 161 | style_pre ="" 162 | style_post="" 163 | if style: 164 | styles=style.split("|") 165 | style_pre =styles[0] 166 | style_post=styles[1] 167 | 168 | if (initial): 169 | next_prompt=None 170 | prompt=prompt_t0 171 | else: 172 | if (next_prompt != None): 173 | prompt=next_prompt 174 | else: 175 | prompt=wildcards_process(prompt_t0) 176 | 177 | next_prompt=wildcards_process(prompt_t0) 178 | prompt = style_pre+" " +prompt+" " +style_post 179 | #print(f"Prompt:{prompt}") 180 | return prompt,next_prompt 181 | 182 | def apply_prompt(prompt): 183 | global next_prompt 184 | next_prompt=prompt 185 | 186 | def wildcards_process(prompt): 187 | from Scripts import wildcards 188 | wildcard=wildcards.WildcardsScript() 189 | new_prompt,discarded=wildcard.process(prompt) 190 | return new_prompt 191 | 192 | def show_next_prompt(): 193 | global next_prompt 194 | return next_prompt 195 | 196 | 197 | def generate_click( 198 | model_drop,prompt_t0,neg_prompt_t0,sch_t0, 199 | iter_t0,batch_t0,steps_t0,steps_t1,guid_t0,height_t0, 200 | width_t0,height_t1,width_t1,eta_t0,seed_t0,fmt_t0, 201 | hires_passes_t1,save_textfile, save_low_res, 202 | latent_formula,name_of_latent,latents_experimental2): 203 | 204 | from Engine.General_parameters import running_config 205 | 206 | if latents_experimental2: 207 | running_config().Running_information.update({"Load_Latents":True}) 208 | running_config().Running_information.update({"Latent_Name":name_of_latent}) 209 | running_config().Running_information.update({"Latent_Formula":latent_formula}) 210 | else: 211 | running_config().Running_information.update({"Load_Latents":False}) 212 | 213 | from Engine.Pipelines.txt2img_hires import txt2img_hires_pipe 214 | 215 | global number_of_passes 216 | number_of_passes=hires_passes_t1 217 | 218 | Running_information= running_config().Running_information 219 | Running_information.update({"Running":True}) 220 | 221 | if (Running_information["model"] != model_drop or Running_information["tab"] != "hires_txt2img"): 222 | UI_common.clean_memory_click() 223 | Running_information.update({"model":model_drop}) 224 | Running_information.update({"tab":"hires_txt2img"}) 225 | 226 | model_path=UI_Configuration().models_dir+"\\"+model_drop 227 | txt2img_hires_pipe().initialize(model_path,sch_t0) 228 | txt2img_hires_pipe().create_seeds(seed_t0,iter_t0,False) 229 | images= [] 230 | images_low= [] 231 | information=[] 232 | counter=1 233 | img_index=Engine_common.get_next_save_index(output_path=UI_Configuration().output_path) 234 | 235 | for seed in txt2img_hires_pipe().seeds: 236 | if running_config().Running_information["cancelled"]: 237 | break 238 | prompt,discard=gen_next_prompt(prompt_t0) 239 | print(f"Iteration:{counter}/{iter_t0}") 240 | counter+=1 241 | #batch_images,info = txt2img_hires_pipe().run_inference( 242 | lowres_image,hires_images,info = txt2img_hires_pipe().run_inference( 243 | prompt, 244 | neg_prompt_t0, 245 | hires_passes_t1, 246 | height_t0, 247 | width_t0, 248 | height_t1, 249 | width_t1, 250 | steps_t0, 251 | steps_t1, 252 | guid_t0, 253 | eta_t0, 254 | batch_t0, 255 | seed) 256 | for hires_image in hires_images: 257 | images.append(hires_image) 258 | images_low.append(lowres_image) 259 | info=dict(info) 260 | info['Sched:']=sch_t0 261 | information.append(info) 262 | 263 | style= running_config().Running_information["Style"] 264 | Engine_common.save_image(hires_images,info,img_index,UI_Configuration().output_path,style,save_textfile) 265 | img_index+=1 266 | if save_low_res: 267 | Engine_common.save_image([lowres_image],info,img_index,UI_Configuration().output_path) 268 | img_index+=1 269 | 270 | running_config().Running_information.update({"cancelled":False}) 271 | gen_next_prompt("",True) 272 | Running_information.update({"Running":False}) 273 | #return images,information 274 | 275 | return images,information,images_low -------------------------------------------------------------------------------- /UI/txt2img_ui.py: -------------------------------------------------------------------------------- 1 | import gradio as gr 2 | import os,gc,re 3 | from Engine.General_parameters import Engine_Configuration as Engine_config 4 | from Engine.General_parameters import running_config 5 | from Engine.General_parameters import UI_Configuration 6 | from Engine import pipelines_engines 7 | from UI import UI_common_funcs as UI_common 8 | from Engine import engine_common_funcs as Engine_common 9 | from PIL import Image, PngImagePlugin 10 | 11 | global next_prompt 12 | global processed_images 13 | processed_images=[] 14 | next_prompt=None 15 | 16 | 17 | def show_txt2img_ui(): 18 | model_list = UI_common.get_model_list("txt2img") 19 | sched_list = get_schedulers_list() 20 | ui_config=UI_Configuration() 21 | gr.Markdown("Start typing below and then click **Generate** to see the output.") 22 | with gr.Row(): 23 | with gr.Column(scale=13, min_width=650): 24 | model_drop = gr.Dropdown(model_list, value=(model_list[0] if len(model_list) > 0 else None), label="model folder", interactive=True) 25 | 26 | with gr.Accordion("Partial Reloads",open=False): 27 | reload_vae_btn = gr.Button("VAE Decoder:Apply Changes & Reload") 28 | reload_model_btn = gr.Button("Model:Apply new model & Fast Reload Pipe") 29 | with gr.Accordion(label="Latents experimentals",open=False): 30 | multiplier = gr.Slider(0, 1, value=0.18215, step=0.05, label="Multiplier, blurry the ingested latent, 1 to do not modify", interactive=True) 31 | strengh_t0 = gr.Slider(0, 1, value=0.8, step=0.05, label="Strengh, or % of steps to apply the latent", interactive=True) 32 | offset_t0 = gr.Slider(0, 100, value=1, step=1, label="Offset Steps for the scheduler", interactive=True) 33 | latents_experimental1 = gr.Checkbox(label="Save generated latents ", value=False, interactive=True) 34 | latents_experimental2 = gr.Checkbox(label="Load latent from a generation", value=False, interactive=True) 35 | name_of_latent = gr.Textbox(value="", lines=1, label="Name of Numpy- Latent") 36 | latent_formula = gr.Textbox(value="", lines=1, label="Formula for the sumatory of latents") 37 | latent_to_img_btn = gr.Button("Convert all latents to imgs", variant="primary", elem_id="gen_button") 38 | prompt_t0 = gr.Textbox(value="", lines=2, label="prompt") 39 | neg_prompt_t0 = gr.Textbox(value="", lines=2, label="negative prompt") 40 | sch_t0 = gr.Radio(sched_list, value=sched_list[0], label="scheduler") 41 | 42 | with gr.Row(): 43 | iter_t0 = gr.Slider(1, 100, value=1, step=1, label="iteration count") 44 | batch_t0 = gr.Slider(1, 4, value=1, step=1, label="batch size", interactive=False) 45 | steps_t0 = gr.Slider(1, 3000, value=16, step=1, label="steps") 46 | guid_t0 = gr.Slider(0, 50, value=7.5, step=0.1, label="guidance") 47 | height_t0 = gr.Slider(64, 2048, value=512, step=64, label="height") 48 | width_t0 = gr.Slider(64, 2048, value=512, step=64, label="width") 49 | eta_t0 = gr.Slider(0, 1, value=0.0, step=0.01, label="DDIM eta", interactive=True) 50 | seed_t0 = gr.Textbox(value="", max_lines=1, label="seed") 51 | fmt_t0 = gr.Radio(["png", "jpg"], value="png", label="image format") 52 | with gr.Column(scale=11, min_width=550): 53 | with gr.Row(): 54 | gen_btn = gr.Button("Generate", variant="primary", elem_id="gen_button") 55 | clear_btn = gr.Button("Cancel",info="Cancel at end of current iteration",variant="stop", elem_id="gen_button") 56 | memory_btn = gr.Button("Release memory", elem_id="mem_button") 57 | 58 | if ui_config.wildcards_activated: 59 | from UI import styles_ui 60 | styles_ui.show_styles_ui() 61 | with gr.Accordion(label="Live Prompt & Wildcards for multiple iterations",open=False): 62 | with gr.Row(): 63 | next_wildcard = gr.Textbox(value="",lines=4, label="Next Prompt", interactive=True) 64 | discard = gr.Textbox(value="", label="Discard", visible=False, interactive=False) 65 | with gr.Row(): 66 | wildcard_show_btn = gr.Button("Show next prompt", elem_id="wildcard_button") 67 | wildcard_gen_btn = gr.Button("Regenerate next prompt", variant="primary", elem_id="wildcard_button") 68 | wildcard_apply_btn = gr.Button("Use edited prompt", elem_id="wildcard_button") 69 | with gr.Row(): 70 | image_out = gr.Gallery(value=None, label="output images") 71 | 72 | with gr.Row(): 73 | status_out = gr.Textbox(value="", label="status") 74 | with gr.Row(): 75 | Selected_image_status= gr.Textbox(value="", label="status",visible=True) 76 | Selected_image_index= gr.Number(show_label=False, visible=False) 77 | with gr.Accordion(label="Edit/Save gallery images",open=False): 78 | with gr.Row(): 79 | delete_btn = gr.Button("Delete", elem_id="gallery_button") 80 | save_btn = gr.Button("Save", elem_id="gallery_button") 81 | #previous_btn = gr.Button("Previous", elem_id="gallery_button") 82 | #next_btn = gr.Button("Next", elem_id="gallery_button") 83 | 84 | image_out.select(fn=get_select_index, inputs=[image_out,status_out], outputs=[Selected_image_index,Selected_image_status]) 85 | delete_btn.click(fn=delete_selected_index, inputs=[Selected_image_index,status_out], outputs=[image_out,status_out]) 86 | clear_btn.click(fn=UI_common.cancel_iteration,inputs=None,outputs=None) 87 | reload_vae_btn.click(fn=change_vae,inputs=model_drop,outputs=None) 88 | reload_model_btn.click(fn=change_model,inputs=model_drop,outputs=None) 89 | 90 | list_of_All_Parameters=[model_drop,prompt_t0,neg_prompt_t0,sch_t0,iter_t0,batch_t0,steps_t0,guid_t0,height_t0,width_t0,eta_t0,seed_t0,fmt_t0,multiplier,strengh_t0,name_of_latent,latent_formula,offset_t0] 91 | gen_btn.click(fn=generate_click, inputs=list_of_All_Parameters, outputs=[image_out,status_out]) 92 | #sch_t0.change(fn=select_scheduler, inputs=sch_t0, outputs= None) #Atencion cambiar el DDIM ETA si este se activa 93 | memory_btn.click(fn=UI_common.clean_memory_click, inputs=None, outputs=None) 94 | 95 | 96 | latents_experimental1.change(fn=_activate_latent_save, inputs=latents_experimental1, outputs= None) 97 | latents_experimental2.change(fn=_activate_latent_load, inputs=[latents_experimental2,name_of_latent], outputs= None) 98 | latent_to_img_btn.click(fn=_latent_to_img,inputs=None,outputs=None) 99 | 100 | if ui_config.wildcards_activated: 101 | wildcard_gen_btn.click(fn=gen_next_prompt, inputs=prompt_t0, outputs=[discard,next_wildcard]) 102 | wildcard_show_btn.click(fn=show_next_prompt, inputs=None, outputs=next_wildcard) 103 | wildcard_apply_btn.click(fn=apply_prompt, inputs=next_wildcard, outputs=None) 104 | 105 | def _latent_to_img(): 106 | import numpy as np 107 | latent_path="./latents" 108 | latent_list = [] 109 | try: 110 | with os.scandir(latent_path) as scan_it: 111 | for entry in scan_it: 112 | if ".npy" in entry.name: 113 | latent_list.append(entry.name) 114 | print(latent_list) 115 | except: 116 | print("Not numpys found.Wrong Directory or no files inside?") 117 | 118 | 119 | vaedec=pipelines_engines.Vae_and_Text_Encoders().vae_decoder 120 | 121 | if vaedec==None: 122 | print("NO VAE LOADED, if you got a default VAE path configurated it will be loaded, if not, will raise an exception") 123 | vaedec= pipelines_engines.Vae_and_Text_Encoders().load_vaedecoder("") 124 | 125 | for latent in latent_list: 126 | loaded_latent=np.load(f"./latents/{latent}") 127 | loaded_latent = 1 / 0.18215 * loaded_latent 128 | image=vaedec(latent_sample=loaded_latent)[0] 129 | name= latent[:-3] 130 | name= name+"png" 131 | image = np.clip(image / 2 + 0.5, 0, 1) 132 | image = image.transpose((0, 2, 3, 1)) 133 | image = Engine_common.numpy_to_pil(image)[0] 134 | image.save(f"./latents/{name}",optimize=True) 135 | 136 | return 137 | 138 | def _activate_latent_save(activate_latent_save): 139 | from Engine.General_parameters import running_config 140 | running_config().Running_information.update({"Save_Latents":activate_latent_save}) 141 | 142 | def _activate_latent_load(activate_latent_load,name_of_latent): 143 | from Engine.General_parameters import running_config 144 | running_config().Running_information.update({"Load_Latents":activate_latent_load}) 145 | 146 | 147 | def change_vae(model_drop): 148 | from Engine.pipelines_engines import txt2img_pipe 149 | from Engine.pipelines_engines import Vae_and_Text_Encoders 150 | pipe=txt2img_pipe().txt2img_pipe 151 | vae=Vae_and_Text_Encoders() 152 | pipe.vae_decoder=vae.load_vaedecoder(f"{UI_Configuration().models_dir}\{model_drop}") 153 | return 154 | 155 | def change_model(model_drop): 156 | from Engine.pipelines_engines import txt2img_pipe 157 | pipe=txt2img_pipe().txt2img_pipe 158 | modelpath=f"{UI_Configuration().models_dir}\{model_drop}" 159 | pipe.unet=None 160 | gc.collect() 161 | pipe.unet=txt2img_pipe().reinitialize(modelpath) 162 | 163 | Running_information= running_config().Running_information 164 | Running_information.update({"model":model_drop}) 165 | 166 | return 167 | 168 | 169 | 170 | def delete_selected_index(Selected_image_index,status_out): 171 | global processed_images 172 | Selected_image_index=int(Selected_image_index) 173 | status_out=eval(status_out) 174 | processed_images.pop(Selected_image_index) 175 | status_out.pop(Selected_image_index) 176 | return processed_images,status_out 177 | 178 | 179 | def get_select_index(image_out,status_out, evt:gr.SelectData): 180 | status_out=eval(status_out) 181 | return evt.index,status_out[evt.index] 182 | 183 | def gallery_view(images,dict_statuses): 184 | return images[0] 185 | 186 | 187 | 188 | def get_schedulers_list(): 189 | sched_config = pipelines_engines.SchedulersConfig() 190 | sched_list =sched_config.available_schedulers 191 | return sched_list 192 | 193 | def select_scheduler(sched_name,model_path): 194 | return pipelines_engines.SchedulersConfig().scheduler(sched_name,model_path) 195 | 196 | def gen_next_prompt(prompt_t0,initial=False): 197 | global next_prompt 198 | Running_information= running_config().Running_information 199 | style=Running_information["Style"] 200 | 201 | style_pre ="" 202 | style_post="" 203 | if style: 204 | styles=style.split("|") 205 | style_pre =styles[0] 206 | style_post=styles[1] 207 | 208 | if (initial): 209 | next_prompt=None 210 | prompt=prompt_t0 211 | else: 212 | if (next_prompt != None): 213 | prompt=next_prompt 214 | else: 215 | prompt=wildcards_process(prompt_t0) 216 | 217 | next_prompt=wildcards_process(prompt_t0) 218 | prompt = style_pre+" " +prompt+" " +style_post 219 | #print(f"Prompt:{prompt}") 220 | return prompt,next_prompt 221 | 222 | def apply_prompt(prompt): 223 | global next_prompt 224 | next_prompt=prompt 225 | 226 | def wildcards_process(prompt): 227 | from Scripts import wildcards 228 | wildcard=wildcards.WildcardsScript() 229 | new_prompt,discarded=wildcard.process(prompt) 230 | return new_prompt 231 | 232 | def show_next_prompt(): 233 | global next_prompt 234 | return next_prompt 235 | 236 | def generate_click( 237 | model_drop,prompt_t0,neg_prompt_t0,sch_t0, 238 | iter_t0,batch_t0,steps_t0,guid_t0,height_t0, 239 | width_t0,eta_t0,seed_t0,fmt_t0,multiplier, 240 | strengh,name_of_latent,latent_formula,offset_t0): 241 | 242 | from Engine.pipelines_engines import txt2img_pipe 243 | 244 | Running_information= running_config().Running_information 245 | Running_information.update({"Running":True}) 246 | Running_information.update({"Latent_Name":name_of_latent}) 247 | Running_information.update({"Latent_Formula":latent_formula}) 248 | Running_information.update({"offset":offset_t0}) 249 | 250 | 251 | if Running_information["Load_Latents"]: 252 | Running_information.update({"Latent_Name":name_of_latent}) 253 | 254 | if (Running_information["model"] != model_drop or Running_information["tab"] != "txt2img"): 255 | UI_common.clean_memory_click() 256 | Running_information.update({"model":model_drop}) 257 | Running_information.update({"tab":"txt2img"}) 258 | 259 | model_path=UI_Configuration().models_dir+"\\"+model_drop 260 | pipe=txt2img_pipe().initialize(model_path,sch_t0) 261 | txt2img_pipe().create_seeds(seed_t0,iter_t0,False) 262 | images= [] 263 | information=[] 264 | counter=1 265 | img_index=Engine_common.get_next_save_index(output_path=UI_Configuration().output_path) 266 | for seed in txt2img_pipe().seeds: 267 | if running_config().Running_information["cancelled"]: 268 | break 269 | prompt,discard=gen_next_prompt(prompt_t0) 270 | print(f"Iteration:{counter}/{iter_t0}") 271 | counter+=1 272 | batch_images,info = txt2img_pipe().run_inference( 273 | prompt, 274 | neg_prompt_t0, 275 | height_t0, 276 | width_t0, 277 | steps_t0, 278 | guid_t0, 279 | eta_t0, 280 | batch_t0, 281 | seed,multiplier,strengh) 282 | images.extend(batch_images) 283 | info=dict(info) 284 | info['Sched:']=sch_t0 285 | info['Multiplier:']=multiplier 286 | information.append(info) 287 | style= running_config().Running_information["Style"] 288 | Engine_common.save_image(batch_images,info,img_index,UI_Configuration().output_path,style) 289 | #Engine_common.save_image(batch_images,info,img_index,UI_Configuration().output_path) 290 | img_index+=1 291 | 292 | running_config().Running_information.update({"cancelled":False}) 293 | global processed_images 294 | processed_images=images 295 | gen_next_prompt("",True) 296 | Running_information.update({"Running":False}) 297 | return images,information 298 | #return separadas,information 299 | 300 | -------------------------------------------------------------------------------- /Engine/Pipelines/txt2img_hires.py: -------------------------------------------------------------------------------- 1 | #from sched import scheduler 2 | from Engine.General_parameters import Engine_Configuration 3 | from Engine.pipelines_engines import Vae_and_Text_Encoders 4 | from Engine.pipelines_engines import SchedulersConfig 5 | from own_pipes.pipeline_onnx_stable_diffusion_hires_txt2img import OnnxStableDiffusionHiResPipeline 6 | 7 | import gc 8 | import numpy as np 9 | 10 | """from diffusers.utils import randn_tensor""" 11 | #from diffusers.pipelines.stable_diffusion.pipeline_onnx_stable_diffusion_hires_txt2img import OnnxStableDiffusionHiResPipeline 12 | #from pipes.pipeline_onnx_stable_diffusion_hires_txt2img import OnnxStableDiffusionHiResPipeline 13 | 14 | 15 | 16 | class Borg10: 17 | _shared_state = {} 18 | def __init__(self): 19 | self.__dict__ = self._shared_state 20 | 21 | class txt2img_hires_pipe(Borg10): 22 | hires_pipe = None 23 | model = None 24 | seeds = [] 25 | latents_list = [] 26 | 27 | def __init__(self): 28 | Borg10.__init__(self) 29 | def __str__(self): return json.dumps(self.__dict__) 30 | 31 | def initialize(self,model_path,sched_name): 32 | import onnxruntime as ort 33 | sess_options = ort.SessionOptions() 34 | sess_options.log_severity_level=3 35 | sess_options.enable_cpu_mem_arena=False 36 | sess_options.enable_mem_reuse= True 37 | sess_options.enable_mem_pattern = True 38 | #sess_options.execution_mode = ort.ExecutionMode.ORT_PARALLEL 39 | 40 | if self.hires_pipe == None: 41 | #from Engine.General_parameters import Engine_Configuration as en_config 42 | if Vae_and_Text_Encoders().text_encoder == None: 43 | Vae_and_Text_Encoders().load_textencoder(model_path) 44 | if Vae_and_Text_Encoders().vae_decoder == None: 45 | Vae_and_Text_Encoders().load_vaedecoder(model_path) 46 | if Vae_and_Text_Encoders().vae_encoder == None: 47 | Vae_and_Text_Encoders().load_vaeencoder(model_path) 48 | 49 | if " " in Engine_Configuration().MAINPipe_provider: 50 | provider =eval(Engine_Configuration().MAINPipe_provider) 51 | else: 52 | provider =Engine_Configuration().MAINPipe_provider 53 | 54 | self.hires_pipe = OnnxStableDiffusionHiResPipeline.from_pretrained( 55 | model_path, 56 | provider=provider, 57 | scheduler=SchedulersConfig().scheduler(sched_name,model_path), 58 | text_encoder=Vae_and_Text_Encoders().text_encoder, 59 | vae_decoder=Vae_and_Text_Encoders().vae_decoder, 60 | vae_encoder=Vae_and_Text_Encoders().vae_encoder, 61 | sess_options=sess_options 62 | ) 63 | else: 64 | self.hires_pipe.scheduler=SchedulersConfig().scheduler(sched_name,model_path) 65 | 66 | 67 | import functools 68 | from Engine import lpw_pipe 69 | self.hires_pipe._encode_prompt = functools.partial(lpw_pipe._encode_prompt, self.hires_pipe) 70 | 71 | 72 | return self.hires_pipe 73 | 74 | def create_seeds(self,seed=None,iter=1,same_seeds=False): 75 | self.seeds=self.seed_generator(seed,iter) 76 | if same_seeds: 77 | for seed in self.seeds: 78 | seed = self.seeds[0] 79 | 80 | def unload_from_memory(self): 81 | self.hires_pipe= None 82 | self.model = None 83 | self.latents_list = None 84 | gc.collect() 85 | 86 | def seed_generator(self,seed,iteration_count): 87 | import numpy as np 88 | # generate seeds for iterations 89 | if seed == "" or seed == None: 90 | rng = np.random.default_rng() 91 | seed = rng.integers(np.iinfo(np.uint32).max) 92 | else: 93 | try: 94 | seed = int(seed) & np.iinfo(np.uint32).max 95 | except ValueError: 96 | seed = hash(seed) & np.iinfo(np.uint32).max 97 | 98 | # use given seed for the first iteration 99 | seeds = np.array([seed], dtype=np.uint32) 100 | 101 | if iteration_count > 1: 102 | seed_seq = np.random.SeedSequence(seed) 103 | seeds = np.concatenate((seeds, seed_seq.generate_state(iteration_count - 1))) 104 | 105 | return seeds 106 | 107 | def run_inference(self,prompt,neg_prompt,hires_passes,height,width,hires_height,hires_width,steps,hires_steps,guid,eta,batch,seed): 108 | import numpy as np 109 | from Engine.General_parameters import running_config 110 | 111 | rng = np.random.RandomState(seed) 112 | prompt.strip("\n") 113 | neg_prompt.strip("\n") 114 | multiplier=1 115 | strengh=0.8 116 | if running_config().Running_information["Load_Latents"]: 117 | loaded_latent=self.get_initial_latent(steps,multiplier,rng,strengh) 118 | else: 119 | loaded_latent=None 120 | lowres_image,hires_image = self.hires_pipe( 121 | prompt=prompt, 122 | negative_prompt=neg_prompt, 123 | height=height, 124 | width=width, 125 | hires_height=hires_height, 126 | hires_width=hires_width, 127 | num_inference_steps=steps, 128 | num_hires_steps=hires_steps, 129 | guidance_scale=guid, 130 | eta=eta, 131 | num_images_per_prompt=batch, 132 | prompt_embeds = None, 133 | negative_prompt_embeds = None, 134 | latents=loaded_latent, 135 | hires_steps=hires_passes, 136 | #callback= self.__callback, 137 | #callback_steps = running_config().Running_information["Callback_Steps"], 138 | #generator=rng).images 139 | generator=rng) 140 | 141 | dictio={'prompt':prompt,'neg_prompt':neg_prompt,'height':height,'width':width,'steps':steps,'guid':guid,'eta':eta,'batch':batch,'seed':seed} 142 | from Engine.General_parameters import running_config 143 | 144 | return lowres_image,hires_image,dictio 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | def get_ordered_latents(self): 153 | from Engine.General_parameters import running_config 154 | import numpy as np 155 | name=running_config().Running_information["Latent_Name"] 156 | name1= name.split(',') 157 | lista=[0]*len(name1) 158 | for pair in name1: 159 | tupla= pair.split(':') 160 | lista[int(tupla[0])-1]=tupla[1] 161 | #print("Ordered numpys"+str(lista)) 162 | return lista 163 | 164 | def sum_latents(self,latent_list,formula,generator,resultant_latents,iter=0): 165 | #print("Processing formula:"+str(formula)) 166 | subformula_latents= None 167 | while ("(" in formula) or (")" in formula): 168 | #print("Subformula exists") 169 | subformula_startmarks=list([pos for pos, char in enumerate(formula) if char == '(']) 170 | subformula_endmarks=list([pos for pos, char in enumerate(formula) if char == ')']) 171 | 172 | if (len(subformula_endmarks) != len(subformula_startmarks)): 173 | raise Exception("Sorry, Error in formula, check it") 174 | 175 | contador=0 176 | while (len(subformula_startmarks)>contador) and (subformula_startmarks[contador] < subformula_endmarks[0]): 177 | contador+=1 178 | if contador==0: raise Exception("Sorry, Error in formula, check it") 179 | 180 | subformula= formula[(subformula_startmarks[contador-1]+1):subformula_endmarks[0]] 181 | #print(f"subformula:{iter},{subformula}") 182 | previous= formula[0:subformula_startmarks[contador-1]] 183 | posterior=formula[subformula_endmarks[0]+1:] 184 | formula= f"{previous}|{iter}|{posterior}" 185 | iter+=1 186 | subformula_latents = self.sum_latents(latent_list,subformula,generator,resultant_latents,iter) 187 | resultant_latents.append(subformula_latents) 188 | 189 | 190 | # Here we got a plain formula 191 | #print("No subformulas") 192 | result = self.process_simple_formula(latent_list,formula,generator,resultant_latents) 193 | return result 194 | 195 | def process_simple_formula(self,latent_list,formula,generator,resultant_latents): 196 | position=-1 197 | #print("Simple_formula process") 198 | for pos, char in enumerate(formula): 199 | if char in "WwHh": 200 | position=pos 201 | break 202 | if position ==-1 and len(formula)>0: #No operators, single item 203 | result=self.load_latent_file(latent_list,formula,generator,resultant_latents) 204 | else: 205 | previous=formula[0:position] 206 | operator=formula[position] 207 | rest=formula[position+1:] 208 | #print("previous:"+previous) 209 | #print("operator:"+operator) 210 | #print("rest:"+rest) 211 | 212 | result=self.load_latent_file(latent_list,previous,generator,resultant_latents) 213 | result2 = self.process_simple_formula(latent_list,rest,generator,resultant_latents) 214 | 215 | if (operator=='w'): 216 | result = self._sum_latents(result,result2,True) #left & right 217 | elif (operator=='h'): 218 | result = self._sum_latents(result,result2,False) #Up & Down 219 | 220 | return result 221 | 222 | def load_latent_file(self,latent_list,data,generator,resultant_latents): 223 | result = "" 224 | if "|" in data: 225 | lista=data.split("|") 226 | index=int(lista[1]) 227 | result = resultant_latents[index] 228 | #result = "SP:"+resultant_latents[index] 229 | else: 230 | index=int(data) 231 | name=latent_list[int(index)-1] 232 | if "noise" not in name: 233 | print(f"Loading latent(idx:name):{index}:{name}") 234 | result=np.load(f"./latents/{name}") 235 | else: 236 | noise_size=name.split("noise-")[1].split("x") 237 | print(f"Creating noise block of W/H:{noise_size}") 238 | noise = (0.0)*(generator.random((1,4,int(int(noise_size[1])/8),int(int(noise_size[0])/8))).astype(np.float32)) 239 | #noise = (generator.random((1,4,int(int(noise_size[1])/8),int(int(noise_size[0])/8))).astype(np.float32)) 240 | result = noise 241 | 242 | return result 243 | 244 | def _sum_latents(self,latent1,latent2,direction): #direction True=horizontal sum(width), False=vertical sum(height) 245 | latent_sum= None 246 | side="" 247 | try: 248 | if direction: 249 | side="Height" 250 | latent_sum = np.concatenate((latent1,latent2),axis=3) #left & right 251 | else: 252 | side="Width" 253 | latent_sum = np.concatenate((latent1,latent2),axis=2) #Up & Down 254 | except: 255 | size1=f"Latent1={(latent1.shape[3]*8)}x{(latent1.shape[2]*8)}" 256 | size2=f"Latent2={(latent2.shape[3]*8)}x{(latent2.shape[2]*8)}" 257 | raise Exception(f"Cannot sum the latents(Width x Height):{size1} and {size2} its {side} must be equal") 258 | return latent_sum 259 | 260 | def get_initial_latent(self, steps,multiplier,generator,strengh): 261 | debug = False 262 | from Engine.General_parameters import running_config 263 | latent_list=self.get_ordered_latents() 264 | formula=running_config().Running_information["Latent_Formula"] 265 | formula=formula.replace(' ', '') 266 | formula=formula.lower() 267 | #formulafinal,loaded_latent=self.sum_latents(latent_list,formula,generator,[]) 268 | #print("Formula final"+formulafinal) 269 | loaded_latent=self.sum_latents(latent_list,formula,generator,[]) 270 | 271 | print("Resultant Latent Shape "+"H:"+str(loaded_latent.shape[2]*8)+"x W:"+str(loaded_latent.shape[3]*8)) 272 | """ 273 | self.txt2img_pipe.scheduler = SchedulersConfig().reset_scheduler() 274 | if multiplier < 1: 275 | print("Multiplier applied (Use 1 as value, to do not apply)") 276 | loaded_latent= multiplier * loaded_latent 277 | 278 | noise = (0.3825 * generator.random(loaded_latent.shape)).astype(loaded_latent.dtype) #works a lot better for EulerA&DDIM than other schedulers , why? 279 | #noise = (0.1825 * generator.random(loaded_latent.shape) + 0.3).astype(loaded_latent.dtype) #works a lot better for EulerA&DDIM than other schedulers , why? 280 | #noise = (generator.random(loaded_latent.shape)).astype(loaded_latent.dtype) 281 | 282 | offset = self.txt2img_pipe.scheduler.config.get("steps_offset", 0) 283 | if True: 284 | offset= running_config().Running_information["offset"] 285 | print(f"Offset:{offset}") 286 | #init_timestep = int(steps * strengh) + offset #Con 0.ocho funciona, con 9 un poco peor?, probar 287 | init_timestep = int(steps * strengh) - offset #Con 0.ocho funciona, con 9 un poco peor?, probar, aqui tenia puesto offset a 0 288 | print(f"init_timestep, {init_timestep}") 289 | init_timestep = min(init_timestep, steps) 290 | print(f"init_timestep, {init_timestep}") 291 | timesteps = self.txt2img_pipe.scheduler.timesteps.numpy()[-init_timestep] 292 | #timesteps = self.txt2img_pipe.scheduler.timesteps.numpy()[-offset] 293 | print(f"timesteps, {timesteps}") 294 | #timesteps = np.array([timesteps] * batch_size * num_images_per_prompt) 295 | 296 | 297 | import torch 298 | init_latents = self.txt2img_pipe.scheduler.add_noise( 299 | torch.from_numpy(loaded_latent), (torch.from_numpy(noise)).type(torch.LongTensor), (torch.from_numpy(np.array([timesteps])).type(torch.LongTensor)) 300 | ) 301 | init_latents = init_latents.numpy() 302 | 303 | return init_latents""" 304 | return loaded_latent 305 | 306 | def savelatents_todisk(self,path="./latents",seed=0,save_steps=True,contador=1000,callback_steps=2): 307 | import numpy as np 308 | if self.latents_list: 309 | latent_to_save= self.latents_list.pop() 310 | if save_steps: 311 | self.savelatents_todisk(path=path,seed=seed,save_steps=save_steps,contador=contador-1,callback_steps=callback_steps) 312 | np.save(f"{path}/Seed-{seed}_latent_Step-{contador*callback_steps}.npy", latent_to_save) 313 | return 314 | 315 | --------------------------------------------------------------------------------