diff --git a/modules/config.py b/modules/config.py index 55e197d..bbe327d 100644 --- a/modules/config.py +++ b/modules/config.py @@ -6,7 +6,7 @@ import gradio as gr CURRENT_DIR = os.getcwd() -CONFIG_PATH = os.getenv('SD_WEBUI_CONFIG_PATH' ,'config.json') +CONFIG_PATH = os.getenv('SD_WEBUI_CONFIG_PATH', 'config.json') PROMPTS_PATH = os.getenv('SD_WEBUI_PROMPTS_PATH', 'prompts.json') @@ -37,13 +37,14 @@ def set_defaults(in_ckpt, in_ckpt_vae, in_unet, in_unet_vae, in_clip_g, - in_clip_l, in_t5xxl, in_type, in_sampling, in_steps, in_scheduler, - in_width, in_height, in_predict, in_flash_attn, - in_diffusion_conv_direct, in_vae_conv_direct, in_ckpt_dir_txt, - in_unet_dir_txt, in_vae_dir_txt, in_clip_dir_txt, - in_emb_dir_txt, in_lora_dir_txt, in_taesd_dir_txt, - in_phtmkr_dir_txt, in_upscl_dir_txt, in_cnnet_dir_txt, - in_txt2img_dir_txt, in_img2img_dir_txt): + in_clip_l, in_clip_vision_h, in_t5xxl, in_umt5_xxl, in_type, + in_sampling, in_steps, in_scheduler, in_width, in_height, + in_predict, in_flash_attn, in_diffusion_conv_direct, + in_vae_conv_direct, in_ckpt_dir_txt, in_unet_dir_txt, + in_vae_dir_txt, in_clip_dir_txt, in_emb_dir_txt, + in_lora_dir_txt, in_taesd_dir_txt, in_phtmkr_dir_txt, + in_upscl_dir_txt, in_cnnet_dir_txt, in_txt2img_dir_txt, + in_img2img_dir_txt): """Sets new defaults""" # Directory defaults dir_defaults = { @@ -88,8 +89,12 @@ def set_defaults(in_ckpt, in_ckpt_vae, in_unet, in_unet_vae, in_clip_g, config_data['def_clip_g'] = in_clip_g if in_clip_l: config_data['def_clip_l'] = in_clip_l + if in_clip_vision_h: + config_data['def_clip_vision_h'] = in_clip_vision_h if in_t5xxl: config_data['def_t5xxl'] = in_t5xxl + if in_umt5_xxl: + config_data['def_umt5_xxl'] = in_umt5_xxl with open(CONFIG_PATH, 'w', encoding='utf-8') as json_file_w: json.dump(config_data, json_file_w, indent=4) @@ -106,7 +111,9 @@ def rst_def(): config_data.pop('def_ckpt_vae', None) config_data.pop('def_unet_vae', None) config_data.pop('def_clip_l', None) + config_data.pop('def_clip_vision_h', None) config_data.pop('def_t5xxl', None) + config_data.pop('def_umt5_xxl', None) with open(CONFIG_PATH, 'w', encoding='utf-8') as json_file_w: json.dump(config_data, json_file_w, indent=4) @@ -229,10 +236,18 @@ def load_prompts(prompt): def_clip_l = config_data['def_clip_l'] else: def_clip_l = None +if 'def_clip_vision_h' in config_data: + def_clip_vision_h = config_data['def_clip_vision_h'] +else: + def_clip_vision_h = None if 'def_t5xxl' in config_data: def_t5xxl = config_data['def_t5xxl'] else: def_t5xxl = None +if 'def_umt5_xxl' in config_data: + def_umt5_xxl = config_data['def_umt5_xxl'] +else: + def_umt5_xxl = None def_type = config_data['def_type'] def_sampling = config_data['def_sampling'] def_steps = config_data['def_steps'] diff --git a/modules/gallery.py b/modules/gallery.py index 4d2bb0e..f616336 100644 --- a/modules/gallery.py +++ b/modules/gallery.py @@ -13,6 +13,7 @@ class GalleryManager: """Controls the gallery block""" + def __init__(self, txt2img_gallery, img2img_gallery): self.page_num = 1 self.ctrl = 0 @@ -36,7 +37,9 @@ def reload_gallery(self, ctrl_inp=None, fpage_num=1, subctrl=0): if ctrl_inp is not None: self.ctrl = int(ctrl_inp) img_dir = self._get_img_dir() - # Use a generator to find image files, avoiding the creation of a full list + # Use a generator to find image files, + # avoiding the creation of a full list + def image_files_gen(directory): for file in os.listdir(directory): if file.endswith(('.jpg', '.png')): @@ -67,7 +70,7 @@ def goto_gallery(self, fpage_num=1): total_imgs = len([file for file in files if file.endswith(('.png', '.jpg'))]) total_pages = (total_imgs + 15) // 16 - if fpage_num == None or fpage_num<1: + if fpage_num is None or fpage_num < 1: fpage_num = 1 self.page_num = min(fpage_num, total_pages) return self.reload_gallery(self.ctrl, self.page_num, subctrl=0) @@ -137,11 +140,13 @@ def img_info(self, sel_img: gr.SelectData): else: self.img_index = (self.page_num * 16) - 16 + self.sel_img img_dir = self._get_img_dir() - # Use a generator to find and sort image files on demand + # Use a generator to find and sort image files on demand + def image_file_gen(directory): for file in os.listdir(directory): file_path = os.path.join(directory, file) - if os.path.isfile(file_path) and file.lower().endswith(('.png', '.jpg')): + if (os.path.isfile(file_path) and + file.lower().endswith(('.png', '.jpg'))): yield file_path # Sort files only when necessary and only the files we need @@ -169,7 +174,10 @@ def image_file_gen(directory): width = w height = h exif = self.extract_exif_from_jpg(self.img_path) - return pprompt, nprompt, width, height, steps, sampler, cfg, seed, exif, self.img_path + return ( + pprompt, nprompt, width, height, steps, sampler, cfg, seed, + exif, self.img_path + ) if self.img_path.endswith('.png'): with open(self.img_path, 'rb') as file: if file.read(8) != b'\x89PNG\r\n\x1a\n': @@ -177,7 +185,10 @@ def image_file_gen(directory): w, h = im.size width = w height = h - return pprompt, nprompt, width, height, steps, sampler, cfg, seed, self.img_path, exif + return ( + pprompt, nprompt, width, height, steps, sampler, + cfg, seed, self.img_path, exif + ) while True: length_chunk = file.read(4) if not length_chunk: @@ -185,7 +196,9 @@ def image_file_gen(directory): w, h = im.size width = w height = h - return pprompt, nprompt, width, height, steps, sampler, cfg, seed, self.img_path, exif + return ( + pprompt, nprompt, width, height, steps, sampler, + cfg, seed, self.img_path, exif) length = int.from_bytes(length_chunk, byteorder='big') chunk_type = file.read(4).decode('utf-8') png_block = file.read(length) @@ -196,16 +209,38 @@ def image_file_gen(directory): # Main parsing method exif = f"PNG: tEXt\nPositive prompt: {png_exif}" - ppattern = r'Positive prompt:\s*(?:"(?P(?:\\.|[^"\\])*)"|(?P.*?))\s*(?=\s*(?:Steps:|Negative prompt:))' - npattern = r'Negative prompt:\s*(?:"(?P(?:\\.|[^"\\])*)"|(?P.*?))\s*(?=\s*Steps:)' + ppattern = ( + r'Positive prompt:\s*' + r'(?:"(?P' + r'(?:\\.|[^"\\])*' + r')"' + r'|' + r'(?P.*?))' + r'\s*' + r'(?=\s*(?:Steps:|Negative prompt:))' + ) + npattern = ( + r'Negative prompt:\s*' + r'(?:"(?P' + r'(?:\\.|[^"\\])*' + r')"' + r'|' + r'(?P.*?))' + r'\s*' + r'(?=\s*Steps:)' + ) pmatch = re.search(ppattern, exif) nmatch = re.search(npattern, exif) if pmatch: - pprompt = pmatch.group("quoted_pprompt") or pmatch.group("unquoted_pprompt") or "" + pprompt = (pmatch.group("quoted_pprompt") or + pmatch.group("unquoted_pprompt") or + "") if nmatch: - nprompt = nmatch.group("quoted_nprompt") or nmatch.group("unquoted_nprompt") or "" + nprompt = (nmatch.group("quoted_nprompt") or + nmatch.group("unquoted_nprompt") or + "") # Fallback parsing method (ComfyUI format) if not pprompt and '{"text":' in png_exif: @@ -218,19 +253,27 @@ def image_file_gen(directory): if len(matches) > 1: nprompt = matches[1] - steps_match_json = re.search(r'"steps":\s*(\d+)', exif) + steps_match_json = re.search( + r'"steps":\s*(\d+)', exif + ) if steps_match_json: steps = int(steps_match_json.group(1)) - sampler_match_json = re.search(r'"sampler_name":\s*"([^"]+)"', exif) + sampler_match_json = re.search( + r'"sampler_name":\s*"([^"]+)"', exif + ) if sampler_match_json: sampler = sampler_match_json.group(1) - cfg_match_json = re.search(r'"cfg":\s*(\d+)', exif) + cfg_match_json = re.search( + r'"cfg":\s*(\d+)', exif + ) if cfg_match_json: cfg = float(int(cfg_match_json.group(1))) - seed_match_json = re.search(r'"seed":\s*(\d+)', exif) + seed_match_json = re.search( + r'"seed":\s*(\d+)', exif + ) if seed_match_json: seed = int(seed_match_json.group(1)) @@ -241,15 +284,21 @@ def image_file_gen(directory): if not steps: steps_pattern = r'Steps:\s*(\d+)(?!.*Steps:)' - steps_match = re.search(steps_pattern, exif, re.DOTALL) + steps_match = re.search( + steps_pattern, exif, re.DOTALL + ) if steps_match: steps = int(steps_match.group(1)) else: steps = None if not sampler: - sampler_pattern = r'Sampler:\s*([^\s,]+)(?!.*Sampler:)' - sampler_match = re.search(sampler_pattern, exif, re.DOTALL) + sampler_pattern = ( + r'Sampler:\s*([^\s,]+)(?!.*Sampler:)' + ) + sampler_match = re.search( + sampler_pattern, exif, re.DOTALL + ) if sampler_match: sampler = sampler_match.group(1) else: @@ -257,7 +306,9 @@ def image_file_gen(directory): if not cfg: cfg_pattern = r'CFG scale:\s*(\d+)(?!.*CFG scale:)' - cfg_match = re.search(cfg_pattern, exif, re.DOTALL) + cfg_match = re.search( + cfg_pattern, exif, re.DOTALL + ) if cfg_match: cfg = float(cfg_match.group(1)) else: @@ -265,23 +316,38 @@ def image_file_gen(directory): if not seed: seed_pattern = r'Seed:\s*(\d+)(?!.*Seed:)' - seed_match = re.search(seed_pattern, exif, re.DOTALL) + seed_match = re.search( + seed_pattern, exif, re.DOTALL + ) if seed_match: seed = int(seed_match.group(1)) else: seed = None - return pprompt, nprompt, width, height, steps, sampler, cfg, seed, self.img_path, exif + return ( + pprompt, nprompt, width, height, steps, sampler, + cfg, seed, self.img_path, exif + ) return None def delete_img(self): """Deletes a selected image""" try: - if not hasattr(self, 'img_path') or not os.path.exists(self.img_path): - print("Deletion failed: No valid image selected or file does not exist.") - imgs, page_num, gallery_update = self.reload_gallery(self.ctrl, self.page_num, subctrl=0) - (pprompt, nprompt, width, height, steps, sampler, cfg, seed, img_path, exif) = ("", "", None, None, None, "", None, None, "", "") - return (imgs, page_num, gallery_update, pprompt, nprompt, width, height, steps, sampler, cfg, seed, img_path, exif) + if (not hasattr(self, 'img_path') + or not os.path.exists(self.img_path)): + print("Deletion failed: No valid image selected " + "or file does not exist.") + imgs, page_num, gallery_update = self.reload_gallery( + self.ctrl, self.page_num, subctrl=0 + ) + (pprompt, nprompt, width, height, steps, + sampler, cfg, seed, img_path, exif) = ( + "", "", None, None, None, "", None, None, "", "" + ) + return ( + imgs, page_num, gallery_update, pprompt, nprompt, width, + height, steps, sampler, cfg, seed, img_path, exif + ) index_of_deleted_img = self.img_index @@ -289,13 +355,19 @@ def delete_img(self): print(f"Deleted {self.img_path}") img_dir = self._get_img_dir() file_paths = sorted( - [os.path.join(img_dir, f) for f in os.listdir(img_dir) if f.lower().endswith(('.png', '.jpg'))], + [ + os.path.join(img_dir, f) + for f in os.listdir(img_dir) + if f.lower().endswith(('.png', '.jpg')) + ], key=os.path.getctime ) total_imgs = len(file_paths) if total_imgs == 0: - self.page_num, self.sel_img, self.img_index, self.img_path = 1, None, 0, "" + self.page_num, self.sel_img, self.img_index, self.img_path = ( + 1, None, 0, "" + ) return ([], 1, gr.Gallery(value=None, selected_index=None), "", "", None, None, None, "", None, None, "", "") @@ -307,23 +379,34 @@ def delete_img(self): self.page_num = (new_selected_index // 16) + 1 self.sel_img = new_selected_index % 16 - imgs, _, _ = self.reload_gallery(self.ctrl, self.page_num, subctrl=0) + imgs, _, _ = self.reload_gallery( + self.ctrl, self.page_num, subctrl=0 + ) img_info_tuple = self.img_info(self.sel_img) if img_info_tuple is None: - raise ValueError("Failed to retrieve information for the new image.") - - pprompt, nprompt, width, height, steps, sampler, cfg, seed, img_path, exif = img_info_tuple + raise ValueError( + "Failed to retrieve information for the new image." + ) + + ( + pprompt, nprompt, width, height, steps, + sampler, cfg, seed, img_path, exif + ) = img_info_tuple gallery_update = gr.Gallery(selected_index=self.sel_img) return ( imgs, self.page_num, gallery_update, - pprompt, nprompt, width, height, steps, sampler, cfg, seed, img_path, exif + pprompt, nprompt, width, height, steps, + sampler, cfg, seed, img_path, exif ) except Exception as e: print(f"An error occurred in delete_img: {e}") - return ([], 1, gr.Gallery(value=None), "", "", None, None, None, "", None, None, "", "") + return ( + [], 1, gr.Gallery(value=None), "", "", None, + None, None, "", None, None, "", "" + ) def get_next_img(subctrl): diff --git a/modules/sdcpp.py b/modules/sdcpp.py index d8f0593..c3560e4 100644 --- a/modules/sdcpp.py +++ b/modules/sdcpp.py @@ -30,7 +30,8 @@ def command_generator( mode: e.g. 'img_gen' prompt: positive prompt nprompt: negative prompt - additional_args: list of fixed arguments (e.g. sampling method, steps, width/height, etc.) + additional_args: list of fixed arguments (e.g. sampling method, + steps, width/height, etc.) options: dict of key-value pairs (only added if value is not None) flags: dict of boolean flags (only added if True) output_path: primary output file (including .png) @@ -71,7 +72,9 @@ def command_generator( outputs = [output_path] else: base = output_path[:-4] # remove the ".png" - outputs = [output_path] + [f"{base}_{i}.png" for i in range(2, batch_count + 1)] + outputs = [output_path] + [ + f"{base}_{i}.png" for i in range(2, batch_count + 1) + ] return command, fcommand, outputs @@ -179,7 +182,13 @@ def txt2img( print(f"\n\n{fcommand}\n\n") - yield fcommand, gr.update(visible=True, value=0), gr.update(visible=True, value="Initializing..."), gr.update(value=""), None + yield ( + fcommand, + gr.update(visible=True, value=0), + gr.update(visible=True, value="Initializing..."), + gr.update(value=""), + None + ) for update in subprocess_manager.run_subprocess(command): if "final_stats" in update: @@ -190,11 +199,26 @@ def txt2img( d_time = stats.get('decoding_time', 'N/A') t_time = stats.get('total_time', 'N/A') speed = stats.get('last_speed', 'N/A') - final_stats_str = f"Tensor Load: {l_time} | Sampling: {s_time} | Decode: {d_time} | Total: {t_time} | Last Speed: {speed}" + final_stats_str = ( + f"Tensor Load: {l_time} | Sampling: {s_time} | " + f"Decode: {d_time} | Total: {t_time} | Last Speed: {speed}" + ) else: - yield fcommand, gr.update(value=update["percent"]), update["status"], gr.update(value=""), None - - yield fcommand, gr.update(visible=False, value=100), gr.update(visible=False, value=""), gr.update(value=final_stats_str), outputs + yield ( + fcommand, + gr.update(value=update["percent"]), + update["status"], + gr.update(value=""), + None + ) + + yield ( + fcommand, + gr.update(visible=False, value=100), + gr.update(visible=False, value=""), + gr.update(value=final_stats_str), + outputs + ) def img2img( @@ -302,7 +326,13 @@ def img2img( print(f"\n\n{fcommand}\n\n") - yield fcommand, gr.update(visible=True, value=0), gr.update(visible=True, value="Initializing..."), gr.update(value=""), None + yield ( + fcommand, + gr.update(visible=True, value=0), + gr.update(visible=True, value="Initializing..."), + gr.update(value=""), + None + ) for update in subprocess_manager.run_subprocess(command): if "final_stats" in update: @@ -313,11 +343,179 @@ def img2img( d_time = stats.get('decoding_time', 'N/A') t_time = stats.get('total_time', 'N/A') speed = stats.get('last_speed', 'N/A') - final_stats_str = f"Tensor Load: {l_time} | Sampling: {s_time} | Decode: {d_time} | Total: {t_time} | Last Speed: {speed}" + final_stats_str = ( + f"Tensor Load: {l_time} | Sampling: {s_time} | " + f"Decode: {d_time} | Total: {t_time} | Last Speed: {speed}" + ) else: - yield fcommand, gr.update(value=update["percent"]), update["status"], gr.update(value=""), None + yield ( + fcommand, + gr.update(value=update["percent"]), + update["status"], + gr.update(value=""), + None + ) + + yield ( + fcommand, + gr.update(visible=False, value=100), + gr.update(visible=False, value=""), + gr.update(value=final_stats_str), + outputs + ) + + +def any2video( + in_unet_model=None, in_unet_vae=None, in_clip_vision_h=None, + in_umt5_xxl=None, in_high_noise_model=None, in_model_type="Default", + in_taesd=None, in_phtmkr=None, in_phtmkr_in=None, in_phtmkr_nrml=False, + in_img_inp=None, in_first_frame_inp=None, in_last_frame_inp=None, + in_upscl=None, in_upscl_rep=1, in_cnnet=None, in_control_img=None, + in_control_strength=1.0, in_ppromt="", in_nprompt="", + in_sampling="default", in_steps=50, in_scheduler="default", + in_width=512, in_height=512, in_batch_count=1, + in_cfg=7.0, in_frames=1, in_fps=24, in_flow_shift_toggle=False, + in_flow_shift=3.0, in_seed=42, in_clip_skip=-1, in_threads=0, + in_offload_to_cpu=False, in_vae_tiling=False, in_vae_cpu=False, + in_clip_cpu=False, in_cnnet_cpu=False, in_canny=False, + in_rng="default", in_predict="Default", in_output=None, + in_color=False, in_flash_attn=False, in_diffusion_conv_direct=False, + in_vae_conv_direct=False, + in_verbose=False +): + """Text to image command creator""" + funet_model = get_path(unet_dir, in_unet_model) + funet_vae = get_path(vae_dir, in_unet_vae) + fclip_vision_h = get_path(clip_dir, in_clip_vision_h) + fumt5_xxl = get_path(clip_dir, in_umt5_xxl) + fhigh_noise_model = get_path(unet_dir, in_high_noise_model) + ftaesd = get_path(taesd_dir, in_taesd) + fphtmkr = get_path(phtmkr_dir, in_phtmkr) + fupscl = get_path(upscl_dir, in_upscl) + fcnnet = get_path(cnnet_dir, in_cnnet) + foutput = (os.path.join(txt2img_dir, f'{in_output}.png') + if in_output + else os.path.join(txt2img_dir, get_next_img(subctrl=0))) + + # Add image generation options + additional_args = [ + '--sampling-method', str(in_sampling), + '--steps', str(in_steps), + '--scheduler', str(in_scheduler), + '-W', str(in_width), + '-H', str(in_height), + '-b', str(in_batch_count), + '--cfg-scale', str(in_cfg), + '--video-frames', str(in_frames), + '--fps', str(in_fps), + '-s', str(in_seed), + '--clip-skip', str(in_clip_skip), + '--embd-dir', emb_dir, + '--lora-model-dir', lora_dir, + '-t', str(in_threads), + '--rng', str(in_rng), + '-o', foutput + ] + + # Optional parameters in dictionaries + options = { + # Model-related options + '--diffusion-model': funet_model, + '--vae': funet_vae, + '--clip_vision': fclip_vision_h, + '--t5xxl': fumt5_xxl, + '--high-noise-diffusion-model': fhigh_noise_model, + '--taesd': ftaesd, + '--stacked-id-embd-dir': fphtmkr, + '--input-id-images-dir': str(in_phtmkr_in) if fphtmkr else None, + '--init-img': ( + str(in_img_inp or in_first_frame_inp) + if (in_img_inp or in_first_frame_inp) + else None + ), + '--end-img': ( + str(in_last_frame_inp) + if in_last_frame_inp is not None + else None + ), + '--upscale-model': fupscl, + '--upscale-repeats': str(in_upscl_rep) if fupscl else None, + '--type': in_model_type if in_model_type != "Default" else None, + '--flow-shift': str(in_flow_shift) if in_flow_shift_toggle else None, + # Control options + '--control-net': fcnnet, + '--control-image': in_control_img if fcnnet else None, + '--control-strength': str(in_control_strength) if fcnnet else None, + # Prediction mode + '--prediction': in_predict if in_predict != "Default" else None + } + + # Boolean flags + flags = { + '--offload-to-cpu': in_offload_to_cpu, + '--vae-tiling': in_vae_tiling, + '--vae-on-cpu': in_vae_cpu, + '--clip-on-cpu': in_clip_cpu, + '--control-net-cpu': in_cnnet_cpu, + '--canny': in_canny, + '--normalize-input': in_phtmkr_nrml, + '--color': in_color, + '--diffusion-fa': in_flash_attn, + '--diffusion-conv-direct': in_diffusion_conv_direct, + '--vae-conv-direct': in_vae_conv_direct, + '-v': in_verbose + } + + command, fcommand, outputs = command_generator( + mode="vid_gen", + prompt=in_ppromt, + nprompt=in_nprompt, + additional_args=additional_args, + options=options, + flags=flags, + output_path=foutput, + batch_count=in_batch_count + ) + + print(f"\n\n{fcommand}\n\n") - yield fcommand, gr.update(visible=False, value=100), gr.update(visible=False, value=""), gr.update(value=final_stats_str), outputs + yield ( + fcommand, + gr.update(visible=True, value=0), + gr.update(visible=True, value="Initializing..."), + gr.update(value=""), + None + ) + + for update in subprocess_manager.run_subprocess(command): + if "final_stats" in update: + stats = update["final_stats"] + # Format the final string + l_time = stats.get('tensor_load_time', 'N/A') + s_time = stats.get('sampling_time', 'N/A') + d_time = stats.get('decoding_time', 'N/A') + t_time = stats.get('total_time', 'N/A') + speed = stats.get('last_speed', 'N/A') + final_stats_str = ( + f"Tensor Load: {l_time} | Sampling: {s_time} | " + f"Decode: {d_time} | Total: {t_time} | Last Speed: {speed}" + ) + else: + yield ( + fcommand, + gr.update(value=update["percent"]), + update["status"], + gr.update(value=""), + None + ) + + yield ( + fcommand, + gr.update(visible=False, value=100), + gr.update(visible=False, value=""), + gr.update(value=final_stats_str), + outputs + ) def convert( diff --git a/modules/ui.py b/modules/ui.py index 6c6da83..9c78ec2 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -8,9 +8,9 @@ get_prompts, ckpt_dir, vae_dir, unet_dir, clip_dir, emb_dir, lora_dir, taesd_dir, phtmkr_dir, upscl_dir, cnnet_dir, txt2img_dir, img2img_dir, def_ckpt, def_ckpt_vae, def_unet, def_unet_vae, - def_clip_g, def_clip_l, def_t5xxl, def_sampling, def_steps, - def_scheduler, def_width, def_height, def_predict, def_flash_attn, - def_diffusion_conv_direct, def_vae_conv_direct + def_clip_g, def_clip_l, def_clip_vision_h, def_t5xxl, def_umt5_xxl, + def_sampling, def_steps, def_scheduler, def_width, def_height, + def_predict, def_flash_attn, def_diffusion_conv_direct, def_vae_conv_direct ) from modules.loader import ( get_models, reload_models @@ -32,10 +32,10 @@ SWITCH_V_SYMBOL = '\u2195' -def create_model_sel_ui(): - """Create the model selection UI""" +def create_img_model_sel_ui(): + """Create the image model selection UI""" # Dictionary to hold UI components - model_components = {} + img_model_components = {} ckpt_dir_txt = gr.Textbox(value=ckpt_dir, visible=False) vae_dir_txt = gr.Textbox(value=vae_dir, visible=False) @@ -43,11 +43,11 @@ def create_model_sel_ui(): clip_dir_txt = gr.Textbox(value=clip_dir, visible=False) # Model & VAE Selection - with gr.Tab("Checkpoint") as model_components['ckpt_tab']: + with gr.Tab("Checkpoint") as img_model_components['ckpt_tab']: with gr.Row(): with gr.Column(): with gr.Row(): - model_components['ckpt_model'] = gr.Dropdown( + img_model_components['ckpt_model'] = gr.Dropdown( label="Checkpoint Model", choices=get_models(ckpt_dir), scale=7, @@ -55,23 +55,23 @@ def create_model_sel_ui(): interactive=True ) with gr.Row(): - model_components['reload_ckpt_btn'] = gr.Button( + img_model_components['reload_ckpt_btn'] = gr.Button( value=RELOAD_SYMBOL, scale=1 ) - model_components['reload_ckpt_btn'].click( + img_model_components['reload_ckpt_btn'].click( reload_models, inputs=[ckpt_dir_txt], - outputs=[model_components['ckpt_model']] + outputs=[img_model_components['ckpt_model']] ) - model_components['clear_ckpt_model'] = gr.ClearButton( - model_components['ckpt_model'], + img_model_components['clear_ckpt_model'] = gr.ClearButton( + img_model_components['ckpt_model'], scale=1 ) with gr.Column(): with gr.Row(): - model_components['ckpt_vae'] = gr.Dropdown( + img_model_components['ckpt_vae'] = gr.Dropdown( label="Checkpoint VAE", choices=get_models(vae_dir), scale=7, @@ -79,26 +79,26 @@ def create_model_sel_ui(): interactive=True ) with gr.Row(): - model_components['reload_vae_btn'] = gr.Button( + img_model_components['reload_vae_btn'] = gr.Button( value=RELOAD_SYMBOL, scale=1 ) - model_components['reload_vae_btn'].click( + img_model_components['reload_vae_btn'].click( reload_models, inputs=[vae_dir_txt], - outputs=[model_components['ckpt_vae']] + outputs=[img_model_components['ckpt_vae']] ) - model_components['clear_vae'] = gr.ClearButton( - model_components['ckpt_vae'], + img_model_components['clear_vae'] = gr.ClearButton( + img_model_components['ckpt_vae'], scale=1 ) - with gr.Tab("UNET") as model_components['unet_tab']: + with gr.Tab("UNET") as img_model_components['unet_tab']: with gr.Row(): with gr.Column(): with gr.Row(): - model_components['unet_model'] = gr.Dropdown( + img_model_components['unet_model'] = gr.Dropdown( label="UNET Model", choices=get_models(unet_dir), scale=7, @@ -106,23 +106,23 @@ def create_model_sel_ui(): interactive=True ) with gr.Row(): - model_components['reload_unet_btn'] = gr.Button( + img_model_components['reload_unet_btn'] = gr.Button( value=RELOAD_SYMBOL, scale=1 ) - model_components['reload_unet_btn'].click( + img_model_components['reload_unet_btn'].click( reload_models, inputs=[unet_dir_txt], - outputs=[model_components['unet_model']] + outputs=[img_model_components['unet_model']] ) - model_components['clear_unet_model'] = gr.ClearButton( - model_components['unet_model'], + img_model_components['clear_unet_model'] = gr.ClearButton( + img_model_components['unet_model'], scale=1 ) with gr.Column(): with gr.Row(): - model_components['unet_vae'] = gr.Dropdown( + img_model_components['unet_vae'] = gr.Dropdown( label="UNET VAE", choices=get_models(vae_dir), scale=7, @@ -130,25 +130,25 @@ def create_model_sel_ui(): interactive=True ) with gr.Row(): - model_components['reload_unet_vae_btn'] = gr.Button( + img_model_components['reload_unet_vae_btn'] = gr.Button( value=RELOAD_SYMBOL, scale=1 ) - model_components['reload_unet_vae_btn'].click( + img_model_components['reload_unet_vae_btn'].click( reload_models, inputs=[vae_dir_txt], - outputs=[model_components['unet_vae']] + outputs=[img_model_components['unet_vae']] ) - model_components['clear_unet_vae'] = gr.ClearButton( - model_components['unet_vae'], + img_model_components['clear_unet_vae'] = gr.ClearButton( + img_model_components['unet_vae'], scale=1 ) with gr.Row(): with gr.Column(): with gr.Row(): - model_components['clip_g'] = gr.Dropdown( + img_model_components['clip_g'] = gr.Dropdown( label="clip_g", choices=get_models(clip_dir), scale=7, @@ -156,23 +156,23 @@ def create_model_sel_ui(): interactive=True ) with gr.Row(): - model_components['reload_clip_g_btn'] = gr.Button( + img_model_components['reload_clip_g_btn'] = gr.Button( value=RELOAD_SYMBOL, scale=1 ) - model_components['reload_clip_g_btn'].click( + img_model_components['reload_clip_g_btn'].click( reload_models, inputs=[clip_dir_txt], - outputs=[model_components['clip_g']] + outputs=[img_model_components['clip_g']] ) - model_components['clear_clip_g'] = gr.ClearButton( - model_components['clip_g'], + img_model_components['clear_clip_g'] = gr.ClearButton( + img_model_components['clip_g'], scale=1 ) with gr.Column(): with gr.Row(): - model_components['clip_l'] = gr.Dropdown( + img_model_components['clip_l'] = gr.Dropdown( label="clip_l", choices=get_models(clip_dir), scale=7, @@ -180,46 +180,187 @@ def create_model_sel_ui(): interactive=True ) with gr.Row(): - model_components['reload_clip_l_btn'] = gr.Button( + img_model_components['reload_clip_l_btn'] = gr.Button( value=RELOAD_SYMBOL, scale=1 ) - model_components['reload_clip_l_btn'].click( + img_model_components['reload_clip_l_btn'].click( reload_models, inputs=[clip_dir_txt], - outputs=[model_components['clip_l']] + outputs=[img_model_components['clip_l']] ) - model_components['clear_clip_l'] = gr.ClearButton( - model_components['clip_l'], + img_model_components['clear_clip_l'] = gr.ClearButton( + img_model_components['clip_l'], scale=1 ) with gr.Column(): with gr.Row(): - model_components['t5xxl'] = gr.Dropdown( + img_model_components['t5xxl'] = gr.Dropdown( label="t5xxl", choices=get_models(clip_dir), scale=7, value=def_t5xxl, interactive=True ) with gr.Row(): - model_components['reload_t5xxl_btn'] = gr.Button( + img_model_components['reload_t5xxl_btn'] = gr.Button( value=RELOAD_SYMBOL, scale=1 ) - model_components['reload_t5xxl_btn'].click( + img_model_components['reload_t5xxl_btn'].click( reload_models, inputs=[clip_dir_txt], - outputs=[model_components['t5xxl']] + outputs=[img_model_components['t5xxl']] ) - model_components['clear_t5xxl'] = gr.ClearButton( - model_components['t5xxl'], + img_model_components['clear_t5xxl'] = gr.ClearButton( + img_model_components['t5xxl'], scale=1 ) # Return the dictionary with all UI components - return model_components + return img_model_components + + +def create_video_model_sel_ui(): + """Create the video model selection UI""" + # Dictionary to hold UI components + video_model_components = {} + + unet_dir_txt = gr.Textbox(value=unet_dir, visible=False) + vae_dir_txt = gr.Textbox(value=vae_dir, visible=False) + clip_dir_txt = gr.Textbox(value=clip_dir, visible=False) + + with gr.Row(): + with gr.Column(): + with gr.Row(): + video_model_components['unet_model'] = gr.Dropdown( + label="UNET Model", + choices=get_models(unet_dir), + scale=7, + value=def_unet, + interactive=True + ) + with gr.Row(): + video_model_components['reload_unet_btn'] = gr.Button( + value=RELOAD_SYMBOL, + scale=1 + ) + video_model_components['reload_unet_btn'].click( + reload_models, + inputs=[unet_dir_txt], + outputs=[video_model_components['unet_model']] + ) + + video_model_components['clear_unet_model'] = gr.ClearButton( + video_model_components['unet_model'], + scale=1 + ) + with gr.Column(): + with gr.Row(): + video_model_components['unet_vae'] = gr.Dropdown( + label="UNET VAE", + choices=get_models(vae_dir), + scale=7, + value=def_unet_vae, + interactive=True + ) + with gr.Row(): + video_model_components['reload_unet_vae_btn'] = gr.Button( + value=RELOAD_SYMBOL, + scale=1 + ) + video_model_components['reload_unet_vae_btn'].click( + reload_models, + inputs=[vae_dir_txt], + outputs=[video_model_components['unet_vae']] + ) + + video_model_components['clear_unet_vae'] = gr.ClearButton( + video_model_components['unet_vae'], + scale=1 + ) + + with gr.Row(): + with gr.Column(): + with gr.Row(): + video_model_components['clip_vision_h'] = gr.Dropdown( + label="clip_vision_h", + choices=get_models(clip_dir), + scale=7, + value=def_clip_vision_h, + interactive=True + ) + with gr.Row(): + video_model_components['reload_clip_vision_h_btn'] = gr.Button( + value=RELOAD_SYMBOL, + scale=1 + ) + video_model_components['reload_clip_vision_h_btn'].click( + reload_models, + inputs=[clip_dir_txt], + outputs=[video_model_components['clip_vision_h']] + ) + + video_model_components['clear_clip_vision_h'] = gr.ClearButton( + video_model_components['clip_vision_h'], + scale=1 + ) + with gr.Column(): + with gr.Row(): + video_model_components['umt5_xxl'] = gr.Dropdown( + label="umt5_xxl", + choices=get_models(clip_dir), + scale=7, + value=def_umt5_xxl, + interactive=True + ) + with gr.Row(): + video_model_components['reload_umt5_xxl_btn'] = gr.Button( + value=RELOAD_SYMBOL, + scale=1 + ) + video_model_components['reload_umt5_xxl_btn'].click( + reload_models, + inputs=[clip_dir_txt], + outputs=[video_model_components['umt5_xxl']] + ) + + video_model_components['clear_umt5_xxl'] = gr.ClearButton( + video_model_components['umt5_xxl'], + scale=1 + ) + with gr.Row(): + with gr.Accordion( + label="High Noise", open=False + ): + with gr.Row(): + video_model_components['high_noise_model'] = gr.Dropdown( + label="High Noise Diffusion model", + choices=get_models(unet_dir), + value=None, + interactive=True + ) + with gr.Row(): + video_model_components['reload_high_noise_model'] = gr.Button( + value=RELOAD_SYMBOL, + scale=1 + ) + video_model_components['reload_high_noise_model'].click( + reload_models, + inputs=[unet_dir_txt], + outputs=[video_model_components['high_noise_model']] + ) + + video_model_components['clear_high_noise_model'] = ( + gr.ClearButton( + video_model_components['high_noise_model'], + scale=1 + ) + ) + + # Return the dictionary with all UI components + return video_model_components def create_prompts_ui(): @@ -323,8 +464,10 @@ def create_settings_ui(): ) settings_components['switch_size'].click( switch_sizes, - inputs=[settings_components['height'], settings_components['width']], - outputs=[settings_components['height'], settings_components['width']] + inputs=[settings_components['height'], + settings_components['width']], + outputs=[settings_components['height'], + settings_components['width']] ) settings_components['batch_count'] = gr.Slider( label="Batch count", @@ -396,7 +539,8 @@ def create_extras_ui(): maximum=os.cpu_count(), value=0 ) - extras_components['offload_to_cpu'] = gr.Checkbox(label="Offload to CPU") + extras_components['offload_to_cpu'] = gr.Checkbox( + label="Offload to CPU") extras_components['vae_tiling'] = gr.Checkbox(label="VAE Tiling") extras_components['vae_cpu'] = gr.Checkbox(label="VAE on CPU") extras_components['clip_cpu'] = gr.Checkbox(label="CLIP on CPU") @@ -420,7 +564,8 @@ def create_extras_ui(): label="Flash Attention", value=def_flash_attn ) extras_components['diffusion_conv_direct'] = gr.Checkbox( - label="Conv2D Direct for diffusion", value=def_diffusion_conv_direct + label="Conv2D Direct for diffusion", + value=def_diffusion_conv_direct ) extras_components['vae_conv_direct'] = gr.Checkbox( label="Conv2D Direct for VAE", value=def_vae_conv_direct diff --git a/modules/ui_any2video.py b/modules/ui_any2video.py new file mode 100644 index 0000000..a566c28 --- /dev/null +++ b/modules/ui_any2video.py @@ -0,0 +1,373 @@ +"""sd.cpp-webui - Anything to Video UI""" + +import gradio as gr + +from modules.sdcpp import any2video +from modules.utility import ( + subprocess_manager, random_seed +) +from modules.config import ( + reload_prompts, save_prompts, delete_prompts, load_prompts, + emb_dir, lora_dir, taesd_dir, phtmkr_dir, upscl_dir, cnnet_dir, + def_type +) +from modules.loader import ( + get_models, reload_models +) +from modules.ui import ( + create_video_model_sel_ui, create_prompts_ui, + create_cnnet_ui, create_extras_ui, create_settings_ui, + QUANTS, RELOAD_SYMBOL, RANDOM_SYMBOL +) + + +with gr.Blocks() as any2video_block: + # Directory Textboxes + emb_dir_txt = gr.Textbox(value=emb_dir, visible=False) + lora_dir_txt = gr.Textbox(value=lora_dir, visible=False) + taesd_dir_txt = gr.Textbox(value=taesd_dir, visible=False) + phtmkr_dir_txt = gr.Textbox(value=phtmkr_dir, visible=False) + upscl_dir_txt = gr.Textbox(value=upscl_dir, visible=False) + cnnet_dir_txt = gr.Textbox(value=cnnet_dir, visible=False) + + # Title + any2video_title = gr.Markdown("# Anything to Video") + + # Model & VAE Selection + video_model_components = create_video_model_sel_ui() + + # UNET Components (Wan 2.1/2.2) + unet_model = video_model_components['unet_model'] + reload_unet_btn = video_model_components['reload_unet_btn'] + clear_unet_model = video_model_components['clear_unet_model'] + unet_vae = video_model_components['unet_vae'] + reload_unet_vae_btn = video_model_components['reload_unet_vae_btn'] + clear_unet_vae = video_model_components['clear_unet_vae'] + umt5_xxl = video_model_components['umt5_xxl'] + reload_umt5_xxl_btn = video_model_components['reload_umt5_xxl_btn'] + clear_umt5_xxl = video_model_components['clear_umt5_xxl'] + clip_vision_h = video_model_components['clip_vision_h'] + reload_clip_vision_h_btn = video_model_components['reload_clip_vision_h_btn'] + clear_clip_vision_h = video_model_components['clear_clip_vision_h'] + high_noise_model = video_model_components['high_noise_model'] + reload_high_noise_model = video_model_components['reload_high_noise_model'] + clear_high_noise_model = video_model_components['clear_high_noise_model'] + + # Model Type Selection + with gr.Row(): + model_type = gr.Dropdown( + label="Quantization", + choices=QUANTS, + value=def_type, + interactive=True + ) + + # Extra Networks Selection + with gr.Row(): + with gr.Accordion(label="Extra Networks", open=False): + with gr.Row(): + taesd_title = gr.Markdown("## TAESD") + with gr.Row(): + taesd_model = gr.Dropdown( + label="TAESD", + choices=get_models(taesd_dir), + value="", + allow_custom_value=True, + interactive=True + ) + with gr.Row(): + reload_taesd_btn = gr.Button(value=RELOAD_SYMBOL) + clear_taesd = gr.ClearButton(taesd_model) + with gr.Row(): + phtmkr_title = gr.Markdown("## PhotoMaker") + with gr.Row(): + phtmkr_model = gr.Dropdown( + label="PhotoMaker", + choices=get_models(phtmkr_dir), + value="", + allow_custom_value=True, + interactive=True + ) + with gr.Row(): + reload_phtmkr_btn = gr.Button(value=RELOAD_SYMBOL) + clear_phtmkr = gr.ClearButton(phtmkr_model) + with gr.Row(): + phtmkr_in = gr.Textbox( + label="PhotoMaker images directory", + value="", + interactive=True + ) + with gr.Row(): + clear_phtmkr_in = gr.ClearButton(phtmkr_in) + with gr.Row(): + phtmkr_nrml = gr.Checkbox( + label="Normalize PhotoMaker input", value=False + ) + + # Prompts + prompts_components = create_prompts_ui() + + saved_prompts = prompts_components['saved_prompts'] + load_prompt_btn = prompts_components['load_prompt_btn'] + reload_prompts_btn = prompts_components['reload_prompts_btn'] + save_prompt_btn = prompts_components['save_prompt_btn'] + del_prompt_btn = prompts_components['del_prompt_btn'] + pprompt_any2video = prompts_components['pprompt'] + nprompt_any2video = prompts_components['nprompt'] + + # Settings + with gr.Row(): + with gr.Column(scale=1): + + settings_components = create_settings_ui() + + sampling_any2video = settings_components['sampling'] + steps_any2video = settings_components['steps'] + scheduler = settings_components['scheduler'] + width_any2video = settings_components['width'] + height_any2video = settings_components['height'] + switch_size = settings_components['switch_size'] + batch_count = settings_components['batch_count'] + cfg_any2video = settings_components['cfg'] + + with gr.Row(): + frames = gr.Number( + label="Video Frames", + minimum=1, + value=24, + scale=1, + interactive=True, + step=1 + ) + fps = gr.Number( + label="FPS", + minimum=1, + value=1, + scale=1, + interactive=True, + step=1 + ) + + with gr.Accordion( + label="Flow Shift", open=False + ): + flow_shift_toggle = gr.Checkbox( + label="Enable Flow Shift", value=False + ) + flow_shift = gr.Number( + label="Flow Shift", + minimum=1.0, + maximum=12.0, + value=3.0, + interactive=True, + step=0.1 + ) + + with gr.Row(): + seed_any2video = gr.Number( + label="Seed", + minimum=-1, + maximum=10**16, + value=-1, + scale=5 + ) + random_seed_btn = gr.Button( + value=RANDOM_SYMBOL, scale=1 + ) + clip_skip = gr.Slider( + label="CLIP skip", + minimum=-1, + maximum=12, + value=-1, + step=1 + ) + + # Upscale + with gr.Accordion( + label="Upscale", open=False + ): + upscl = gr.Dropdown( + label="Upscaler", + choices=get_models(upscl_dir), + value="", + allow_custom_value=True, + interactive=True + ) + reload_upscl_btn = gr.Button(value=RELOAD_SYMBOL) + clear_upscl = gr.ClearButton(upscl) + upscl_rep = gr.Slider( + label="Upscaler repeats", + minimum=1, + maximum=5, + value=1, + step=0.1 + ) + + # ControlNet + cnnet_components = create_cnnet_ui() + + cnnet = cnnet_components['cnnet'] + reload_cnnet_btn = cnnet_components['reload_cnnet_btn'] + clear_cnnet = cnnet_components['clear_cnnet'] + control_img = cnnet_components['control_img'] + control_strength = cnnet_components['control_strength'] + cnnet_cpu = cnnet_components['cnnet_cpu'] + canny = cnnet_components['canny'] + + # Extra Settings + extras_components = create_extras_ui() + + threads = extras_components['threads'] + offload_to_cpu = extras_components['offload_to_cpu'] + vae_tiling = extras_components['vae_tiling'] + vae_cpu = extras_components['vae_cpu'] + clip_cpu = extras_components['clip_cpu'] + rng = extras_components['rng'] + predict = extras_components['predict'] + output = extras_components['output'] + color = extras_components['color'] + flash_attn = extras_components['flash_attn'] + diffusion_conv_direct = extras_components['diffusion_conv_direct'] + vae_conv_direct = extras_components['vae_conv_direct'] + verbose = extras_components['verbose'] + + # Output + with gr.Column(scale=1): + with gr.Row(): + with gr.Accordion( + label="Image to Video", open=False + ): + img_inp = gr.Image( + sources="upload", type="filepath" + ) + with gr.Row(): + with gr.Accordion( + label="First-Last Frame Video", open=False + ): + with gr.Row(): + first_frame_inp = gr.Image( + sources="upload", type="filepath" + ) + with gr.Row(): + last_frame_inp = gr.Image( + sources="upload", type="filepath" + ) + + with gr.Row(): + gen_btn = gr.Button( + value="Generate", size="lg", + variant="primary" + ) + kill_btn = gr.Button( + value="Stop", size="lg", + variant="stop" + ) + with gr.Row(): + progress_slider = gr.Slider( + minimum=0, + maximum=100, + value=0, + interactive=False, + visible=False, + label="Progress" + ) + with gr.Row(): + progress_textbox = gr.Textbox( + label="Status:", + visible=False, + interactive=False + ) + with gr.Row(): + video_final = gr.Gallery( + label="Generated videos", + show_label=False, + columns=[3], + rows=[1], + object_fit="contain", + height="auto" + ) + with gr.Row(): + stats = gr.Textbox( + label="Statistics:", + show_label=True, + value="", + interactive=False + ) + with gr.Row(): + command = gr.Textbox( + label="stable-diffusion.cpp command:", + show_label=True, + value="", + interactive=False, + show_copy_button=True, + ) + + # Generate + gen_btn.click( + any2video, + inputs=[unet_model, unet_vae, umt5_xxl, clip_vision_h, + high_noise_model, model_type, taesd_model, phtmkr_model, + phtmkr_in, phtmkr_nrml, img_inp, first_frame_inp, + last_frame_inp, upscl, upscl_rep, cnnet, control_img, + control_strength, pprompt_any2video, nprompt_any2video, + sampling_any2video, steps_any2video, scheduler, + width_any2video, height_any2video, batch_count, cfg_any2video, + frames, fps, flow_shift_toggle, flow_shift, seed_any2video, + clip_skip, threads, offload_to_cpu, vae_tiling, vae_cpu, + clip_cpu, cnnet_cpu, canny, rng, predict, output, color, + flash_attn, diffusion_conv_direct, vae_conv_direct, verbose], + outputs=[command, progress_slider, progress_textbox, stats, + video_final] + ) + kill_btn.click( + subprocess_manager.kill_subprocess, + inputs=[], + outputs=[] + ) + + # Interactive Bindings + reload_taesd_btn.click( + reload_models, + inputs=[taesd_dir_txt], + outputs=[taesd_model] + ) + reload_phtmkr_btn.click( + reload_models, + inputs=[phtmkr_dir_txt], + outputs=[phtmkr_model] + ) + reload_upscl_btn.click( + reload_models, + inputs=[upscl_dir_txt], + outputs=[upscl] + ) + reload_cnnet_btn.click( + reload_models, + inputs=[cnnet_dir_txt], + outputs=[cnnet] + ) + save_prompt_btn.click( + save_prompts, + inputs=[saved_prompts, pprompt_any2video, + nprompt_any2video], + outputs=[] + ) + del_prompt_btn.click( + delete_prompts, + inputs=[saved_prompts], + outputs=[] + ) + reload_prompts_btn.click( + reload_prompts, + inputs=[], + outputs=[saved_prompts] + ) + load_prompt_btn.click( + load_prompts, + inputs=[saved_prompts], + outputs=[pprompt_any2video, nprompt_any2video] + ) + random_seed_btn.click( + random_seed, + inputs=[], + outputs=[seed_any2video]) diff --git a/modules/ui_convert.py b/modules/ui_convert.py index db092c9..934e347 100644 --- a/modules/ui_convert.py +++ b/modules/ui_convert.py @@ -6,7 +6,7 @@ from modules.utility import subprocess_manager from modules.config import ( ckpt_dir, vae_dir, unet_dir, clip_dir, emb_dir, - lora_dir, taesd_dir, upscl_dir, cnnet_dir + lora_dir, taesd_dir, upscl_dir, cnnet_dir ) from modules.loader import ( get_models, reload_models, model_choice diff --git a/modules/ui_gallery.py b/modules/ui_gallery.py index fa1371f..1009ebd 100644 --- a/modules/ui_gallery.py +++ b/modules/ui_gallery.py @@ -160,6 +160,8 @@ cpy_2_txt2img_btn = gr.Button(value="Copy to txt2img") # Copy to img2img cpy_2_img2img_btn = gr.Button(value="Copy to img2img") + # Copy to any2video + cpy_2_any2video_btn = gr.Button(value="Copy to any2video") # Delete image Button del_img = gr.Button( value="Delete", variant="stop") @@ -168,7 +170,9 @@ gallery.select( gallery_manager.img_info, inputs=[], - outputs=[pprompt_info, nprompt_info, width_info, height_info, steps_info, sampler_info, cfg_info, seed_info, path_info, img_info_txt] + outputs=[pprompt_info, nprompt_info, width_info, height_info, + steps_info, sampler_info, cfg_info, seed_info, path_info, + img_info_txt] ) txt2img_btn.click( gallery_manager.reload_gallery, @@ -214,5 +218,7 @@ gallery_manager.delete_img, inputs=[], outputs=[gallery, page_num_select, gallery, - pprompt_info, nprompt_info, width_info, height_info, steps_info, sampler_info, cfg_info, seed_info, path_info, img_info_txt] + pprompt_info, nprompt_info, width_info, height_info, + steps_info, sampler_info, cfg_info, seed_info, path_info, + img_info_txt] ) diff --git a/modules/ui_img2img.py b/modules/ui_img2img.py index 2e4d3c0..f9effe8 100644 --- a/modules/ui_img2img.py +++ b/modules/ui_img2img.py @@ -15,9 +15,9 @@ get_models, reload_models ) from modules.ui import ( - create_model_sel_ui, create_prompts_ui, + create_img_model_sel_ui, create_prompts_ui, create_cnnet_ui, create_extras_ui, create_settings_ui, - QUANTS, RELOAD_SYMBOL, RANDOM_SYMBOL, SWITCH_V_SYMBOL + QUANTS, RELOAD_SYMBOL, RANDOM_SYMBOL ) @@ -34,34 +34,34 @@ img2img_title = gr.Markdown("# Image to Image") # Model & VAE Selection - model_components = create_model_sel_ui() + img_model_components = create_img_model_sel_ui() # Checkpoint Tab Components - ckpt_tab = model_components['ckpt_tab'] - ckpt_model = model_components['ckpt_model'] - reload_ckpt_btn = model_components['reload_ckpt_btn'] - clear_ckpt_model = model_components['clear_ckpt_model'] - ckpt_vae = model_components['ckpt_vae'] - reload_vae_btn = model_components['reload_vae_btn'] - clear_vae = model_components['clear_vae'] + ckpt_tab = img_model_components['ckpt_tab'] + ckpt_model = img_model_components['ckpt_model'] + reload_ckpt_btn = img_model_components['reload_ckpt_btn'] + clear_ckpt_model = img_model_components['clear_ckpt_model'] + ckpt_vae = img_model_components['ckpt_vae'] + reload_vae_btn = img_model_components['reload_vae_btn'] + clear_vae = img_model_components['clear_vae'] # UNET Tab Components - unet_tab = model_components['unet_tab'] - unet_model = model_components['unet_model'] - reload_unet_btn = model_components['reload_unet_btn'] - clear_unet_model = model_components['clear_unet_model'] - unet_vae = model_components['unet_vae'] - reload_unet_vae_btn = model_components['reload_unet_vae_btn'] - clear_unet_vae = model_components['clear_unet_vae'] - clip_g = model_components['clip_g'] - reload_clip_g_btn = model_components['reload_clip_g_btn'] - clear_clip_g = model_components['clear_clip_g'] - clip_l = model_components['clip_l'] - reload_clip_l_btn = model_components['reload_clip_l_btn'] - clear_clip_l = model_components['clear_clip_l'] - t5xxl = model_components['t5xxl'] - reload_t5xxl_btn = model_components['reload_t5xxl_btn'] - clear_t5xxl = model_components['clear_t5xxl'] + unet_tab = img_model_components['unet_tab'] + unet_model = img_model_components['unet_model'] + reload_unet_btn = img_model_components['reload_unet_btn'] + clear_unet_model = img_model_components['clear_unet_model'] + unet_vae = img_model_components['unet_vae'] + reload_unet_vae_btn = img_model_components['reload_unet_vae_btn'] + clear_unet_vae = img_model_components['clear_unet_vae'] + clip_g = img_model_components['clip_g'] + reload_clip_g_btn = img_model_components['reload_clip_g_btn'] + clear_clip_g = img_model_components['clear_clip_g'] + clip_l = img_model_components['clip_l'] + reload_clip_l_btn = img_model_components['reload_clip_l_btn'] + clear_clip_l = img_model_components['clear_clip_l'] + t5xxl = img_model_components['t5xxl'] + reload_t5xxl_btn = img_model_components['reload_t5xxl_btn'] + clear_t5xxl = img_model_components['clear_t5xxl'] # Model Type Selection with gr.Row(): diff --git a/modules/ui_options.py b/modules/ui_options.py index 62ac4fa..55b36e4 100644 --- a/modules/ui_options.py +++ b/modules/ui_options.py @@ -5,9 +5,10 @@ from modules.config import ( set_defaults, rst_def, ckpt_dir, vae_dir, unet_dir, clip_dir, def_ckpt, def_ckpt_vae, def_unet, def_unet_vae, def_clip_g, - def_clip_l, def_t5xxl, def_type, def_sampling, def_steps, - def_scheduler, def_width, def_height, def_predict, - def_flash_attn, def_diffusion_conv_direct, def_vae_conv_direct + def_clip_l, def_clip_vision_h, def_t5xxl, def_umt5_xxl, + def_type, def_sampling, def_steps, def_scheduler, def_width, + def_height, def_predict, def_flash_attn, + def_diffusion_conv_direct, def_vae_conv_direct ) from modules.loader import ( get_models @@ -101,7 +102,7 @@ value=RELOAD_SYMBOL, scale=1 ) clear_clip_g = gr.ClearButton( - clip_g, scale=1 + clip_g, scale=1 ) with gr.Column(): with gr.Row(): @@ -119,6 +120,23 @@ clear_clip_l = gr.ClearButton( clip_l, scale=1 ) + with gr.Column(): + with gr.Row(): + clip_vision_h = gr.Dropdown( + label="clip_vision_h", + choices=get_models(clip_dir), + scale=7, + value=def_clip_vision_h, + interactive=True + ) + with gr.Row(): + reload_clip_vision_h_btn = gr.Button( + value=RELOAD_SYMBOL, scale=1 + ) + clear_clip_vision_h = gr.ClearButton( + clip_vision_h, scale=1 + ) + with gr.Row(): with gr.Column(): with gr.Row(): t5xxl = gr.Dropdown( @@ -135,6 +153,22 @@ clear_t5xxl = gr.ClearButton( t5xxl, scale=1 ) + with gr.Column(): + with gr.Row(): + umt5_xxl = gr.Dropdown( + label="umt5_xxl", + choices=get_models(clip_dir), + scale=7, + value=def_umt5_xxl, + interactive=True + ) + with gr.Row(): + reload_umt5_xxl_btn = gr.Button( + value=RELOAD_SYMBOL, scale=1 + ) + clear_umt5_xxl = gr.ClearButton( + umt5_xxl, scale=1 + ) model_type = gr.Dropdown( label="Quantization", choices=QUANTS, @@ -233,11 +267,11 @@ set_btn.click( set_defaults, inputs=[ckpt_model, ckpt_vae, unet_model, unet_vae, - clip_g, clip_l, t5xxl, model_type, sampling, - steps, scheduler, width, height, predict, - flash_attn, diffusion_conv_direct, vae_conv_direct, - ckpt_dir_txt, unet_dir_txt, vae_dir_txt, - clip_dir_txt, + clip_g, clip_l, clip_vision_h, t5xxl, umt5_xxl, + model_type, sampling, steps, scheduler, width, height, + predict, flash_attn, diffusion_conv_direct, + vae_conv_direct, ckpt_dir_txt, unet_dir_txt, + vae_dir_txt, clip_dir_txt, emb_dir_txt, lora_dir_txt, taesd_dir_txt, phtmkr_dir_txt, upscl_dir_txt, cnnet_dir_txt, diff --git a/modules/ui_txt2img.py b/modules/ui_txt2img.py index e59647a..f55e976 100644 --- a/modules/ui_txt2img.py +++ b/modules/ui_txt2img.py @@ -15,9 +15,9 @@ get_models, reload_models ) from modules.ui import ( - create_model_sel_ui, create_prompts_ui, + create_img_model_sel_ui, create_prompts_ui, create_cnnet_ui, create_extras_ui, create_settings_ui, - QUANTS, RELOAD_SYMBOL, RANDOM_SYMBOL, SWITCH_V_SYMBOL, + QUANTS, RELOAD_SYMBOL, RANDOM_SYMBOL ) @@ -34,34 +34,34 @@ txt2img_title = gr.Markdown("# Text to Image") # Model & VAE Selection - model_components = create_model_sel_ui() + img_model_components = create_img_model_sel_ui() # Checkpoint Tab Components - ckpt_tab = model_components['ckpt_tab'] - ckpt_model = model_components['ckpt_model'] - reload_ckpt_btn = model_components['reload_ckpt_btn'] - clear_ckpt_model = model_components['clear_ckpt_model'] - ckpt_vae = model_components['ckpt_vae'] - reload_vae_btn = model_components['reload_vae_btn'] - clear_vae = model_components['clear_vae'] + ckpt_tab = img_model_components['ckpt_tab'] + ckpt_model = img_model_components['ckpt_model'] + reload_ckpt_btn = img_model_components['reload_ckpt_btn'] + clear_ckpt_model = img_model_components['clear_ckpt_model'] + ckpt_vae = img_model_components['ckpt_vae'] + reload_vae_btn = img_model_components['reload_vae_btn'] + clear_vae = img_model_components['clear_vae'] # UNET Tab Components (FLUX, Stable Diffusion 3/3.5) - unet_tab = model_components['unet_tab'] - unet_model = model_components['unet_model'] - reload_unet_btn = model_components['reload_unet_btn'] - clear_unet_model = model_components['clear_unet_model'] - unet_vae = model_components['unet_vae'] - reload_unet_vae_btn = model_components['reload_unet_vae_btn'] - clear_unet_vae = model_components['clear_unet_vae'] - clip_g = model_components['clip_g'] - reload_clip_g_btn = model_components['reload_clip_g_btn'] - clear_clip_g = model_components['clear_clip_g'] - clip_l = model_components['clip_l'] - reload_clip_l_btn = model_components['reload_clip_l_btn'] - clear_clip_l = model_components['clear_clip_l'] - t5xxl = model_components['t5xxl'] - reload_t5xxl_btn = model_components['reload_t5xxl_btn'] - clear_t5xxl = model_components['clear_t5xxl'] + unet_tab = img_model_components['unet_tab'] + unet_model = img_model_components['unet_model'] + reload_unet_btn = img_model_components['reload_unet_btn'] + clear_unet_model = img_model_components['clear_unet_model'] + unet_vae = img_model_components['unet_vae'] + reload_unet_vae_btn = img_model_components['reload_unet_vae_btn'] + clear_unet_vae = img_model_components['clear_unet_vae'] + clip_g = img_model_components['clip_g'] + reload_clip_g_btn = img_model_components['reload_clip_g_btn'] + clear_clip_g = img_model_components['clear_clip_g'] + clip_l = img_model_components['clip_l'] + reload_clip_l_btn = img_model_components['reload_clip_l_btn'] + clear_clip_l = img_model_components['clear_clip_l'] + t5xxl = img_model_components['t5xxl'] + reload_t5xxl_btn = img_model_components['reload_t5xxl_btn'] + clear_t5xxl = img_model_components['clear_t5xxl'] # Model Type Selection with gr.Row(): diff --git a/modules/utility.py b/modules/utility.py index 19e965b..d8e3d0a 100644 --- a/modules/utility.py +++ b/modules/utility.py @@ -48,7 +48,9 @@ def update(self, **kwargs): if hasattr(self, key): setattr(self, key, value) else: - raise AttributeError(f"{key} is not a valid attribute of ModelState.") + raise AttributeError( + f"{key} is not a valid attribute of ModelState." + ) def bak_ckpt_tab(self, ckpt_model, ckpt_vae): """Updates the state with values from the checkpoint tab.""" @@ -79,14 +81,15 @@ class SubprocessManager: def __init__(self): """Initializes the SubprocessManager with no active subprocess.""" self.process = None - # --- Patterns are now defined once as class attributes --- self.STATS_REGEX = re.compile(r"completed, taking ([\d.]+)s") self.TOTAL_TIME_REGEX = re.compile(r"completed in ([\d.]+)s") self.ETA_REGEX = re.compile(r'(\d+)/(\d+)\s*-\s*([\d.]+)(s/it|it/s)') self.SIMPLE_REGEX = re.compile(r'(\d+)/(\d+)') def _parse_final_stats(self, line, final_stats): - """Parses a line for final summary stats and updates the stats dictionary.""" + """ + Parses a line for final summary stats and updates the stats dictionary. + """ if 'loading tensors completed' in line: match = self.STATS_REGEX.search(line) if match: @@ -108,8 +111,11 @@ def _parse_progress_update(self, line, final_stats): """Parses a progress bar line and returns a dictionary for the UI.""" eta_match = self.ETA_REGEX.search(line) if eta_match: - current_step, total_steps, speed_value, speed_unit = eta_match.groups() - final_stats['last_speed'] = f"{float(speed_value):.2f} {speed_unit}" + (current_step, total_steps, + speed_value, speed_unit) = eta_match.groups() + final_stats['last_speed'] = ( + f"{float(speed_value):.2f} {speed_unit}" + ) current_step, total_steps = map(int, [current_step, total_steps]) speed_value = float(speed_value) @@ -137,7 +143,10 @@ def _parse_progress_update(self, line, final_stats): return { "percent": int(phase_fraction * 100), - "status": f"Speed: {final_stats['last_speed']} | ETA: {eta_str}" + "status": ( + f"Speed: {final_stats['last_speed']} | " + f"ETA: {eta_str}" + ) } # Fallback for progress lines without ETA info @@ -183,7 +192,10 @@ def run_subprocess(self, command): if "|" in output_line and "/" in output_line: if phase == "Sampling": - update_data = self._parse_progress_update(output_line, final_stats) + update_data = self._parse_progress_update( + output_line, + final_stats + ) if update_data: yield update_data diff --git a/sdcpp_webui.py b/sdcpp_webui.py index e24ef2c..ceecb1d 100644 --- a/sdcpp_webui.py +++ b/sdcpp_webui.py @@ -7,9 +7,25 @@ import gradio as gr -from modules.ui_txt2img import txt2img_block, pprompt_txt2img, nprompt_txt2img, width_txt2img, height_txt2img, steps_txt2img, sampling_txt2img, cfg_txt2img, seed_txt2img -from modules.ui_img2img import img2img_block, pprompt_img2img, nprompt_img2img, width_img2img, height_img2img, steps_img2img, sampling_img2img, cfg_img2img, seed_img2img, img_inp -from modules.ui_gallery import gallery_block, cpy_2_txt2img_btn, cpy_2_img2img_btn, pprompt_info, nprompt_info, width_info, height_info, steps_info, sampler_info, cfg_info, seed_info, path_info +from modules.ui_txt2img import ( + txt2img_block, pprompt_txt2img, nprompt_txt2img, width_txt2img, + height_txt2img, steps_txt2img, sampling_txt2img, cfg_txt2img, seed_txt2img +) +from modules.ui_img2img import ( + img2img_block, pprompt_img2img, nprompt_img2img, width_img2img, + height_img2img, steps_img2img, sampling_img2img, cfg_img2img, seed_img2img, + img_inp +) +from modules.ui_any2video import ( + any2video_block, pprompt_any2video, nprompt_any2video, width_any2video, + height_any2video, steps_any2video, sampling_any2video, cfg_any2video, + seed_any2video +) +from modules.ui_gallery import ( + gallery_block, cpy_2_txt2img_btn, cpy_2_img2img_btn, cpy_2_any2video_btn, + pprompt_info, nprompt_info, width_info, height_info, steps_info, + sampler_info, cfg_info, seed_info, path_info +) from modules.ui_convert import convert_block from modules.ui_options import options_block @@ -39,12 +55,34 @@ def main(): sdcpp_launch(args.listen, args.autostart, args.darkmode) -def cpy_2_txt2img(pprompt_info, nprompt_info, width_info, height_info, steps_info, sampler_info, cfg_info, seed_info): - return gr.Tabs(selected="txt2img"), pprompt_info, nprompt_info, width_info, height_info, steps_info, sampler_info, cfg_info, seed_info +def cpy_2_txt2img( + pprompt_info, nprompt_info, width_info, height_info, steps_info, + sampler_info, cfg_info, seed_info +): + return ( + gr.Tabs(selected="txt2img"), pprompt_info, nprompt_info, width_info, + height_info, steps_info, sampler_info, cfg_info, seed_info + ) + + +def cpy_2_img2img( + pprompt_info, nprompt_info, width_info, height_info, steps_info, + sampler_info, cfg_info, seed_info, path_info +): + return ( + gr.Tabs(selected="img2img"), pprompt_info, nprompt_info, width_info, + height_info, steps_info, sampler_info, cfg_info, seed_info, path_info + ) -def cpy_2_img2img(pprompt_info, nprompt_info, width_info, height_info, steps_info, sampler_info, cfg_info, seed_info, path_info): - return gr.Tabs(selected="img2img"), pprompt_info, nprompt_info, width_info, height_info, steps_info, sampler_info, cfg_info, seed_info, path_info +def cpy_2_any2video( + pprompt_info, nprompt_info, width_info, height_info, steps_info, + sampler_info, cfg_info, seed_info, path_info +): + return ( + gr.Tabs(selected="any2video"), pprompt_info, nprompt_info, width_info, + height_info, steps_info, sampler_info, cfg_info, seed_info, path_info + ) def sdcpp_launch( @@ -70,13 +108,17 @@ def sdcpp_launch( } """ if darkmode else None - - with gr.Blocks(css="footer {visibility: hidden}", title="sd.cpp-webui", theme="default", js=dark_js) as sdcpp: + with gr.Blocks( + css="footer {visibility: hidden}", title="sd.cpp-webui", + theme="default", js=dark_js + ) as sdcpp: with gr.Tabs() as tabs: with gr.TabItem("txt2img", id="txt2img"): txt2img_block.render() with gr.TabItem("img2img", id="img2img"): img2img_block.render() + with gr.TabItem("any2video", id="any2video"): + any2video_block.render() with gr.TabItem("Gallery", id="gallery"): gallery_block.render() with gr.TabItem("Checkpoint Converter", id="convert"): @@ -87,13 +129,28 @@ def sdcpp_launch( # Set up the button click event cpy_2_txt2img_btn.click( cpy_2_txt2img, - inputs=[pprompt_info, nprompt_info, width_info, height_info, steps_info, sampler_info, cfg_info, seed_info], - outputs=[tabs, pprompt_txt2img, nprompt_txt2img, width_txt2img, height_txt2img, steps_txt2img, sampling_txt2img, cfg_txt2img, seed_txt2img] + inputs=[pprompt_info, nprompt_info, width_info, height_info, + steps_info, sampler_info, cfg_info, seed_info], + outputs=[tabs, pprompt_txt2img, nprompt_txt2img, width_txt2img, + height_txt2img, steps_txt2img, sampling_txt2img, + cfg_txt2img, seed_txt2img] ) cpy_2_img2img_btn.click( cpy_2_img2img, - inputs=[pprompt_info, nprompt_info, width_info, height_info, steps_info, sampler_info, cfg_info, seed_info, path_info], - outputs=[tabs, pprompt_img2img, nprompt_img2img, width_img2img, height_img2img, steps_img2img, sampling_img2img, cfg_img2img, seed_img2img, img_inp] + inputs=[pprompt_info, nprompt_info, width_info, height_info, + steps_info, sampler_info, cfg_info, seed_info, path_info], + outputs=[tabs, pprompt_img2img, nprompt_img2img, width_img2img, + height_img2img, steps_img2img, sampling_img2img, + cfg_img2img, seed_img2img, img_inp] + ) + cpy_2_any2video_btn.click( + cpy_2_any2video, + inputs=[pprompt_info, nprompt_info, width_info, height_info, + steps_info, sampler_info, cfg_info, seed_info, path_info], + outputs=[tabs, pprompt_any2video, nprompt_any2video, + width_any2video, height_any2video, steps_any2video, + sampling_any2video, cfg_any2video, seed_any2video, + img_inp] ) # Pass the arguments to sdcpp.launch with argument unpacking