From f581d8e8dda57f07f1ce44c6bca9b2e1d8d995ae Mon Sep 17 00:00:00 2001 From: Charles Fettinger Date: Thu, 20 Apr 2023 21:51:18 -0700 Subject: [PATCH 01/44] Implement Infinite Zoom Exit Image update gitignore specify seed --- .gitignore | 1 + scripts/infinite-zoom.py | 86 ++++++++++++++++++++++++---------------- 2 files changed, 53 insertions(+), 34 deletions(-) diff --git a/.gitignore b/.gitignore index 876e20e..2638864 100644 --- a/.gitignore +++ b/.gitignore @@ -129,3 +129,4 @@ dmypy.json .pyre/ .vscode/settings.json .DS_Store +/.vs diff --git a/scripts/infinite-zoom.py b/scripts/infinite-zoom.py index 6ebe1ad..f5e9bc4 100644 --- a/scripts/infinite-zoom.py +++ b/scripts/infinite-zoom.py @@ -113,7 +113,7 @@ def do_upscaleImg(curImg, upscale_do, upscaler_name, upscale_by): return pp.image -def renderTxt2Img(prompt, negative_prompt, sampler, steps, cfg_scale, width, height): +def renderTxt2Img(prompt, negative_prompt, sampler, steps, cfg_scale, seed, width, height): processed = None p = StableDiffusionProcessingTxt2Img( sd_model=shared.sd_model, @@ -121,7 +121,7 @@ def renderTxt2Img(prompt, negative_prompt, sampler, steps, cfg_scale, width, hei outpath_grids=shared.opts.outdir_txt2img_grids, prompt=prompt, negative_prompt=negative_prompt, - # seed=-1, + seed=seed, sampler_name=sampler, n_iter=1, steps=steps, @@ -130,7 +130,8 @@ def renderTxt2Img(prompt, negative_prompt, sampler, steps, cfg_scale, width, hei height=height, ) processed = process_images(p) - return processed + newseed = p.seed + return processed, newseed def renderImg2Img( @@ -139,6 +140,7 @@ def renderImg2Img( sampler, steps, cfg_scale, + seed, width, height, init_image, @@ -157,7 +159,7 @@ def renderImg2Img( outpath_grids=shared.opts.outdir_img2img_grids, prompt=prompt, negative_prompt=negative_prompt, - # seed=-1, + seed=seed, sampler_name=sampler, n_iter=1, steps=steps, @@ -175,7 +177,8 @@ def renderImg2Img( # p.latent_mask = Image.new("RGB", (p.width, p.height), "white") processed = process_images(p) - return processed + newseed = p.seed + return processed, newseed def fix_env_Path_ffprobe(): @@ -227,6 +230,7 @@ def create_zoom( inpainting_full_res, inpainting_padding, zoom_speed, + seed, outputsizeW, outputsizeH, batchcount, @@ -257,6 +261,7 @@ def create_zoom( inpainting_full_res, inpainting_padding, zoom_speed, + seed, outputsizeW, outputsizeH, sampler, @@ -286,6 +291,7 @@ def create_zoom_single( inpainting_full_res, inpainting_padding, zoom_speed, + seed, outputsizeW, outputsizeH, sampler, @@ -319,24 +325,28 @@ def create_zoom_single( mask_image = np.array(current_image)[:, :, 3] mask_image = Image.fromarray(255 - mask_image).convert("RGB") current_image = current_image.convert("RGB") + current_seed = seed if custom_init_image: current_image = custom_init_image.resize( (width, height), resample=Image.LANCZOS ) + print("using Custom Initial Image") else: load_model_from_setting("infzoom_txt2img_model", progress, "Loading Model for txt2img: ") - processed = renderTxt2Img( + processed, newseed = renderTxt2Img( prompts[min(k for k in prompts.keys() if k >= 0)], negative_prompt, sampler, num_inference_steps, guidance_scale, + current_seed, width, height, ) current_image = processed.images[0] + current_seed = newseed mask_width = math.trunc(width / 4) # was initially 512px => 128px mask_height = math.trunc(height / 4) # was initially 512px => 128px @@ -357,7 +367,7 @@ def create_zoom_single( load_model_from_setting("infzoom_inpainting_model", progress, "Loading Model for inpainting/img2img: " ) for i in range(num_outpainting_steps): - print_out = "Outpaint step: " + str(i + 1) + " / " + str(num_outpainting_steps) + print_out = "Outpaint step: " + str(i + 1) + " / " + str(num_outpainting_steps) + " Seed: " + str(current_seed) print(print_out) if progress: progress(((i + 1) / num_outpainting_steps), desc=print_out) @@ -373,23 +383,31 @@ def create_zoom_single( # inpainting step current_image = current_image.convert("RGB") - processed = renderImg2Img( - prompts[max(k for k in prompts.keys() if k <= i)], - negative_prompt, - sampler, - num_inference_steps, - guidance_scale, - width, - height, - current_image, - mask_image, - inpainting_denoising_strength, - inpainting_mask_blur, - inpainting_fill_mode, - inpainting_full_res, - inpainting_padding, - ) - current_image = processed.images[0] + if custom_exit_image and ((i + 1) == num_outpainting_steps): + current_image = custom_exit_image.resize( + (width, height), resample=Image.LANCZOS + ) + print("using Custom Exit Image") + else: + processed, newseed = renderImg2Img( + prompts[max(k for k in prompts.keys() if k <= i)], + negative_prompt, + sampler, + num_inference_steps, + guidance_scale, + current_seed, + width, + height, + current_image, + mask_image, + inpainting_denoising_strength, + inpainting_mask_blur, + inpainting_fill_mode, + inpainting_full_res, + inpainting_padding, + ) + current_image = processed.images[0] + current_seed = newseed current_image.paste(prev_image, mask=prev_image) @@ -614,13 +632,14 @@ def on_ui_tabs(): inputs=[], outputs=[main_prompts, main_negative_prompt], ) - - main_sampler = gr.Dropdown( - label="Sampler", - choices=available_samplers, - value="Euler a", - type="value", - ) + with gr.Row(): + seed = gr.Number(label="Seed", value=-1, precision=0, interactive=True) + main_sampler = gr.Dropdown( + label="Sampler", + choices=available_samplers, + value="Euler a", + type="value", + ) with gr.Row(): main_width = gr.Slider( minimum=16, @@ -653,9 +672,7 @@ def on_ui_tabs(): ) with gr.Row(): init_image = gr.Image(type="pil", label="custom initial image") - exit_image = gr.Image( - type="pil", label="custom exit image", visible=False - ) # TODO: implement exit-image rendering + exit_image = gr.Image(type="pil", label="custom exit image") batchcount_slider = gr.Slider( minimum=1, @@ -768,6 +785,7 @@ Our best experience and trade-off is the R-ERSGAn4x upscaler. inpainting_full_res, inpainting_padding, video_zoom_speed, + seed, main_width, main_height, batchcount_slider, From f48719000f5b2592300a14ee6b9818b4f26d3c30 Mon Sep 17 00:00:00 2001 From: vahid khroasani <62482657+v8hid@users.noreply.github.com> Date: Sun, 23 Apr 2023 07:20:06 +0400 Subject: [PATCH 02/44] Refactor (#54) A little bit of cleaning up the code! --- iz_helpers/__init__.py | 4 +- iz_helpers/extra.py | 0 iz_helpers/helpers.py | 125 +++ {scripts => iz_helpers}/promptschema.json | 0 iz_helpers/run.py | 324 ++++++++ iz_helpers/sd_helpers.py | 76 ++ iz_helpers/settings.py | 95 +++ iz_helpers/static_variables.py | 39 + iz_helpers/ui.py | 270 +++++++ scripts/infinite-zoom.py | 896 +--------------------- 10 files changed, 933 insertions(+), 896 deletions(-) create mode 100644 iz_helpers/extra.py create mode 100644 iz_helpers/helpers.py rename {scripts => iz_helpers}/promptschema.json (100%) create mode 100644 iz_helpers/run.py create mode 100644 iz_helpers/sd_helpers.py create mode 100644 iz_helpers/settings.py create mode 100644 iz_helpers/static_variables.py create mode 100644 iz_helpers/ui.py diff --git a/iz_helpers/__init__.py b/iz_helpers/__init__.py index 8798cf1..076e90a 100644 --- a/iz_helpers/__init__.py +++ b/iz_helpers/__init__.py @@ -1,2 +1,2 @@ -from .image import shrink_and_paste_on_blank -from .video import write_video +from .ui import on_ui_tabs +from .settings import on_ui_settings diff --git a/iz_helpers/extra.py b/iz_helpers/extra.py new file mode 100644 index 0000000..e69de29 diff --git a/iz_helpers/helpers.py b/iz_helpers/helpers.py new file mode 100644 index 0000000..c119212 --- /dev/null +++ b/iz_helpers/helpers.py @@ -0,0 +1,125 @@ +import math +import os +import json +from jsonschema import validate +import modules.shared as shared +import modules.sd_models +import gradio as gr +from scripts import postprocessing_upscale +from .static_variables import jsonprompt_schemafile + + +def fix_env_Path_ffprobe(): + envpath = os.environ["PATH"] + ffppath = shared.opts.data.get("infzoom_ffprobepath", "") + + if ffppath and not ffppath in envpath: + path_sep = ";" if os.name == "nt" else ":" + os.environ["PATH"] = envpath + path_sep + ffppath + + +def closest_upper_divisible_by_eight(num): + if num % 8 == 0: + return num + else: + return math.ceil(num / 8) * 8 + + +def load_model_from_setting(model_field_name, progress, progress_desc): + # fix typo in Automatic1111 vs Vlad111 + if hasattr(modules.sd_models, "checkpoint_alisases"): + checkPList = modules.sd_models.checkpoint_alisases + elif hasattr(modules.sd_models, "checkpoint_aliases"): + checkPList = modules.sd_models.checkpoint_aliases + else: + raise Exception( + "This is not a compatible StableDiffusion Platform, can not access checkpoints" + ) + + model_name = shared.opts.data.get(model_field_name) + if model_name is not None and model_name != "": + checkinfo = checkPList[model_name] + + if not checkinfo: + raise NameError(model_field_name + " Does not exist in your models.") + + if progress: + progress(0, desc=progress_desc + checkinfo.name) + + modules.sd_models.load_model(checkinfo) + + +def do_upscaleImg(curImg, upscale_do, upscaler_name, upscale_by): + if not upscale_do: + return curImg + + # ensure even width and even height for ffmpeg + # if odd, switch to scale to mode + rwidth = round(curImg.width * upscale_by) + rheight = round(curImg.height * upscale_by) + + ups_mode = 2 # upscale_by + if (rwidth % 2) == 1: + ups_mode = 1 + rwidth += 1 + if (rheight % 2) == 1: + ups_mode = 1 + rheight += 1 + + if 1 == ups_mode: + print( + "Infinite Zoom: aligning output size to even width and height: " + + str(rwidth) + + " x " + + str(rheight), + end="\r", + ) + + pp = postprocessing_upscale.scripts_postprocessing.PostprocessedImage(curImg) + ups = postprocessing_upscale.ScriptPostprocessingUpscale() + ups.process( + pp, + upscale_mode=ups_mode, + upscale_by=upscale_by, + upscale_to_width=rwidth, + upscale_to_height=rheight, + upscale_crop=False, + upscaler_1_name=upscaler_name, + upscaler_2_name=None, + upscaler_2_visibility=0.0, + ) + return pp.image + + +def validatePromptJson_throws(data): + with open(jsonprompt_schemafile, "r") as s: + schema = json.load(s) + validate(instance=data, schema=schema) + + +def putPrompts(files): + try: + with open(files.name, "r") as f: + file_contents = f.read() + data = json.loads(file_contents) + validatePromptJson_throws(data) + return [ + gr.DataFrame.update(data["prompts"]), + gr.Textbox.update(data["negPrompt"]), + ] + + except Exception: + gr.Error( + "loading your prompt failed. It seems to be invalid. Your prompt table is preserved." + ) + print( + "[InfiniteZoom:] Loading your prompt failed. It seems to be invalid. Your prompt table is preserved." + ) + return [gr.DataFrame.update(), gr.Textbox.update()] + + +def clearPrompts(): + return [ + gr.DataFrame.update(value=[[0, "Infinite Zoom. Start over"]]), + gr.Textbox.update(""), + ] diff --git a/scripts/promptschema.json b/iz_helpers/promptschema.json similarity index 100% rename from scripts/promptschema.json rename to iz_helpers/promptschema.json diff --git a/iz_helpers/run.py b/iz_helpers/run.py new file mode 100644 index 0000000..c9b2d74 --- /dev/null +++ b/iz_helpers/run.py @@ -0,0 +1,324 @@ +import math, time, os +import numpy as np +from PIL import Image +from modules.ui import plaintext_to_html +import modules.shared as shared + +from .helpers import ( + fix_env_Path_ffprobe, + closest_upper_divisible_by_eight, + load_model_from_setting, + do_upscaleImg, +) +from .sd_helpers import renderImg2Img, renderTxt2Img +from .image import shrink_and_paste_on_blank +from .video import write_video + + +def create_zoom( + prompts_array, + negative_prompt, + num_outpainting_steps, + guidance_scale, + num_inference_steps, + custom_init_image, + custom_exit_image, + video_frame_rate, + video_zoom_mode, + video_start_frame_dupe_amount, + video_last_frame_dupe_amount, + inpainting_denoising_strength, + inpainting_mask_blur, + inpainting_fill_mode, + inpainting_full_res, + inpainting_padding, + zoom_speed, + seed, + outputsizeW, + outputsizeH, + batchcount, + sampler, + upscale_do, + upscaler_name, + upscale_by, + progress=None, +): + for i in range(batchcount): + print(f"Batch {i+1}/{batchcount}") + result = create_zoom_single( + prompts_array, + negative_prompt, + num_outpainting_steps, + guidance_scale, + num_inference_steps, + custom_init_image, + custom_exit_image, + video_frame_rate, + video_zoom_mode, + video_start_frame_dupe_amount, + video_last_frame_dupe_amount, + inpainting_denoising_strength, + inpainting_mask_blur, + inpainting_fill_mode, + inpainting_full_res, + inpainting_padding, + zoom_speed, + seed, + outputsizeW, + outputsizeH, + sampler, + upscale_do, + upscaler_name, + upscale_by, + progress, + ) + return result + + +def create_zoom_single( + prompts_array, + negative_prompt, + num_outpainting_steps, + guidance_scale, + num_inference_steps, + custom_init_image, + custom_exit_image, + video_frame_rate, + video_zoom_mode, + video_start_frame_dupe_amount, + video_last_frame_dupe_amount, + inpainting_denoising_strength, + inpainting_mask_blur, + inpainting_fill_mode, + inpainting_full_res, + inpainting_padding, + zoom_speed, + seed, + outputsizeW, + outputsizeH, + sampler, + upscale_do, + upscaler_name, + upscale_by, + progress=None, +): + # try: + # if gr.Progress() is not None: + # progress = gr.Progress() + # progress(0, desc="Preparing Initial Image") + # except Exception: + # pass + fix_env_Path_ffprobe() + + prompts = {} + for x in prompts_array: + try: + key = int(x[0]) + value = str(x[1]) + prompts[key] = value + except ValueError: + pass + assert len(prompts_array) > 0, "prompts is empty" + + width = closest_upper_divisible_by_eight(outputsizeW) + height = closest_upper_divisible_by_eight(outputsizeH) + + current_image = Image.new(mode="RGBA", size=(width, height)) + mask_image = np.array(current_image)[:, :, 3] + mask_image = Image.fromarray(255 - mask_image).convert("RGB") + current_image = current_image.convert("RGB") + current_seed = seed + + if custom_init_image: + current_image = custom_init_image.resize( + (width, height), resample=Image.LANCZOS + ) + print("using Custom Initial Image") + else: + load_model_from_setting( + "infzoom_txt2img_model", progress, "Loading Model for txt2img: " + ) + + processed, newseed = renderTxt2Img( + prompts[min(k for k in prompts.keys() if k >= 0)], + negative_prompt, + sampler, + num_inference_steps, + guidance_scale, + current_seed, + width, + height, + ) + current_image = processed.images[0] + current_seed = newseed + + mask_width = math.trunc(width / 4) # was initially 512px => 128px + mask_height = math.trunc(height / 4) # was initially 512px => 128px + + num_interpol_frames = round(video_frame_rate * zoom_speed) + + all_frames = [] + + if upscale_do and progress: + progress(0, desc="upscaling inital image") + + all_frames.append( + do_upscaleImg(current_image, upscale_do, upscaler_name, upscale_by) + if upscale_do + else current_image + ) + + load_model_from_setting( + "infzoom_inpainting_model", progress, "Loading Model for inpainting/img2img: " + ) + + for i in range(num_outpainting_steps): + print_out = ( + "Outpaint step: " + + str(i + 1) + + " / " + + str(num_outpainting_steps) + + " Seed: " + + str(current_seed) + ) + print(print_out) + if progress: + progress(((i + 1) / num_outpainting_steps), desc=print_out) + + prev_image_fix = current_image + prev_image = shrink_and_paste_on_blank(current_image, mask_width, mask_height) + current_image = prev_image + + # create mask (black image with white mask_width width edges) + mask_image = np.array(current_image)[:, :, 3] + mask_image = Image.fromarray(255 - mask_image).convert("RGB") + + # inpainting step + current_image = current_image.convert("RGB") + + if custom_exit_image and ((i + 1) == num_outpainting_steps): + current_image = custom_exit_image.resize( + (width, height), resample=Image.LANCZOS + ) + print("using Custom Exit Image") + else: + processed, newseed = renderImg2Img( + prompts[max(k for k in prompts.keys() if k <= i)], + negative_prompt, + sampler, + num_inference_steps, + guidance_scale, + current_seed, + width, + height, + current_image, + mask_image, + inpainting_denoising_strength, + inpainting_mask_blur, + inpainting_fill_mode, + inpainting_full_res, + inpainting_padding, + ) + current_image = processed.images[0] + current_seed = newseed + + current_image.paste(prev_image, mask=prev_image) + + # interpolation steps between 2 inpainted images (=sequential zoom and crop) + for j in range(num_interpol_frames - 1): + interpol_image = current_image + + interpol_width = round( + ( + 1 + - (1 - 2 * mask_width / width) + ** (1 - (j + 1) / num_interpol_frames) + ) + * width + / 2 + ) + + interpol_height = round( + ( + 1 + - (1 - 2 * mask_height / height) + ** (1 - (j + 1) / num_interpol_frames) + ) + * height + / 2 + ) + + interpol_image = interpol_image.crop( + ( + interpol_width, + interpol_height, + width - interpol_width, + height - interpol_height, + ) + ) + + interpol_image = interpol_image.resize((width, height)) + + # paste the higher resolution previous image in the middle to avoid drop in quality caused by zooming + interpol_width2 = round( + (1 - (width - 2 * mask_width) / (width - 2 * interpol_width)) + / 2 + * width + ) + + interpol_height2 = round( + (1 - (height - 2 * mask_height) / (height - 2 * interpol_height)) + / 2 + * height + ) + + prev_image_fix_crop = shrink_and_paste_on_blank( + prev_image_fix, interpol_width2, interpol_height2 + ) + + interpol_image.paste(prev_image_fix_crop, mask=prev_image_fix_crop) + + if upscale_do and progress: + progress(((i + 1) / num_outpainting_steps), desc="upscaling interpol") + + all_frames.append( + do_upscaleImg(interpol_image, upscale_do, upscaler_name, upscale_by) + if upscale_do + else interpol_image + ) + + if upscale_do and progress: + progress(((i + 1) / num_outpainting_steps), desc="upscaling current") + + all_frames.append( + do_upscaleImg(current_image, upscale_do, upscaler_name, upscale_by) + if upscale_do + else current_image + ) + + video_file_name = "infinite_zoom_" + str(int(time.time())) + ".mp4" + output_path = shared.opts.data.get( + "infzoom_outpath", shared.opts.data.get("outdir_img2img_samples") + ) + save_path = os.path.join( + output_path, shared.opts.data.get("infzoom_outSUBpath", "infinite-zooms") + ) + if not os.path.exists(save_path): + os.makedirs(save_path) + out = os.path.join(save_path, video_file_name) + write_video( + out, + all_frames, + video_frame_rate, + video_zoom_mode, + int(video_start_frame_dupe_amount), + int(video_last_frame_dupe_amount), + ) + + return ( + out, + processed.images, + processed.js(), + plaintext_to_html(processed.info), + plaintext_to_html(""), + ) diff --git a/iz_helpers/sd_helpers.py b/iz_helpers/sd_helpers.py new file mode 100644 index 0000000..396eb1d --- /dev/null +++ b/iz_helpers/sd_helpers.py @@ -0,0 +1,76 @@ +from modules.processing import ( + process_images, + StableDiffusionProcessingTxt2Img, + StableDiffusionProcessingImg2Img, +) +import modules.shared as shared + + +def renderTxt2Img( + prompt, negative_prompt, sampler, steps, cfg_scale, seed, width, height +): + processed = None + p = StableDiffusionProcessingTxt2Img( + sd_model=shared.sd_model, + outpath_samples=shared.opts.outdir_txt2img_samples, + outpath_grids=shared.opts.outdir_txt2img_grids, + prompt=prompt, + negative_prompt=negative_prompt, + seed=seed, + sampler_name=sampler, + n_iter=1, + steps=steps, + cfg_scale=cfg_scale, + width=width, + height=height, + ) + processed = process_images(p) + newseed = p.seed + return processed, newseed + + +def renderImg2Img( + prompt, + negative_prompt, + sampler, + steps, + cfg_scale, + seed, + width, + height, + init_image, + mask_image, + inpainting_denoising_strength, + inpainting_mask_blur, + inpainting_fill_mode, + inpainting_full_res, + inpainting_padding, +): + processed = None + + p = StableDiffusionProcessingImg2Img( + sd_model=shared.sd_model, + outpath_samples=shared.opts.outdir_img2img_samples, + outpath_grids=shared.opts.outdir_img2img_grids, + prompt=prompt, + negative_prompt=negative_prompt, + seed=seed, + sampler_name=sampler, + n_iter=1, + steps=steps, + cfg_scale=cfg_scale, + width=width, + height=height, + init_images=[init_image], + denoising_strength=inpainting_denoising_strength, + mask_blur=inpainting_mask_blur, + inpainting_fill=inpainting_fill_mode, + inpaint_full_res=inpainting_full_res, + inpaint_full_res_padding=inpainting_padding, + mask=mask_image, + ) + # p.latent_mask = Image.new("RGB", (p.width, p.height), "white") + + processed = process_images(p) + newseed = p.seed + return processed, newseed diff --git a/iz_helpers/settings.py b/iz_helpers/settings.py new file mode 100644 index 0000000..8c6af78 --- /dev/null +++ b/iz_helpers/settings.py @@ -0,0 +1,95 @@ +import modules.shared as shared +from .static_variables import default_prompt +import gradio as gr + + +def on_ui_settings(): + section = ("infinite-zoom", "Infinite Zoom") + + shared.opts.add_option( + "outputs" "infzoom_outpath", + shared.OptionInfo( + "", + "Path where to store your infinite video. Default is Outputs", + gr.Textbox, + {"interactive": True}, + section=section, + ), + ) + + shared.opts.add_option( + "infzoom_outSUBpath", + shared.OptionInfo( + "infinite-zooms", + "Which subfolder name to be created in the outpath. Default is 'infinite-zooms'", + gr.Textbox, + {"interactive": True}, + section=section, + ), + ) + + shared.opts.add_option( + "infzoom_outsizeW", + shared.OptionInfo( + 512, + "Default width of your video", + gr.Slider, + {"minimum": 16, "maximum": 2048, "step": 16}, + section=section, + ), + ) + + shared.opts.add_option( + "infzoom_outsizeH", + shared.OptionInfo( + 512, + "Default height your video", + gr.Slider, + {"minimum": 16, "maximum": 2048, "step": 16}, + section=section, + ), + ) + + shared.opts.add_option( + "infzoom_ffprobepath", + shared.OptionInfo( + "", + "Writing videos has dependency to an existing FFPROBE executable on your machine. D/L here (https://github.com/BtbN/FFmpeg-Builds/releases) your OS variant and point to your installation path", + gr.Textbox, + {"interactive": True}, + section=section, + ), + ) + + shared.opts.add_option( + "infzoom_txt2img_model", + shared.OptionInfo( + None, + "Name of your desired model to render keyframes (txt2img)", + gr.Dropdown, + lambda: {"choices": shared.list_checkpoint_tiles()}, + section=section, + ), + ) + + shared.opts.add_option( + "infzoom_inpainting_model", + shared.OptionInfo( + None, + "Name of your desired inpaint model (img2img-inpaint). Default is vanilla sd-v1-5-inpainting.ckpt ", + gr.Dropdown, + lambda: {"choices": shared.list_checkpoint_tiles()}, + section=section, + ), + ) + + shared.opts.add_option( + "infzoom_defPrompt", + shared.OptionInfo( + default_prompt, + "Default prompt-setup to start with'", + gr.Code, + {"interactive": True, "language": "json"}, + section=section, + ), + ) diff --git a/iz_helpers/static_variables.py b/iz_helpers/static_variables.py new file mode 100644 index 0000000..ac25cb1 --- /dev/null +++ b/iz_helpers/static_variables.py @@ -0,0 +1,39 @@ +import os +from modules import scripts +import modules.sd_samplers + +default_prompt = """ +{ + "prompts":{ + "headers":["outpaint steps","prompt"], + "data":[ + [0,"Huge spectacular Waterfall in a dense tropical forest,epic perspective,(vegetation overgrowth:1.3)(intricate, ornamentation:1.1),(baroque:1.1), fantasy, (realistic:1) digital painting , (magical,mystical:1.2) , (wide angle shot:1.4), (landscape composed:1.2)(medieval:1.1), divine,cinematic,(tropical forest:1.4),(river:1.3)mythology,india, volumetric lighting, Hindu ,epic, Alex Horley Wenjun Lin greg rutkowski Ruan Jia (Wayne Barlowe:1.2) "] + ] + }, + "negPrompt":"frames, borderline, text, character, duplicate, error, out of frame, watermark, low quality, ugly, deformed, blur bad-artist" +} +""" +available_samplers = [ + s.name for s in modules.sd_samplers.samplers if "UniPc" not in s.name +] + +empty_prompt = ( + '{"prompts":{"data":[],"headers":["outpaint steps","prompt"]},"negPrompt":""}' +) + +invalid_prompt = { + "prompts": { + "data": [[0, "Your prompt-json is invalid, please check Settings"]], + "headers": ["outpaint steps", "prompt"], + }, + "negPrompt": "Invalid prompt-json", +} +current_script_dir = scripts.basedir().split(os.sep)[ + -2: +] # contains install and our extension foldername +jsonprompt_schemafile = ( + current_script_dir[0] + + "/" + + current_script_dir[1] + + "/iz_helpers/promptschema.json" +) diff --git a/iz_helpers/ui.py b/iz_helpers/ui.py new file mode 100644 index 0000000..9729631 --- /dev/null +++ b/iz_helpers/ui.py @@ -0,0 +1,270 @@ +import json +import gradio as gr +from .run import create_zoom +import modules.shared as shared +from webui import wrap_gradio_gpu_call +from modules.ui import create_output_panel +from .static_variables import ( + default_prompt, + empty_prompt, + invalid_prompt, + available_samplers, +) +from .helpers import validatePromptJson_throws, putPrompts, clearPrompts + + +def on_ui_tabs(): + with gr.Blocks(analytics_enabled=False) as infinite_zoom_interface: + gr.HTML( + """ +

+ GitHub Repo + Discord server +

+ + """ + ) + with gr.Row(): + generate_btn = gr.Button(value="Generate video", variant="primary") + interrupt = gr.Button(value="Interrupt", elem_id="interrupt_training") + with gr.Row(): + with gr.Column(scale=1, variant="panel"): + with gr.Tab("Main"): + main_outpaint_steps = gr.Slider( + minimum=2, + maximum=100, + step=1, + value=8, + label="Total Outpaint Steps", + info="The more it is, the longer your videos will be", + ) + + # safe reading json prompt + pr = shared.opts.data.get("infzoom_defPrompt", default_prompt) + if not pr: + pr = empty_prompt + try: + jpr = json.loads(pr) + validatePromptJson_throws(jpr) + except Exception: + jpr = invalid_prompt + + main_prompts = gr.Dataframe( + type="array", + headers=["outpaint step", "prompt"], + datatype=["number", "str"], + row_count=1, + col_count=(2, "fixed"), + value=jpr["prompts"], + wrap=True, + ) + + main_negative_prompt = gr.Textbox( + value=jpr["negPrompt"], label="Negative Prompt" + ) + + # these button will be moved using JS unde the dataframe view as small ones + exportPrompts_button = gr.Button( + value="Export prompts", + variant="secondary", + elem_classes="sm infzoom_tab_butt", + elem_id="infzoom_exP_butt", + ) + importPrompts_button = gr.UploadButton( + label="Import prompts", + variant="secondary", + elem_classes="sm infzoom_tab_butt", + elem_id="infzoom_imP_butt", + ) + exportPrompts_button.click( + None, + _js="exportPrompts", + inputs=[main_prompts, main_negative_prompt], + outputs=None, + ) + importPrompts_button.upload( + fn=putPrompts, + outputs=[main_prompts, main_negative_prompt], + inputs=[importPrompts_button], + ) + + clearPrompts_button = gr.Button( + value="Clear prompts", + variant="secondary", + elem_classes="sm infzoom_tab_butt", + elem_id="infzoom_clP_butt", + ) + clearPrompts_button.click( + fn=clearPrompts, + inputs=[], + outputs=[main_prompts, main_negative_prompt], + ) + with gr.Row(): + seed = gr.Number( + label="Seed", value=-1, precision=0, interactive=True + ) + main_sampler = gr.Dropdown( + label="Sampler", + choices=available_samplers, + value="Euler a", + type="value", + ) + with gr.Row(): + main_width = gr.Slider( + minimum=16, + maximum=2048, + value=shared.opts.data.get("infzoom_outsizeW", 512), + step=16, + label="Output Width", + ) + main_height = gr.Slider( + minimum=16, + maximum=2048, + value=shared.opts.data.get("infzoom_outsizeH", 512), + step=16, + label="Output Height", + ) + with gr.Row(): + main_guidance_scale = gr.Slider( + minimum=0.1, + maximum=15, + step=0.1, + value=7, + label="Guidance Scale", + ) + sampling_step = gr.Slider( + minimum=1, + maximum=100, + step=1, + value=50, + label="Sampling Steps for each outpaint", + ) + with gr.Row(): + init_image = gr.Image(type="pil", label="custom initial image") + exit_image = gr.Image(type="pil", label="custom exit image") + + batchcount_slider = gr.Slider( + minimum=1, + maximum=25, + value=shared.opts.data.get("infzoom_batchcount", 1), + step=1, + label="Batch Count", + ) + with gr.Tab("Video"): + video_frame_rate = gr.Slider( + label="Frames per second", + value=30, + minimum=1, + maximum=60, + ) + video_zoom_mode = gr.Radio( + label="Zoom mode", + choices=["Zoom-out", "Zoom-in"], + value="Zoom-out", + type="index", + ) + video_start_frame_dupe_amount = gr.Slider( + label="number of start frame dupe", + info="Frames to freeze at the start of the video", + value=0, + minimum=1, + maximum=60, + ) + video_last_frame_dupe_amount = gr.Slider( + label="number of last frame dupe", + info="Frames to freeze at the end of the video", + value=0, + minimum=1, + maximum=60, + ) + video_zoom_speed = gr.Slider( + label="Zoom Speed", + value=1.0, + minimum=0.1, + maximum=20.0, + step=0.1, + info="Zoom speed in seconds (higher values create slower zoom)", + ) + + with gr.Tab("Outpaint"): + inpainting_denoising_strength = gr.Slider( + label="Denoising Strength", minimum=0.75, maximum=1, value=1 + ) + inpainting_mask_blur = gr.Slider( + label="Mask Blur", minimum=0, maximum=64, value=0 + ) + inpainting_fill_mode = gr.Radio( + label="Masked content", + choices=["fill", "original", "latent noise", "latent nothing"], + value="latent noise", + type="index", + ) + inpainting_full_res = gr.Checkbox(label="Inpaint Full Resolution") + inpainting_padding = gr.Slider( + label="masked padding", minimum=0, maximum=256, value=0 + ) + + with gr.Tab("Post proccess"): + upscale_do = gr.Checkbox(False, label="Enable Upscale") + upscaler_name = gr.Dropdown( + label="Upscaler", + elem_id="infZ_upscaler", + choices=[x.name for x in shared.sd_upscalers], + value=shared.sd_upscalers[0].name, + ) + + upscale_by = gr.Slider( + label="Upscale by factor", minimum=1, maximum=8, value=1 + ) + with gr.Accordion("Help", open=False): + gr.Markdown( + """# Performance critical +Depending on amount of frames and which upscaler you choose it might took a long time to render. +Our best experience and trade-off is the R-ERSGAn4x upscaler. +""" + ) + + with gr.Column(scale=1, variant="compact"): + output_video = gr.Video(label="Output").style(width=512, height=512) + ( + out_image, + generation_info, + html_info, + html_log, + ) = create_output_panel( + "infinite-zoom", shared.opts.outdir_img2img_samples + ) + generate_btn.click( + fn=wrap_gradio_gpu_call(create_zoom, extra_outputs=[None, "", ""]), + inputs=[ + main_prompts, + main_negative_prompt, + main_outpaint_steps, + main_guidance_scale, + sampling_step, + init_image, + exit_image, + video_frame_rate, + video_zoom_mode, + video_start_frame_dupe_amount, + video_last_frame_dupe_amount, + inpainting_denoising_strength, + inpainting_mask_blur, + inpainting_fill_mode, + inpainting_full_res, + inpainting_padding, + video_zoom_speed, + seed, + main_width, + main_height, + batchcount_slider, + main_sampler, + upscale_do, + upscaler_name, + upscale_by, + ], + outputs=[output_video, out_image, generation_info, html_info, html_log], + ) + interrupt.click(fn=lambda: shared.state.interrupt(), inputs=[], outputs=[]) + infinite_zoom_interface.queue() + return [(infinite_zoom_interface, "Infinite Zoom", "iz_interface")] diff --git a/scripts/infinite-zoom.py b/scripts/infinite-zoom.py index f5e9bc4..1aaadab 100644 --- a/scripts/infinite-zoom.py +++ b/scripts/infinite-zoom.py @@ -1,897 +1,5 @@ -import sys -import os -import time -import json -from jsonschema import validate - -import numpy as np -import gradio as gr -from PIL import Image -import math -import json - -from iz_helpers import shrink_and_paste_on_blank, write_video -from webui import wrap_gradio_gpu_call -from modules import script_callbacks, scripts -import modules.shared as shared -from modules.processing import ( - process_images, - StableDiffusionProcessingTxt2Img, - StableDiffusionProcessingImg2Img, -) - -from scripts import postprocessing_upscale - -from modules.ui import create_output_panel, plaintext_to_html -import modules.sd_models -import modules.sd_samplers - -from modules import scripts - -usefulDirs = scripts.basedir().split(os.sep)[ - -2: -] # contains install and our extension foldername -jsonprompt_schemafile = ( - usefulDirs[0] + "/" + usefulDirs[1] + "/scripts/promptschema.json" -) - -available_samplers = [s.name for s in modules.sd_samplers.samplers if "UniPc" not in s.name] - -default_prompt = """ -{ - "prompts":{ - "headers":["outpaint steps","prompt"], - "data":[ - [0,"Huge spectacular Waterfall in a dense tropical forest,epic perspective,(vegetation overgrowth:1.3)(intricate, ornamentation:1.1),(baroque:1.1), fantasy, (realistic:1) digital painting , (magical,mystical:1.2) , (wide angle shot:1.4), (landscape composed:1.2)(medieval:1.1), divine,cinematic,(tropical forest:1.4),(river:1.3)mythology,india, volumetric lighting, Hindu ,epic, Alex Horley Wenjun Lin greg rutkowski Ruan Jia (Wayne Barlowe:1.2) "], - ] - }, - "negPrompt":"frames, borderline, text, character, duplicate, error, out of frame, watermark, low quality, ugly, deformed, blur bad-artist" -} -""" - -empty_prompt = ( - '{"prompts":{"data":[],"headers":["outpaint steps","prompt"]},"negPrompt":""}' -) - -# must be python dict -invalid_prompt = { - "prompts": { - "data": [[0, "Your prompt-json is invalid, please check Settings"]], - "headers": ["outpaint steps", "prompt"], - }, - "negPrompt": "Invalid prompt-json", -} - - -def closest_upper_divisible_by_eight(num): - if num % 8 == 0: - return num - else: - return math.ceil(num / 8) * 8 - - -# example fail: 720 px width * 1.66 upscale => 1195.2 => 1195 crash -# 512 px * 1.66 = 513.66 = ? -# assume ffmpeg will CUT to integer -# 721 /720 - -def do_upscaleImg(curImg, upscale_do, upscaler_name, upscale_by): - if not upscale_do: - return curImg - - # ensure even width and even height for ffmpeg - # if odd, switch to scale to mode - rwidth = round(curImg.width * upscale_by) - rheight = round(curImg.height * upscale_by) - - ups_mode = 2 # upscale_by - if ( (rwidth %2) == 1 ): - ups_mode = 1 - rwidth += 1 - if ( (rheight %2) == 1 ): - ups_mode = 1 - rheight += 1 - - if (1 == ups_mode ): - print ("Infinite Zoom: aligning output size to even width and height: " + str(rwidth) +" x "+str(rheight), end='\r' ) - - pp = postprocessing_upscale.scripts_postprocessing.PostprocessedImage( - curImg - ) - ups = postprocessing_upscale.ScriptPostprocessingUpscale() - ups.process( - pp, - upscale_mode=ups_mode, - upscale_by=upscale_by, - upscale_to_width=rwidth, - upscale_to_height=rheight, - upscale_crop=False, - upscaler_1_name=upscaler_name, - upscaler_2_name=None, - upscaler_2_visibility=0.0, - ) - return pp.image - - -def renderTxt2Img(prompt, negative_prompt, sampler, steps, cfg_scale, seed, width, height): - processed = None - p = StableDiffusionProcessingTxt2Img( - sd_model=shared.sd_model, - outpath_samples=shared.opts.outdir_txt2img_samples, - outpath_grids=shared.opts.outdir_txt2img_grids, - prompt=prompt, - negative_prompt=negative_prompt, - seed=seed, - sampler_name=sampler, - n_iter=1, - steps=steps, - cfg_scale=cfg_scale, - width=width, - height=height, - ) - processed = process_images(p) - newseed = p.seed - return processed, newseed - - -def renderImg2Img( - prompt, - negative_prompt, - sampler, - steps, - cfg_scale, - seed, - width, - height, - init_image, - mask_image, - inpainting_denoising_strength, - inpainting_mask_blur, - inpainting_fill_mode, - inpainting_full_res, - inpainting_padding, -): - processed = None - - p = StableDiffusionProcessingImg2Img( - sd_model=shared.sd_model, - outpath_samples=shared.opts.outdir_img2img_samples, - outpath_grids=shared.opts.outdir_img2img_grids, - prompt=prompt, - negative_prompt=negative_prompt, - seed=seed, - sampler_name=sampler, - n_iter=1, - steps=steps, - cfg_scale=cfg_scale, - width=width, - height=height, - init_images=[init_image], - denoising_strength=inpainting_denoising_strength, - mask_blur=inpainting_mask_blur, - inpainting_fill=inpainting_fill_mode, - inpaint_full_res=inpainting_full_res, - inpaint_full_res_padding=inpainting_padding, - mask=mask_image, - ) - # p.latent_mask = Image.new("RGB", (p.width, p.height), "white") - - processed = process_images(p) - newseed = p.seed - return processed, newseed - - -def fix_env_Path_ffprobe(): - envpath = os.environ["PATH"] - ffppath = shared.opts.data.get("infzoom_ffprobepath", "") - - if ffppath and not ffppath in envpath: - path_sep = ";" if os.name == "nt" else ":" - os.environ["PATH"] = envpath + path_sep + ffppath - - -def load_model_from_setting(model_field_name, progress, progress_desc): - # fix typo in Automatic1111 vs Vlad111 - if hasattr(modules.sd_models, "checkpoint_alisases"): - checkPList = modules.sd_models.checkpoint_alisases - elif hasattr(modules.sd_models, "checkpoint_aliases"): - checkPList = modules.sd_models.checkpoint_aliases - else: - raise Exception("This is not a compatible StableDiffusion Platform, can not access checkpoints") - - model_name = shared.opts.data.get(model_field_name) - if model_name is not None and model_name != "": - checkinfo = checkPList[model_name] - - if not checkinfo: - raise NameError(model_field_name + " Does not exist in your models.") - - if progress: - progress(0, desc=progress_desc + checkinfo.name) - - modules.sd_models.load_model(checkinfo) - - -def create_zoom( - prompts_array, - negative_prompt, - num_outpainting_steps, - guidance_scale, - num_inference_steps, - custom_init_image, - custom_exit_image, - video_frame_rate, - video_zoom_mode, - video_start_frame_dupe_amount, - video_last_frame_dupe_amount, - inpainting_denoising_strength, - inpainting_mask_blur, - inpainting_fill_mode, - inpainting_full_res, - inpainting_padding, - zoom_speed, - seed, - outputsizeW, - outputsizeH, - batchcount, - sampler, - upscale_do, - upscaler_name, - upscale_by, - progress=None, -): - - for i in range(batchcount): - print(f"Batch {i+1}/{batchcount}") - result = create_zoom_single( - prompts_array, - negative_prompt, - num_outpainting_steps, - guidance_scale, - num_inference_steps, - custom_init_image, - custom_exit_image, - video_frame_rate, - video_zoom_mode, - video_start_frame_dupe_amount, - video_last_frame_dupe_amount, - inpainting_denoising_strength, - inpainting_mask_blur, - inpainting_fill_mode, - inpainting_full_res, - inpainting_padding, - zoom_speed, - seed, - outputsizeW, - outputsizeH, - sampler, - upscale_do, - upscaler_name, - upscale_by, - progress, - ) - return result - - -def create_zoom_single( - prompts_array, - negative_prompt, - num_outpainting_steps, - guidance_scale, - num_inference_steps, - custom_init_image, - custom_exit_image, - video_frame_rate, - video_zoom_mode, - video_start_frame_dupe_amount, - video_last_frame_dupe_amount, - inpainting_denoising_strength, - inpainting_mask_blur, - inpainting_fill_mode, - inpainting_full_res, - inpainting_padding, - zoom_speed, - seed, - outputsizeW, - outputsizeH, - sampler, - upscale_do, - upscaler_name, - upscale_by, - progress=None, -): - # try: - # if gr.Progress() is not None: - # progress = gr.Progress() - # progress(0, desc="Preparing Initial Image") - # except Exception: - # pass - fix_env_Path_ffprobe() - - prompts = {} - for x in prompts_array: - try: - key = int(x[0]) - value = str(x[1]) - prompts[key] = value - except ValueError: - pass - assert len(prompts_array) > 0, "prompts is empty" - - width = closest_upper_divisible_by_eight(outputsizeW) - height = closest_upper_divisible_by_eight(outputsizeH) - - current_image = Image.new(mode="RGBA", size=(width, height)) - mask_image = np.array(current_image)[:, :, 3] - mask_image = Image.fromarray(255 - mask_image).convert("RGB") - current_image = current_image.convert("RGB") - current_seed = seed - - if custom_init_image: - current_image = custom_init_image.resize( - (width, height), resample=Image.LANCZOS - ) - print("using Custom Initial Image") - else: - load_model_from_setting("infzoom_txt2img_model", progress, "Loading Model for txt2img: ") - - processed, newseed = renderTxt2Img( - prompts[min(k for k in prompts.keys() if k >= 0)], - negative_prompt, - sampler, - num_inference_steps, - guidance_scale, - current_seed, - width, - height, - ) - current_image = processed.images[0] - current_seed = newseed - - mask_width = math.trunc(width / 4) # was initially 512px => 128px - mask_height = math.trunc(height / 4) # was initially 512px => 128px - - num_interpol_frames = round(video_frame_rate * zoom_speed) - - all_frames = [] - - if upscale_do and progress: - progress(0, desc="upscaling inital image") - - all_frames.append( - do_upscaleImg(current_image, upscale_do, upscaler_name, upscale_by) - if upscale_do - else current_image - ) - - load_model_from_setting("infzoom_inpainting_model", progress, "Loading Model for inpainting/img2img: " ) - - for i in range(num_outpainting_steps): - print_out = "Outpaint step: " + str(i + 1) + " / " + str(num_outpainting_steps) + " Seed: " + str(current_seed) - print(print_out) - if progress: - progress(((i + 1) / num_outpainting_steps), desc=print_out) - - prev_image_fix = current_image - prev_image = shrink_and_paste_on_blank(current_image, mask_width, mask_height) - current_image = prev_image - - # create mask (black image with white mask_width width edges) - mask_image = np.array(current_image)[:, :, 3] - mask_image = Image.fromarray(255 - mask_image).convert("RGB") - - # inpainting step - current_image = current_image.convert("RGB") - - if custom_exit_image and ((i + 1) == num_outpainting_steps): - current_image = custom_exit_image.resize( - (width, height), resample=Image.LANCZOS - ) - print("using Custom Exit Image") - else: - processed, newseed = renderImg2Img( - prompts[max(k for k in prompts.keys() if k <= i)], - negative_prompt, - sampler, - num_inference_steps, - guidance_scale, - current_seed, - width, - height, - current_image, - mask_image, - inpainting_denoising_strength, - inpainting_mask_blur, - inpainting_fill_mode, - inpainting_full_res, - inpainting_padding, - ) - current_image = processed.images[0] - current_seed = newseed - - current_image.paste(prev_image, mask=prev_image) - - # interpolation steps between 2 inpainted images (=sequential zoom and crop) - for j in range(num_interpol_frames - 1): - interpol_image = current_image - - interpol_width = round( - ( - 1 - - (1 - 2 * mask_width / width) - ** (1 - (j + 1) / num_interpol_frames) - ) - * width - / 2 - ) - - interpol_height = round( - ( - 1 - - (1 - 2 * mask_height / height) - ** (1 - (j + 1) / num_interpol_frames) - ) - * height - / 2 - ) - - interpol_image = interpol_image.crop( - ( - interpol_width, - interpol_height, - width - interpol_width, - height - interpol_height, - ) - ) - - interpol_image = interpol_image.resize((width, height)) - - # paste the higher resolution previous image in the middle to avoid drop in quality caused by zooming - interpol_width2 = round( - (1 - (width - 2 * mask_width) / (width - 2 * interpol_width)) - / 2 - * width - ) - - interpol_height2 = round( - (1 - (height - 2 * mask_height) / (height - 2 * interpol_height)) - / 2 - * height - ) - - prev_image_fix_crop = shrink_and_paste_on_blank( - prev_image_fix, interpol_width2, interpol_height2 - ) - - interpol_image.paste(prev_image_fix_crop, mask=prev_image_fix_crop) - - if upscale_do and progress: - progress(((i + 1) / num_outpainting_steps), desc="upscaling interpol") - - all_frames.append( - do_upscaleImg(interpol_image, upscale_do, upscaler_name, upscale_by) - if upscale_do - else interpol_image - ) - - if upscale_do and progress: - progress(((i + 1) / num_outpainting_steps), desc="upscaling current") - - all_frames.append( - do_upscaleImg(current_image, upscale_do, upscaler_name, upscale_by) - if upscale_do - else current_image - ) - - video_file_name = "infinite_zoom_" + str(int(time.time())) + ".mp4" - output_path = shared.opts.data.get( - "infzoom_outpath", shared.opts.data.get("outdir_img2img_samples") - ) - save_path = os.path.join( - output_path, shared.opts.data.get("infzoom_outSUBpath", "infinite-zooms") - ) - if not os.path.exists(save_path): - os.makedirs(save_path) - out = os.path.join(save_path, video_file_name) - write_video( - out, - all_frames, - video_frame_rate, - video_zoom_mode, - int(video_start_frame_dupe_amount), - int(video_last_frame_dupe_amount), - ) - - return ( - out, - processed.images, - processed.js(), - plaintext_to_html(processed.info), - plaintext_to_html(""), - ) - - -def validatePromptJson_throws(data): - with open(jsonprompt_schemafile, "r") as s: - schema = json.load(s) - validate(instance=data, schema=schema) - - -def putPrompts(files): - try: - with open(files.name, "r") as f: - file_contents = f.read() - data = json.loads(file_contents) - validatePromptJson_throws(data) - return [ - gr.DataFrame.update(data["prompts"]), - gr.Textbox.update(data["negPrompt"]), - ] - - except Exception: - gr.Error( - "loading your prompt failed. It seems to be invalid. Your prompt table is preserved." - ) - print( - "[InfiniteZoom:] Loading your prompt failed. It seems to be invalid. Your prompt table is preserved." - ) - return [gr.DataFrame.update(), gr.Textbox.update()] - - -def clearPrompts(): - return [ - gr.DataFrame.update(value=[[0, "Infinite Zoom. Start over"]]), - gr.Textbox.update(""), - ] - - -def on_ui_tabs(): - with gr.Blocks(analytics_enabled=False) as infinite_zoom_interface: - gr.HTML( - """ -

- GitHub Repo - Discord server -

- - """ - ) - with gr.Row(): - generate_btn = gr.Button(value="Generate video", variant="primary") - interrupt = gr.Button(value="Interrupt", elem_id="interrupt_training") - with gr.Row(): - with gr.Column(scale=1, variant="panel"): - with gr.Tab("Main"): - main_outpaint_steps = gr.Slider( - minimum=2, - maximum=100, - step=1, - value=8, - label="Total Outpaint Steps", - info="The more it is, the longer your videos will be", - ) - - # safe reading json prompt - pr = shared.opts.data.get("infzoom_defPrompt", default_prompt) - if not pr: - pr = empty_prompt - - try: - jpr = json.loads(pr) - validatePromptJson_throws(jpr) - except Exception: - jpr = invalid_prompt - - main_prompts = gr.Dataframe( - type="array", - headers=["outpaint step", "prompt"], - datatype=["number", "str"], - row_count=1, - col_count=(2, "fixed"), - value=jpr["prompts"], - wrap=True, - ) - - main_negative_prompt = gr.Textbox( - value=jpr["negPrompt"], label="Negative Prompt" - ) - - # these button will be moved using JS unde the dataframe view as small ones - exportPrompts_button = gr.Button( - value="Export prompts", - variant="secondary", - elem_classes="sm infzoom_tab_butt", - elem_id="infzoom_exP_butt", - ) - importPrompts_button = gr.UploadButton( - label="Import prompts", - variant="secondary", - elem_classes="sm infzoom_tab_butt", - elem_id="infzoom_imP_butt", - ) - exportPrompts_button.click( - None, - _js="exportPrompts", - inputs=[main_prompts, main_negative_prompt], - outputs=None, - ) - importPrompts_button.upload( - fn=putPrompts, - outputs=[main_prompts, main_negative_prompt], - inputs=[importPrompts_button], - ) - - clearPrompts_button = gr.Button( - value="Clear prompts", - variant="secondary", - elem_classes="sm infzoom_tab_butt", - elem_id="infzoom_clP_butt", - ) - clearPrompts_button.click( - fn=clearPrompts, - inputs=[], - outputs=[main_prompts, main_negative_prompt], - ) - with gr.Row(): - seed = gr.Number(label="Seed", value=-1, precision=0, interactive=True) - main_sampler = gr.Dropdown( - label="Sampler", - choices=available_samplers, - value="Euler a", - type="value", - ) - with gr.Row(): - main_width = gr.Slider( - minimum=16, - maximum=2048, - value=shared.opts.data.get("infzoom_outsizeW", 512), - step=16, - label="Output Width", - ) - main_height = gr.Slider( - minimum=16, - maximum=2048, - value=shared.opts.data.get("infzoom_outsizeH", 512), - step=16, - label="Output Height", - ) - with gr.Row(): - main_guidance_scale = gr.Slider( - minimum=0.1, - maximum=15, - step=0.1, - value=7, - label="Guidance Scale", - ) - sampling_step = gr.Slider( - minimum=1, - maximum=100, - step=1, - value=50, - label="Sampling Steps for each outpaint", - ) - with gr.Row(): - init_image = gr.Image(type="pil", label="custom initial image") - exit_image = gr.Image(type="pil", label="custom exit image") - - batchcount_slider = gr.Slider( - minimum=1, - maximum=25, - value=shared.opts.data.get("infzoom_batchcount", 1), - step=1, - label="Batch Count", - ) - with gr.Tab("Video"): - video_frame_rate = gr.Slider( - label="Frames per second", - value=30, - minimum=1, - maximum=60, - ) - video_zoom_mode = gr.Radio( - label="Zoom mode", - choices=["Zoom-out", "Zoom-in"], - value="Zoom-out", - type="index", - ) - video_start_frame_dupe_amount = gr.Slider( - label="number of start frame dupe", - info="Frames to freeze at the start of the video", - value=0, - minimum=1, - maximum=60, - ) - video_last_frame_dupe_amount = gr.Slider( - label="number of last frame dupe", - info="Frames to freeze at the end of the video", - value=0, - minimum=1, - maximum=60, - ) - video_zoom_speed = gr.Slider( - label="Zoom Speed", - value=1.0, - minimum=0.1, - maximum=20.0, - step=0.1, - info="Zoom speed in seconds (higher values create slower zoom)", - ) - - with gr.Tab("Outpaint"): - inpainting_denoising_strength = gr.Slider( - label="Denoising Strength", minimum=0.75, maximum=1, value=1 - ) - inpainting_mask_blur = gr.Slider( - label="Mask Blur", minimum=0, maximum=64, value=0 - ) - inpainting_fill_mode = gr.Radio( - label="Masked content", - choices=["fill", "original", "latent noise", "latent nothing"], - value="latent noise", - type="index", - ) - inpainting_full_res = gr.Checkbox(label="Inpaint Full Resolution") - inpainting_padding = gr.Slider( - label="masked padding", minimum=0, maximum=256, value=0 - ) - - with gr.Tab("Post proccess"): - upscale_do = gr.Checkbox(False, label="Enable Upscale") - upscaler_name = gr.Dropdown( - label="Upscaler", - elem_id="infZ_upscaler", - choices=[x.name for x in shared.sd_upscalers], - value=shared.sd_upscalers[0].name, - ) - - upscale_by = gr.Slider( - label="Upscale by factor", minimum=1, maximum=8, value=1 - ) - with gr.Accordion("Help", open=False): - gr.Markdown( - """# Performance critical -Depending on amount of frames and which upscaler you choose it might took a long time to render. -Our best experience and trade-off is the R-ERSGAn4x upscaler. -""" - ) - - with gr.Column(scale=1, variant="compact"): - output_video = gr.Video(label="Output").style(width=512, height=512) - ( - out_image, - generation_info, - html_info, - html_log, - ) = create_output_panel( - "infinite-zoom", shared.opts.outdir_img2img_samples - ) - generate_btn.click( - fn=wrap_gradio_gpu_call(create_zoom, extra_outputs=[None, "", ""]), - inputs=[ - main_prompts, - main_negative_prompt, - main_outpaint_steps, - main_guidance_scale, - sampling_step, - init_image, - exit_image, - video_frame_rate, - video_zoom_mode, - video_start_frame_dupe_amount, - video_last_frame_dupe_amount, - inpainting_denoising_strength, - inpainting_mask_blur, - inpainting_fill_mode, - inpainting_full_res, - inpainting_padding, - video_zoom_speed, - seed, - main_width, - main_height, - batchcount_slider, - main_sampler, - upscale_do, - upscaler_name, - upscale_by, - ], - outputs=[output_video, out_image, generation_info, html_info, html_log], - ) - interrupt.click(fn=lambda: shared.state.interrupt(), inputs=[], outputs=[]) - infinite_zoom_interface.queue() - return [(infinite_zoom_interface, "Infinite Zoom", "iz_interface")] - - -def on_ui_settings(): - section = ("infinite-zoom", "Infinite Zoom") - - shared.opts.add_option( - "outputs" - "infzoom_outpath", - shared.OptionInfo( - "", - "Path where to store your infinite video. Default is Outputs", - gr.Textbox, - {"interactive": True}, - section=section, - ), - ) - - shared.opts.add_option( - "infzoom_outSUBpath", - shared.OptionInfo( - "infinite-zooms", - "Which subfolder name to be created in the outpath. Default is 'infinite-zooms'", - gr.Textbox, - {"interactive": True}, - section=section, - ), - ) - - shared.opts.add_option( - "infzoom_outsizeW", - shared.OptionInfo( - 512, - "Default width of your video", - gr.Slider, - {"minimum": 16, "maximum": 2048, "step": 16}, - section=section, - ), - ) - - shared.opts.add_option( - "infzoom_outsizeH", - shared.OptionInfo( - 512, - "Default height your video", - gr.Slider, - {"minimum": 16, "maximum": 2048, "step": 16}, - section=section, - ), - ) - - shared.opts.add_option( - "infzoom_ffprobepath", - shared.OptionInfo( - "", - "Writing videos has dependency to an existing FFPROBE executable on your machine. D/L here (https://github.com/BtbN/FFmpeg-Builds/releases) your OS variant and point to your installation path", - gr.Textbox, - {"interactive": True}, - section=section, - ), - ) - - shared.opts.add_option( - "infzoom_txt2img_model", - shared.OptionInfo( - None, - "Name of your desired model to render keyframes (txt2img)", - gr.Dropdown, - lambda: {"choices": shared.list_checkpoint_tiles()}, - section=section, - ), - ) - - shared.opts.add_option( - "infzoom_inpainting_model", - shared.OptionInfo( - None, - "Name of your desired inpaint model (img2img-inpaint). Default is vanilla sd-v1-5-inpainting.ckpt ", - gr.Dropdown, - lambda: {"choices": shared.list_checkpoint_tiles()}, - section=section, - ), - ) - - shared.opts.add_option( - "infzoom_defPrompt", - shared.OptionInfo( - default_prompt, - "Default prompt-setup to start with'", - gr.Code, - {"interactive": True, "language": "json"}, - section=section, - ), - ) +from iz_helpers import on_ui_tabs, on_ui_settings +from modules import script_callbacks script_callbacks.on_ui_tabs(on_ui_tabs) From f933aeb2849e03ee04164ea344365f1ad571225d Mon Sep 17 00:00:00 2001 From: jmsether Date: Sun, 23 Apr 2023 03:46:11 -0600 Subject: [PATCH 03/44] Fixing grid images causing ffmpeg to crash --- iz_helpers/sd_helpers.py | 7 ++++++- iz_helpers/video.py | 5 ++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/iz_helpers/sd_helpers.py b/iz_helpers/sd_helpers.py index 396eb1d..3a4b543 100644 --- a/iz_helpers/sd_helpers.py +++ b/iz_helpers/sd_helpers.py @@ -70,7 +70,12 @@ def renderImg2Img( mask=mask_image, ) # p.latent_mask = Image.new("RGB", (p.width, p.height), "white") - + processed = process_images(p) + # For those that use Image grids this will make sure that ffmpeg does not crash out + if (processed.images[0].size[0] != processed.images[1].size[0]): + processed.images.pop(0) + print("\nGrid image detected applying patch") + newseed = p.seed return processed, newseed diff --git a/iz_helpers/video.py b/iz_helpers/video.py index c3d61a3..cb1c5d9 100644 --- a/iz_helpers/video.py +++ b/iz_helpers/video.py @@ -13,9 +13,8 @@ def write_video(file_path, frames, fps, reversed=True, start_frame_dupe_amount=1 if reversed == True: frames = frames[::-1] - # Get dimensions of the first frames, all subsequent has to be same sized - for k in frames: - assert (k.size == frames[0].size,"Different frame sizes found!") + # Drop missformed frames + frames = [frame for frame in frames if frame.size == frames[0].size] # Create an imageio video writer, avoid block size of 512. writer = imageio.get_writer(file_path, fps=fps, macro_block_size=None) From 38b64d866e79ae880b836ee23b799e28570d6966 Mon Sep 17 00:00:00 2001 From: jmsether Date: Sun, 23 Apr 2023 16:29:48 -0600 Subject: [PATCH 04/44] Fixed rare crash bug when interupting generation --- iz_helpers/sd_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iz_helpers/sd_helpers.py b/iz_helpers/sd_helpers.py index 3a4b543..7b0ac0f 100644 --- a/iz_helpers/sd_helpers.py +++ b/iz_helpers/sd_helpers.py @@ -73,7 +73,7 @@ def renderImg2Img( processed = process_images(p) # For those that use Image grids this will make sure that ffmpeg does not crash out - if (processed.images[0].size[0] != processed.images[1].size[0]): + if (processed.images[0].size[0] != processed.images[-1].size[0]): processed.images.pop(0) print("\nGrid image detected applying patch") From 7f56865112dc89071c00ad0d0e86028c7a68ea23 Mon Sep 17 00:00:00 2001 From: jmsether Date: Sun, 23 Apr 2023 16:33:38 -0600 Subject: [PATCH 05/44] So apparently SD can return an array of zero... who knew, Implemented check for that as well. Interupting generation should no longer produce crashes --- iz_helpers/sd_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iz_helpers/sd_helpers.py b/iz_helpers/sd_helpers.py index 7b0ac0f..e6d5d6b 100644 --- a/iz_helpers/sd_helpers.py +++ b/iz_helpers/sd_helpers.py @@ -73,7 +73,7 @@ def renderImg2Img( processed = process_images(p) # For those that use Image grids this will make sure that ffmpeg does not crash out - if (processed.images[0].size[0] != processed.images[-1].size[0]): + if (len(processed.images) > 1) and (processed.images[0].size[0] != processed.images[-1].size[0]): processed.images.pop(0) print("\nGrid image detected applying patch") From f2764672defbc3ccc76b4efcebaa4a3115659c1e Mon Sep 17 00:00:00 2001 From: jmsether Date: Sun, 23 Apr 2023 18:03:56 -0600 Subject: [PATCH 06/44] Fixed all areas that were triggering with a array of zero crash. --- iz_helpers/run.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/iz_helpers/run.py b/iz_helpers/run.py index c9b2d74..3a8d97e 100644 --- a/iz_helpers/run.py +++ b/iz_helpers/run.py @@ -149,7 +149,8 @@ def create_zoom_single( width, height, ) - current_image = processed.images[0] + if(len(processed.images) > 0): + current_image = processed.images[0] current_seed = newseed mask_width = math.trunc(width / 4) # was initially 512px => 128px @@ -219,10 +220,11 @@ def create_zoom_single( inpainting_full_res, inpainting_padding, ) - current_image = processed.images[0] + if(len(processed.images) > 0): + current_image = processed.images[0] current_seed = newseed - - current_image.paste(prev_image, mask=prev_image) + if(len(processed.images) > 0): + current_image.paste(prev_image, mask=prev_image) # interpolation steps between 2 inpainted images (=sequential zoom and crop) for j in range(num_interpol_frames - 1): From b0cb75399eb4e51b4ce8690a248b0a518589b327 Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Mon, 24 Apr 2023 14:12:00 +0200 Subject: [PATCH 07/44] added common prompt; todo convert old json prompts to 1.1 schema --- iz_helpers/helpers.py | 1 + iz_helpers/promptschema.json | 97 +++++++++++++++++++--------------- iz_helpers/run.py | 9 +++- iz_helpers/static_variables.py | 18 ++++--- iz_helpers/ui.py | 9 +++- javascript/infinite-zoom.js | 4 +- 6 files changed, 82 insertions(+), 56 deletions(-) diff --git a/iz_helpers/helpers.py b/iz_helpers/helpers.py index c119212..5756470 100644 --- a/iz_helpers/helpers.py +++ b/iz_helpers/helpers.py @@ -122,4 +122,5 @@ def clearPrompts(): return [ gr.DataFrame.update(value=[[0, "Infinite Zoom. Start over"]]), gr.Textbox.update(""), + gr.Textbox.update("") ] diff --git a/iz_helpers/promptschema.json b/iz_helpers/promptschema.json index b7fcfd4..281855f 100644 --- a/iz_helpers/promptschema.json +++ b/iz_helpers/promptschema.json @@ -1,49 +1,60 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "prompts": { - "type": "object", - "properties": { - "data": { + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "1.1", + "type": "object", + "properties": { + "prompts": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { "type": "array", - "items": { - "type": "array", - "items": [ - { - "oneOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "string" - } - ] - }, - { - "type": "string" - } - ], - "minItems": 0, - "maxItems": 999, - "uniqueItems": false - }, - "minItems": 0 + "items": [ + { + "oneOf": [ + { + "type": "integer", + "minimum": 0 + }, + { + "type": "string" + } + ] + }, + { + "type": "string" + } + ], + "minItems": 0, + "maxItems": 999, + "uniqueItems": false }, - "headers": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 2 - } + "minItems": 0 }, - "required": ["data", "headers"] + "headers": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 3 + } }, - "negPrompt": { - "type": "string" - } + "required": [ + "data", + "headers" + ] }, - "required": ["prompts", "negPrompt"] - } \ No newline at end of file + "negPrompt": { + "type": "string" + }, + "commonPrompt": { + "type": "string" + } + }, + "required": [ + "prompts", + "negPrompt", + "commonPrompt" + ] +} \ No newline at end of file diff --git a/iz_helpers/run.py b/iz_helpers/run.py index 3a8d97e..0a9789a 100644 --- a/iz_helpers/run.py +++ b/iz_helpers/run.py @@ -16,6 +16,7 @@ from .video import write_video def create_zoom( + common_prompt, prompts_array, negative_prompt, num_outpainting_steps, @@ -46,6 +47,7 @@ def create_zoom( for i in range(batchcount): print(f"Batch {i+1}/{batchcount}") result = create_zoom_single( + common_prompt, prompts_array, negative_prompt, num_outpainting_steps, @@ -76,6 +78,7 @@ def create_zoom( def create_zoom_single( + common_prompt, prompts_array, negative_prompt, num_outpainting_steps, @@ -139,8 +142,9 @@ def create_zoom_single( "infzoom_txt2img_model", progress, "Loading Model for txt2img: " ) + pr = prompts[min(k for k in prompts.keys() if k >= 0)] processed, newseed = renderTxt2Img( - prompts[min(k for k in prompts.keys() if k >= 0)], + f"{common_prompt}\n{pr}" if common_prompt else pr, negative_prompt, sampler, num_inference_steps, @@ -203,8 +207,9 @@ def create_zoom_single( ) print("using Custom Exit Image") else: + pr = prompts[max(k for k in prompts.keys() if k <= i)] processed, newseed = renderImg2Img( - prompts[max(k for k in prompts.keys() if k <= i)], + f"{common_prompt}\n{pr}" if common_prompt else pr, negative_prompt, sampler, num_inference_steps, diff --git a/iz_helpers/static_variables.py b/iz_helpers/static_variables.py index ac25cb1..efa83dd 100644 --- a/iz_helpers/static_variables.py +++ b/iz_helpers/static_variables.py @@ -5,20 +5,18 @@ import modules.sd_samplers default_prompt = """ { "prompts":{ - "headers":["outpaint steps","prompt"], + "headers":["outpaint steps","prompt","img"], "data":[ - [0,"Huge spectacular Waterfall in a dense tropical forest,epic perspective,(vegetation overgrowth:1.3)(intricate, ornamentation:1.1),(baroque:1.1), fantasy, (realistic:1) digital painting , (magical,mystical:1.2) , (wide angle shot:1.4), (landscape composed:1.2)(medieval:1.1), divine,cinematic,(tropical forest:1.4),(river:1.3)mythology,india, volumetric lighting, Hindu ,epic, Alex Horley Wenjun Lin greg rutkowski Ruan Jia (Wayne Barlowe:1.2) "] + [0,"Huge spectacular Waterfall in a dense tropical forest,epic perspective,(vegetation overgrowth:1.3)(intricate, ornamentation:1.1),(baroque:1.1), fantasy, (realistic:1) digital painting , (magical,mystical:1.2) , (wide angle shot:1.4), (landscape composed:1.2)(medieval:1.1), divine,cinematic,(tropical forest:1.4),(river:1.3)mythology,india, volumetric lighting, Hindu ,epic"] ] }, - "negPrompt":"frames, borderline, text, character, duplicate, error, out of frame, watermark, low quality, ugly, deformed, blur bad-artist" + "negPrompt":"frames, border, edges, borderline, text, character, duplicate, error, out of frame, watermark, low quality, ugly, deformed, blur bad-artist", + "commonPrompt":"style by Alex Horley Wenjun Lin greg rutkowski Ruan Jia (Wayne Barlowe:1.2), " } """ -available_samplers = [ - s.name for s in modules.sd_samplers.samplers if "UniPc" not in s.name -] empty_prompt = ( - '{"prompts":{"data":[],"headers":["outpaint steps","prompt"]},"negPrompt":""}' + '{"prompts":{"data":[],"headers":["outpaint steps","prompt"]},"negPrompt":"", commonPrompt:""}' ) invalid_prompt = { @@ -27,7 +25,13 @@ invalid_prompt = { "headers": ["outpaint steps", "prompt"], }, "negPrompt": "Invalid prompt-json", + "commonPrompt": "Invalid prompt" } + +available_samplers = [ + s.name for s in modules.sd_samplers.samplers if "UniPc" not in s.name +] + current_script_dir = scripts.basedir().split(os.sep)[ -2: ] # contains install and our extension foldername diff --git a/iz_helpers/ui.py b/iz_helpers/ui.py index 9729631..4fc3ab5 100644 --- a/iz_helpers/ui.py +++ b/iz_helpers/ui.py @@ -49,6 +49,10 @@ def on_ui_tabs(): except Exception: jpr = invalid_prompt + main_common_prompt = gr.Textbox( + value=jpr["commonPrompt"], label="Common Prompt" + ) + main_prompts = gr.Dataframe( type="array", headers=["outpaint step", "prompt"], @@ -79,7 +83,7 @@ def on_ui_tabs(): exportPrompts_button.click( None, _js="exportPrompts", - inputs=[main_prompts, main_negative_prompt], + inputs=[main_common_prompt, main_prompts, main_negative_prompt], outputs=None, ) importPrompts_button.upload( @@ -97,7 +101,7 @@ def on_ui_tabs(): clearPrompts_button.click( fn=clearPrompts, inputs=[], - outputs=[main_prompts, main_negative_prompt], + outputs=[main_prompts, main_negative_prompt, main_common_prompt], ) with gr.Row(): seed = gr.Number( @@ -237,6 +241,7 @@ Our best experience and trade-off is the R-ERSGAn4x upscaler. generate_btn.click( fn=wrap_gradio_gpu_call(create_zoom, extra_outputs=[None, "", ""]), inputs=[ + main_common_prompt, main_prompts, main_negative_prompt, main_outpaint_steps, diff --git a/javascript/infinite-zoom.js b/javascript/infinite-zoom.js index 8f7d546..0a41d0c 100644 --- a/javascript/infinite-zoom.js +++ b/javascript/infinite-zoom.js @@ -1,7 +1,7 @@ // Function to download data to a file -function exportPrompts(p, np, filename = "infinite-zoom-prompts.json") { +function exportPrompts(cp,p, np, filename = "infinite-zoom-prompts.json") { - let J = { prompts: p, negPrompt: np } + let J = { prompts: p, negPrompt: np, commonPrompt: cp } var file = new Blob([JSON.stringify(J)], { type: "text/csv" }); if (window.navigator.msSaveOrOpenBlob) // IE10+ From 5dde17ad39a5a5180534e993a441bffa7f64b81a Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Tue, 25 Apr 2023 00:24:37 +0200 Subject: [PATCH 08/44] refactor jsonschema --- iz_helpers/helpers.py | 24 ++++++++++-------------- iz_helpers/ui.py | 22 +++++++++------------- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/iz_helpers/helpers.py b/iz_helpers/helpers.py index 5756470..6e5ffca 100644 --- a/iz_helpers/helpers.py +++ b/iz_helpers/helpers.py @@ -1,12 +1,11 @@ import math import os -import json -from jsonschema import validate import modules.shared as shared import modules.sd_models import gradio as gr from scripts import postprocessing_upscale -from .static_variables import jsonprompt_schemafile +from .prompt_util import readJsonPrompt +import asyncio def fix_env_Path_ffprobe(): @@ -90,31 +89,28 @@ def do_upscaleImg(curImg, upscale_do, upscaler_name, upscale_by): ) return pp.image - -def validatePromptJson_throws(data): - with open(jsonprompt_schemafile, "r") as s: - schema = json.load(s) - validate(instance=data, schema=schema) - +async def showGradioErrorAsync(txt, delay=1): + await asyncio.sleep(delay) # sleep for 1 second + raise gr.Error(txt) def putPrompts(files): try: with open(files.name, "r") as f: file_contents = f.read() - data = json.loads(file_contents) - validatePromptJson_throws(data) + + data = readJsonPrompt(file_contents,False) return [ gr.DataFrame.update(data["prompts"]), gr.Textbox.update(data["negPrompt"]), ] except Exception: - gr.Error( - "loading your prompt failed. It seems to be invalid. Your prompt table is preserved." - ) print( "[InfiniteZoom:] Loading your prompt failed. It seems to be invalid. Your prompt table is preserved." ) + asyncio.run( + showGradioErrorAsync("Loading your prompts failed. It seems to be invalid. Your prompt table has been preserved.",5) + ) return [gr.DataFrame.update(), gr.Textbox.update()] diff --git a/iz_helpers/ui.py b/iz_helpers/ui.py index 4fc3ab5..d136ea0 100644 --- a/iz_helpers/ui.py +++ b/iz_helpers/ui.py @@ -1,4 +1,3 @@ -import json import gradio as gr from .run import create_zoom import modules.shared as shared @@ -6,11 +5,10 @@ from webui import wrap_gradio_gpu_call from modules.ui import create_output_panel from .static_variables import ( default_prompt, - empty_prompt, - invalid_prompt, available_samplers, ) -from .helpers import validatePromptJson_throws, putPrompts, clearPrompts +from .helpers import putPrompts, clearPrompts +from .prompt_util import readJsonPrompt def on_ui_tabs(): @@ -41,13 +39,11 @@ def on_ui_tabs(): # safe reading json prompt pr = shared.opts.data.get("infzoom_defPrompt", default_prompt) - if not pr: - pr = empty_prompt - try: - jpr = json.loads(pr) - validatePromptJson_throws(jpr) - except Exception: - jpr = invalid_prompt + jpr = readJsonPrompt(pr, True) + + main_common_prompt = gr.Textbox( + value=jpr["commonPrompt"], label="Common Prompt" + ) main_common_prompt = gr.Textbox( value=jpr["commonPrompt"], label="Common Prompt" @@ -83,8 +79,8 @@ def on_ui_tabs(): exportPrompts_button.click( None, _js="exportPrompts", - inputs=[main_common_prompt, main_prompts, main_negative_prompt], - outputs=None, + inputs=[main_prompts, main_negative_prompt], + outputs=None ) importPrompts_button.upload( fn=putPrompts, From eb6f532da2c2c9cb5c9184cf7a9647ec17ffa45c Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Tue, 25 Apr 2023 00:24:48 +0200 Subject: [PATCH 09/44] refactor jsonutil --- iz_helpers/prompt_util.py | 71 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 iz_helpers/prompt_util.py diff --git a/iz_helpers/prompt_util.py b/iz_helpers/prompt_util.py new file mode 100644 index 0000000..4e9fc2c --- /dev/null +++ b/iz_helpers/prompt_util.py @@ -0,0 +1,71 @@ +import json +from jsonschema import validate + +from .static_variables import ( + empty_prompt, + invalid_prompt, + jsonprompt_schemafile +) + +""" + json is valid, but not our current schema. + lets try something. + does it look like something usable? +def fixJson(j): + fixedJ = empty_prompt + try: + if isinstance(j, dict): + if "prompts" in j: + if "data" in j["prompts"]: + if isinstance (j["prompts"]["data"],list): + fixedJ["prompts"]["data"] = j["prompts"]["data"] + if not isinstance (fixedJ["prompts"]["data"][0]. + + if "headers" not in j["prompts"]: + fixedJ["prompts"]["headers"] = ["outpaint steps","prompt"] + else: + fixedJ["prompts"]["headers"] = j["prompts"]["headers"] + + if "negPrompt" in j: + fixedJ["prompts"]["headers"] + + if "commonPrompt" in j: + return j + except Exception: + raise "JsonFix: Failed on recovering json prompt" + return j +""" + +def fixHeaders(j): + if isinstance(j, dict): + if "prompts" in j: + if "headers" not in j["prompts"]: + j["prompts"]["headers"] = ["outpaint steps","prompt"] + return j + + +def validatePromptJson_throws(data): + with open(jsonprompt_schemafile, "r") as s: + schema = json.load(s) + try: + validate(instance=data, schema=schema) + except Exception: + raise "Your prompts are not schema valid." + #fixJson(data) + + return fixHeaders(data) + + +def readJsonPrompt(txt, returnFailPrompt=False): + if not txt: + return empty_prompt + + try: + jpr = json.loads(txt) + except Exception: + if returnFailPrompt is not None: + print (f"Infinite Zoom: Corrupted Json structure: {txt[:24]} ...") + return invalid_prompt + raise (f"Infinite Zoom: Corrupted Json structure: {txt[:24]} ...") + + return validatePromptJson_throws(jpr) From 2f3858eba294b81d83ee2f956154fa0d9d0f9dd0 Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Tue, 25 Apr 2023 00:25:20 +0200 Subject: [PATCH 10/44] headers optional --- iz_helpers/promptschema.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/iz_helpers/promptschema.json b/iz_helpers/promptschema.json index 281855f..1598454 100644 --- a/iz_helpers/promptschema.json +++ b/iz_helpers/promptschema.json @@ -37,12 +37,11 @@ "items": { "type": "string" }, - "minItems": 3 + "minItems": 2 } }, "required": [ - "data", - "headers" + "data" ] }, "negPrompt": { From cf25a1a5aa1555e4b3fcfb30d161e21f490a20b6 Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Tue, 25 Apr 2023 00:49:11 +0200 Subject: [PATCH 11/44] disabled gr.error, fixed double main prompt merge glitch --- iz_helpers/helpers.py | 7 ++++--- iz_helpers/ui.py | 4 ---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/iz_helpers/helpers.py b/iz_helpers/helpers.py index 6e5ffca..2383712 100644 --- a/iz_helpers/helpers.py +++ b/iz_helpers/helpers.py @@ -108,9 +108,10 @@ def putPrompts(files): print( "[InfiniteZoom:] Loading your prompt failed. It seems to be invalid. Your prompt table is preserved." ) - asyncio.run( - showGradioErrorAsync("Loading your prompts failed. It seems to be invalid. Your prompt table has been preserved.",5) - ) + + # error only be shown with raise, so ui gets broken. + #asyncio.run(showGradioErrorAsync("Loading your prompts failed. It seems to be invalid. Your prompt table has been preserved.",5)) + return [gr.DataFrame.update(), gr.Textbox.update()] diff --git a/iz_helpers/ui.py b/iz_helpers/ui.py index d136ea0..d1d52c3 100644 --- a/iz_helpers/ui.py +++ b/iz_helpers/ui.py @@ -45,10 +45,6 @@ def on_ui_tabs(): value=jpr["commonPrompt"], label="Common Prompt" ) - main_common_prompt = gr.Textbox( - value=jpr["commonPrompt"], label="Common Prompt" - ) - main_prompts = gr.Dataframe( type="array", headers=["outpaint step", "prompt"], From 3a481c06ee749a44cf3eff2601d1aec2ff23b1b0 Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Tue, 25 Apr 2023 01:03:21 +0200 Subject: [PATCH 12/44] fix putprompts keep table on invalid input --- iz_helpers/prompt_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iz_helpers/prompt_util.py b/iz_helpers/prompt_util.py index 4e9fc2c..e689528 100644 --- a/iz_helpers/prompt_util.py +++ b/iz_helpers/prompt_util.py @@ -63,7 +63,7 @@ def readJsonPrompt(txt, returnFailPrompt=False): try: jpr = json.loads(txt) except Exception: - if returnFailPrompt is not None: + if returnFailPrompt: print (f"Infinite Zoom: Corrupted Json structure: {txt[:24]} ...") return invalid_prompt raise (f"Infinite Zoom: Corrupted Json structure: {txt[:24]} ...") From 1c44b0b41cc3287cd6361eddabd9c1fbc2f5c717 Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Wed, 26 Apr 2023 05:49:16 +0200 Subject: [PATCH 13/44] added Prefix,Suffix, Toolltips, Accordions for sectionizing groups --- iz_helpers/helpers.py | 7 +- iz_helpers/prompt_util.py | 8 +- iz_helpers/promptschema.json | 8 +- iz_helpers/run.py | 13 +- iz_helpers/static_variables.py | 10 +- iz_helpers/ui.py | 204 ++++++++++++++++-------------- javascript/infinite-zoom-hints.js | 50 ++++++++ javascript/infinite-zoom.js | 4 +- 8 files changed, 191 insertions(+), 113 deletions(-) create mode 100644 javascript/infinite-zoom-hints.js diff --git a/iz_helpers/helpers.py b/iz_helpers/helpers.py index 2383712..4373bac 100644 --- a/iz_helpers/helpers.py +++ b/iz_helpers/helpers.py @@ -100,8 +100,10 @@ def putPrompts(files): data = readJsonPrompt(file_contents,False) return [ + gr.Textbox.update(data["commonPromptPrefix"]), gr.DataFrame.update(data["prompts"]), - gr.Textbox.update(data["negPrompt"]), + gr.Textbox.update(data["commonPromptSuffix"]), + gr.Textbox.update(data["negPrompt"]) ] except Exception: @@ -112,12 +114,13 @@ def putPrompts(files): # error only be shown with raise, so ui gets broken. #asyncio.run(showGradioErrorAsync("Loading your prompts failed. It seems to be invalid. Your prompt table has been preserved.",5)) - return [gr.DataFrame.update(), gr.Textbox.update()] + return [gr.Textbox.update(), gr.DataFrame.update(), gr.Textbox.update(),gr.Textbox.update()] def clearPrompts(): return [ gr.DataFrame.update(value=[[0, "Infinite Zoom. Start over"]]), gr.Textbox.update(""), + gr.Textbox.update(""), gr.Textbox.update("") ] diff --git a/iz_helpers/prompt_util.py b/iz_helpers/prompt_util.py index e689528..a1bd445 100644 --- a/iz_helpers/prompt_util.py +++ b/iz_helpers/prompt_util.py @@ -68,4 +68,10 @@ def readJsonPrompt(txt, returnFailPrompt=False): return invalid_prompt raise (f"Infinite Zoom: Corrupted Json structure: {txt[:24]} ...") - return validatePromptJson_throws(jpr) + try: + return validatePromptJson_throws(jpr) + except Exception: + if returnFailPrompt: + return invalid_prompt + pass + \ No newline at end of file diff --git a/iz_helpers/promptschema.json b/iz_helpers/promptschema.json index 1598454..396a554 100644 --- a/iz_helpers/promptschema.json +++ b/iz_helpers/promptschema.json @@ -47,13 +47,17 @@ "negPrompt": { "type": "string" }, - "commonPrompt": { + "commonPromptPrefix": { + "type": "string" + }, + "commonPromptSuffix": { "type": "string" } }, "required": [ "prompts", "negPrompt", - "commonPrompt" + "commonPromptPrefix", + "commonPromptSuffix" ] } \ No newline at end of file diff --git a/iz_helpers/run.py b/iz_helpers/run.py index 0a9789a..ed15bfd 100644 --- a/iz_helpers/run.py +++ b/iz_helpers/run.py @@ -16,8 +16,9 @@ from .video import write_video def create_zoom( - common_prompt, + common_prompt_pre, prompts_array, + common_prompt_suf, negative_prompt, num_outpainting_steps, guidance_scale, @@ -47,8 +48,9 @@ def create_zoom( for i in range(batchcount): print(f"Batch {i+1}/{batchcount}") result = create_zoom_single( - common_prompt, + common_prompt_pre, prompts_array, + common_prompt_suf, negative_prompt, num_outpainting_steps, guidance_scale, @@ -78,8 +80,9 @@ def create_zoom( def create_zoom_single( - common_prompt, + common_prompt_pre, prompts_array, + common_prompt_suf, negative_prompt, num_outpainting_steps, guidance_scale, @@ -144,7 +147,7 @@ def create_zoom_single( pr = prompts[min(k for k in prompts.keys() if k >= 0)] processed, newseed = renderTxt2Img( - f"{common_prompt}\n{pr}" if common_prompt else pr, + f"{common_prompt_pre}\n{pr}\n{common_prompt_suf}".strip(), negative_prompt, sampler, num_inference_steps, @@ -209,7 +212,7 @@ def create_zoom_single( else: pr = prompts[max(k for k in prompts.keys() if k <= i)] processed, newseed = renderImg2Img( - f"{common_prompt}\n{pr}" if common_prompt else pr, + f"{common_prompt_pre}\n{pr}\n{common_prompt_suf}".strip(), negative_prompt, sampler, num_inference_steps, diff --git a/iz_helpers/static_variables.py b/iz_helpers/static_variables.py index efa83dd..b52b31d 100644 --- a/iz_helpers/static_variables.py +++ b/iz_helpers/static_variables.py @@ -4,19 +4,20 @@ import modules.sd_samplers default_prompt = """ { + "commonPromptPrefix":" ", "prompts":{ "headers":["outpaint steps","prompt","img"], "data":[ [0,"Huge spectacular Waterfall in a dense tropical forest,epic perspective,(vegetation overgrowth:1.3)(intricate, ornamentation:1.1),(baroque:1.1), fantasy, (realistic:1) digital painting , (magical,mystical:1.2) , (wide angle shot:1.4), (landscape composed:1.2)(medieval:1.1), divine,cinematic,(tropical forest:1.4),(river:1.3)mythology,india, volumetric lighting, Hindu ,epic"] ] }, - "negPrompt":"frames, border, edges, borderline, text, character, duplicate, error, out of frame, watermark, low quality, ugly, deformed, blur bad-artist", - "commonPrompt":"style by Alex Horley Wenjun Lin greg rutkowski Ruan Jia (Wayne Barlowe:1.2), " + "commonPromptSuffix":"style by Alex Horley Wenjun Lin greg rutkowski Ruan Jia (Wayne Barlowe:1.2)", + "negPrompt":"frames, border, edges, borderline, text, character, duplicate, error, out of frame, watermark, low quality, ugly, deformed, blur, bad-artist" } """ empty_prompt = ( - '{"prompts":{"data":[],"headers":["outpaint steps","prompt"]},"negPrompt":"", commonPrompt:""}' + '{"prompts":{"data":[],"headers":["outpaint steps","prompt"]},"negPrompt":"", commonPromptPrefix:"", commonPromptSuffix}' ) invalid_prompt = { @@ -25,7 +26,8 @@ invalid_prompt = { "headers": ["outpaint steps", "prompt"], }, "negPrompt": "Invalid prompt-json", - "commonPrompt": "Invalid prompt" + "commonPromptPrefix": "Invalid prompt", + "commonPromptSuffix": "Invalid prompt" } available_samplers = [ diff --git a/iz_helpers/ui.py b/iz_helpers/ui.py index d1d52c3..afb0e12 100644 --- a/iz_helpers/ui.py +++ b/iz_helpers/ui.py @@ -28,6 +28,15 @@ def on_ui_tabs(): with gr.Row(): with gr.Column(scale=1, variant="panel"): with gr.Tab("Main"): + + batchcount_slider = gr.Slider( + minimum=1, + maximum=25, + value=shared.opts.data.get("infzoom_batchcount", 1), + step=1, + label="Batch Count", + ) + main_outpaint_steps = gr.Slider( minimum=2, maximum=100, @@ -41,111 +50,111 @@ def on_ui_tabs(): pr = shared.opts.data.get("infzoom_defPrompt", default_prompt) jpr = readJsonPrompt(pr, True) - main_common_prompt = gr.Textbox( - value=jpr["commonPrompt"], label="Common Prompt" - ) + with gr.Accordion(label="Prompt Section"): + main_common_prompt_pre = gr.Textbox( + value=jpr["commonPromptPrefix"], label="Common Prompt Prefix" + ) - main_prompts = gr.Dataframe( - type="array", - headers=["outpaint step", "prompt"], - datatype=["number", "str"], - row_count=1, - col_count=(2, "fixed"), - value=jpr["prompts"], - wrap=True, - ) + main_prompts = gr.Dataframe( + type="array", + headers=["outpaint step", "prompt"], + datatype=["number", "str"], + row_count=1, + col_count=(2, "fixed"), + value=jpr["prompts"], + wrap=True, + ) - main_negative_prompt = gr.Textbox( - value=jpr["negPrompt"], label="Negative Prompt" - ) + main_common_prompt_suf = gr.Textbox( + value=jpr["commonPromptSuffix"], label="Common Prompt Suffix" + ) - # these button will be moved using JS unde the dataframe view as small ones - exportPrompts_button = gr.Button( - value="Export prompts", - variant="secondary", - elem_classes="sm infzoom_tab_butt", - elem_id="infzoom_exP_butt", - ) - importPrompts_button = gr.UploadButton( - label="Import prompts", - variant="secondary", - elem_classes="sm infzoom_tab_butt", - elem_id="infzoom_imP_butt", - ) - exportPrompts_button.click( - None, - _js="exportPrompts", - inputs=[main_prompts, main_negative_prompt], - outputs=None - ) - importPrompts_button.upload( - fn=putPrompts, - outputs=[main_prompts, main_negative_prompt], - inputs=[importPrompts_button], - ) + main_negative_prompt = gr.Textbox( + value=jpr["negPrompt"], label="Negative Prompt" + ) - clearPrompts_button = gr.Button( - value="Clear prompts", - variant="secondary", - elem_classes="sm infzoom_tab_butt", - elem_id="infzoom_clP_butt", - ) - clearPrompts_button.click( - fn=clearPrompts, - inputs=[], - outputs=[main_prompts, main_negative_prompt, main_common_prompt], - ) - with gr.Row(): - seed = gr.Number( - label="Seed", value=-1, precision=0, interactive=True + # these button will be moved using JS unde the dataframe view as small ones + exportPrompts_button = gr.Button( + value="Export prompts", + variant="secondary", + elem_classes="sm infzoom_tab_butt", + elem_id="infzoom_exP_butt", ) - main_sampler = gr.Dropdown( - label="Sampler", - choices=available_samplers, - value="Euler a", - type="value", + importPrompts_button = gr.UploadButton( + label="Import prompts", + variant="secondary", + elem_classes="sm infzoom_tab_butt", + elem_id="infzoom_imP_butt", ) - with gr.Row(): - main_width = gr.Slider( - minimum=16, - maximum=2048, - value=shared.opts.data.get("infzoom_outsizeW", 512), - step=16, - label="Output Width", + exportPrompts_button.click( + None, + _js="exportPrompts", + inputs=[main_common_prompt_pre, main_prompts, main_common_prompt_suf, main_negative_prompt], + outputs=None ) - main_height = gr.Slider( - minimum=16, - maximum=2048, - value=shared.opts.data.get("infzoom_outsizeH", 512), - step=16, - label="Output Height", + importPrompts_button.upload( + fn=putPrompts, + outputs=[main_common_prompt_pre, main_prompts,main_common_prompt_suf , main_negative_prompt], + inputs=[importPrompts_button], ) - with gr.Row(): - main_guidance_scale = gr.Slider( - minimum=0.1, - maximum=15, - step=0.1, - value=7, - label="Guidance Scale", - ) - sampling_step = gr.Slider( - minimum=1, - maximum=100, - step=1, - value=50, - label="Sampling Steps for each outpaint", - ) - with gr.Row(): - init_image = gr.Image(type="pil", label="custom initial image") - exit_image = gr.Image(type="pil", label="custom exit image") - batchcount_slider = gr.Slider( - minimum=1, - maximum=25, - value=shared.opts.data.get("infzoom_batchcount", 1), - step=1, - label="Batch Count", - ) + clearPrompts_button = gr.Button( + value="Clear prompts", + variant="secondary", + elem_classes="sm infzoom_tab_butt", + elem_id="infzoom_clP_butt", + ) + clearPrompts_button.click( + fn=clearPrompts, + inputs=[], + outputs=[main_prompts, main_negative_prompt, main_common_prompt_pre, main_common_prompt_suf], + ) + + with gr.Accordion("Render settings"): + with gr.Row(): + seed = gr.Number( + label="Seed", value=-1, precision=0, interactive=True + ) + main_sampler = gr.Dropdown( + label="Sampler", + choices=available_samplers, + value="Euler a", + type="value", + ) + with gr.Row(): + main_width = gr.Slider( + minimum=16, + maximum=2048, + value=shared.opts.data.get("infzoom_outsizeW", 512), + step=16, + label="Output Width", + ) + main_height = gr.Slider( + minimum=16, + maximum=2048, + value=shared.opts.data.get("infzoom_outsizeH", 512), + step=16, + label="Output Height", + ) + with gr.Row(): + main_guidance_scale = gr.Slider( + minimum=0.1, + maximum=15, + step=0.1, + value=7, + label="Guidance Scale", + ) + sampling_step = gr.Slider( + minimum=1, + maximum=100, + step=1, + value=50, + label="Sampling Steps for each outpaint", + ) + with gr.Row(): + init_image = gr.Image(type="pil", label="Custom initial image") + exit_image = gr.Image(type="pil", label="Custom exit image") + with gr.Tab("Video"): video_frame_rate = gr.Slider( label="Frames per second", @@ -233,8 +242,9 @@ Our best experience and trade-off is the R-ERSGAn4x upscaler. generate_btn.click( fn=wrap_gradio_gpu_call(create_zoom, extra_outputs=[None, "", ""]), inputs=[ - main_common_prompt, + main_common_prompt_pre, main_prompts, + main_common_prompt_suf, main_negative_prompt, main_outpaint_steps, main_guidance_scale, diff --git a/javascript/infinite-zoom-hints.js b/javascript/infinite-zoom-hints.js new file mode 100644 index 0000000..d54d604 --- /dev/null +++ b/javascript/infinite-zoom-hints.js @@ -0,0 +1,50 @@ +// mouseover tooltips for various UI elements + +infzoom_titles = { + "Batch Count":"How many separate videos to create", + "Total Outpaint Steps":"Each step generates frame for 1 second at your FPS, while cycling through your array of prompts", + "Common Prompt Prefix":"Prompt inserted before each step", + "Common Prompt Suffix":"Prompt inserted after each step", + "Negative Prompt":"What your model shall avoid", + "Export prompts": "Downloads a JSON file to save all prompts", + "Import prompts": "Restore Prompts table from a specific JSON file", + "Clear prompts": "Start over, remove all entries from prompt table, prefix, suffix, negative", + "Custom initial image":"An image at the end resp. begin of your movie, depending or ZoomIn or Out", + "Custom exit image":"An image at the end resp. begin of your movie, depending or ZoomIn or Out", + "Zoom Speed":"Varies additional frames per second", + + + +} + + +onUiUpdate(function(){ + gradioApp().querySelectorAll('span, button, select, p').forEach(function(span){ + tooltip = infzoom_titles[span.textContent]; + + if(!tooltip){ + tooltip = infzoom_titles[span.value]; + } + + if(!tooltip){ + for (const c of span.classList) { + if (c in infzoom_titles) { + tooltip = infzoom_titles[c]; + break; + } + } + } + + if(tooltip){ + span.title = tooltip; + } + }) + + gradioApp().querySelectorAll('select').forEach(function(select){ + if (select.onchange != null) return; + + select.onchange = function(){ + select.title = infzoom_titles[select.value] || ""; + } + }) +}) diff --git a/javascript/infinite-zoom.js b/javascript/infinite-zoom.js index 0a41d0c..834d8fb 100644 --- a/javascript/infinite-zoom.js +++ b/javascript/infinite-zoom.js @@ -1,7 +1,7 @@ // Function to download data to a file -function exportPrompts(cp,p, np, filename = "infinite-zoom-prompts.json") { +function exportPrompts(cppre,p, cpsuf,np, filename = "infinite-zoom-prompts.json") { - let J = { prompts: p, negPrompt: np, commonPrompt: cp } + let J = { prompts: p, negPrompt: np, commonPromptPrefix: cppre, commonPromptSuffix: cpsuf } var file = new Blob([JSON.stringify(J)], { type: "text/csv" }); if (window.navigator.msSaveOrOpenBlob) // IE10+ From 00b2eed6e722f66355cd2862394bc88f75946913 Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Wed, 26 Apr 2023 06:16:57 +0200 Subject: [PATCH 14/44] slightly layout improves, hinting --- iz_helpers/ui.py | 138 +++++++++++++++--------------- javascript/infinite-zoom-hints.js | 2 +- 2 files changed, 69 insertions(+), 71 deletions(-) diff --git a/iz_helpers/ui.py b/iz_helpers/ui.py index afb0e12..c67a53a 100644 --- a/iz_helpers/ui.py +++ b/iz_helpers/ui.py @@ -28,87 +28,85 @@ def on_ui_tabs(): with gr.Row(): with gr.Column(scale=1, variant="panel"): with gr.Tab("Main"): + with gr.Row(): + batchcount_slider = gr.Slider( + minimum=1, + maximum=25, + value=shared.opts.data.get("infzoom_batchcount", 1), + step=1, + label="Batch Count", + ) - batchcount_slider = gr.Slider( - minimum=1, - maximum=25, - value=shared.opts.data.get("infzoom_batchcount", 1), - step=1, - label="Batch Count", - ) - - main_outpaint_steps = gr.Slider( - minimum=2, - maximum=100, - step=1, - value=8, - label="Total Outpaint Steps", - info="The more it is, the longer your videos will be", - ) + main_outpaint_steps = gr.Slider( + minimum=2, + maximum=100, + step=1, + value=8, + label="Total Outpaint Steps" + ) # safe reading json prompt pr = shared.opts.data.get("infzoom_defPrompt", default_prompt) jpr = readJsonPrompt(pr, True) - with gr.Accordion(label="Prompt Section"): - main_common_prompt_pre = gr.Textbox( - value=jpr["commonPromptPrefix"], label="Common Prompt Prefix" - ) + main_common_prompt_pre = gr.Textbox( + value=jpr["commonPromptPrefix"], label="Common Prompt Prefix" + ) - main_prompts = gr.Dataframe( - type="array", - headers=["outpaint step", "prompt"], - datatype=["number", "str"], - row_count=1, - col_count=(2, "fixed"), - value=jpr["prompts"], - wrap=True, - ) + main_prompts = gr.Dataframe( + type="array", + headers=["outpaint step", "prompt"], + datatype=["number", "str"], + row_count=1, + col_count=(2, "fixed"), + value=jpr["prompts"], + wrap=True, + ) - main_common_prompt_suf = gr.Textbox( - value=jpr["commonPromptSuffix"], label="Common Prompt Suffix" - ) + main_common_prompt_suf = gr.Textbox( + value=jpr["commonPromptSuffix"], label="Common Prompt Suffix" + ) - main_negative_prompt = gr.Textbox( - value=jpr["negPrompt"], label="Negative Prompt" - ) + main_negative_prompt = gr.Textbox( + value=jpr["negPrompt"], label="Negative Prompt" + ) - # these button will be moved using JS unde the dataframe view as small ones - exportPrompts_button = gr.Button( - value="Export prompts", - variant="secondary", - elem_classes="sm infzoom_tab_butt", - elem_id="infzoom_exP_butt", - ) - importPrompts_button = gr.UploadButton( - label="Import prompts", - variant="secondary", - elem_classes="sm infzoom_tab_butt", - elem_id="infzoom_imP_butt", - ) - exportPrompts_button.click( - None, - _js="exportPrompts", - inputs=[main_common_prompt_pre, main_prompts, main_common_prompt_suf, main_negative_prompt], - outputs=None - ) - importPrompts_button.upload( - fn=putPrompts, - outputs=[main_common_prompt_pre, main_prompts,main_common_prompt_suf , main_negative_prompt], - inputs=[importPrompts_button], - ) + # these button will be moved using JS unde the dataframe view as small ones + exportPrompts_button = gr.Button( + value="Export prompts", + variant="secondary", + elem_classes="sm infzoom_tab_butt", + elem_id="infzoom_exP_butt", + ) + importPrompts_button = gr.UploadButton( + label="Import prompts", + variant="secondary", + elem_classes="sm infzoom_tab_butt", + elem_id="infzoom_imP_butt", + ) + exportPrompts_button.click( + None, + _js="exportPrompts", + inputs=[main_common_prompt_pre, main_prompts, main_common_prompt_suf, main_negative_prompt], + outputs=None + ) + importPrompts_button.upload( + fn=putPrompts, + outputs=[main_common_prompt_pre, main_prompts,main_common_prompt_suf , main_negative_prompt], + inputs=[importPrompts_button], + ) - clearPrompts_button = gr.Button( - value="Clear prompts", - variant="secondary", - elem_classes="sm infzoom_tab_butt", - elem_id="infzoom_clP_butt", - ) - clearPrompts_button.click( - fn=clearPrompts, - inputs=[], - outputs=[main_prompts, main_negative_prompt, main_common_prompt_pre, main_common_prompt_suf], - ) + clearPrompts_button = gr.Button( + value="Clear prompts", + variant="secondary", + elem_classes="sm infzoom_tab_butt", + elem_id="infzoom_clP_butt", + ) + clearPrompts_button.click( + fn=clearPrompts, + inputs=[], + outputs=[main_prompts, main_negative_prompt, main_common_prompt_pre, main_common_prompt_suf], + ) with gr.Accordion("Render settings"): with gr.Row(): diff --git a/javascript/infinite-zoom-hints.js b/javascript/infinite-zoom-hints.js index d54d604..ef7be89 100644 --- a/javascript/infinite-zoom-hints.js +++ b/javascript/infinite-zoom-hints.js @@ -2,7 +2,7 @@ infzoom_titles = { "Batch Count":"How many separate videos to create", - "Total Outpaint Steps":"Each step generates frame for 1 second at your FPS, while cycling through your array of prompts", + "Total Outpaint Steps":"The more it is, the longer your videos will be. Each step generates frame for 1 second at your FPS, while cycling through your array of prompts", "Common Prompt Prefix":"Prompt inserted before each step", "Common Prompt Suffix":"Prompt inserted after each step", "Negative Prompt":"What your model shall avoid", From 18832be8cefc136b34f9f0188461a274d4311c34 Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Thu, 27 Apr 2023 02:04:35 +0200 Subject: [PATCH 15/44] exporting JSON with pretty printing --- javascript/infinite-zoom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/infinite-zoom.js b/javascript/infinite-zoom.js index 834d8fb..f4c1692 100644 --- a/javascript/infinite-zoom.js +++ b/javascript/infinite-zoom.js @@ -3,7 +3,7 @@ function exportPrompts(cppre,p, cpsuf,np, filename = "infinite-zoom-prompts.json let J = { prompts: p, negPrompt: np, commonPromptPrefix: cppre, commonPromptSuffix: cpsuf } - var file = new Blob([JSON.stringify(J)], { type: "text/csv" }); + var file = new Blob([JSON.stringify(J,null,2)], { type: "text/csv" }); if (window.navigator.msSaveOrOpenBlob) // IE10+ window.navigator.msSaveOrOpenBlob(file, filename); else { // Others From 90dbc3b2f0922ecc4c9735340f5204151a688b46 Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Thu, 27 Apr 2023 04:22:40 +0200 Subject: [PATCH 16/44] added setting to collect all images etc into one folder. fixed default output path folder in settings default --- iz_helpers/run.py | 72 ++++++++++++++++++++++++++++++++-------- iz_helpers/settings.py | 23 ++++++++++--- scripts/infinite-zoom.py | 3 +- 3 files changed, 80 insertions(+), 18 deletions(-) diff --git a/iz_helpers/run.py b/iz_helpers/run.py index ed15bfd..fcb05d3 100644 --- a/iz_helpers/run.py +++ b/iz_helpers/run.py @@ -14,7 +14,6 @@ from .sd_helpers import renderImg2Img, renderTxt2Img from .image import shrink_and_paste_on_blank from .video import write_video - def create_zoom( common_prompt_pre, prompts_array, @@ -79,6 +78,40 @@ def create_zoom( return result + +def prepare_output_path(): + + isCollect = shared.opts.data.get("infzoom_collectAllResources",False) + output_path = shared.opts.data.get( + "infzoom_outpath", "output" + ) + + save_path = os.path.join( + output_path, shared.opts.data.get("infzoom_outSUBpath", "infinite-zooms") + ) + + if isCollect: + save_path = os.path.join(save_path,"iz_collect" + str(int(time.time()))) + + if not os.path.exists(save_path): + os.makedirs(save_path) + + video_filename = os.path.join(save_path,"infinite_zoom_" + str(int(time.time())) + ".mp4") + + return {"isCollect":isCollect,"save_path":save_path,"video_filename":video_filename} + + +def save2Collect(img, out_config, name): + if out_config["isCollect"]: + img.save(f'{out_config["save_path"]}/{name}.png') + +def frame2Collect(all_frames, out_config): + save2Collect(all_frames[-1], out_config, f"frame_{len(all_frames)}.png") + +def frames2Collect(all_frames, out_config): + for i,f in enumerate(all_frames): + save2Collect(f, out_config, f"frame_{i}.png") + def create_zoom_single( common_prompt_pre, prompts_array, @@ -116,7 +149,10 @@ def create_zoom_single( # pass fix_env_Path_ffprobe() + out_config = prepare_output_path() + prompts = {} + for x in prompts_array: try: key = int(x[0]) @@ -124,6 +160,7 @@ def create_zoom_single( prompts[key] = value except ValueError: pass + assert len(prompts_array) > 0, "prompts is empty" width = closest_upper_divisible_by_eight(outputsizeW) @@ -139,7 +176,9 @@ def create_zoom_single( current_image = custom_init_image.resize( (width, height), resample=Image.LANCZOS ) + save2Collect(current_image, out_config, f"init_img.png") print("using Custom Initial Image") + else: load_model_from_setting( "infzoom_txt2img_model", progress, "Loading Model for txt2img: " @@ -158,6 +197,8 @@ def create_zoom_single( ) if(len(processed.images) > 0): current_image = processed.images[0] + save2Collect(current_image, out_config, f"txt2img.png") + current_seed = newseed mask_width = math.trunc(width / 4) # was initially 512px => 128px @@ -194,12 +235,18 @@ def create_zoom_single( progress(((i + 1) / num_outpainting_steps), desc=print_out) prev_image_fix = current_image + save2Collect(prev_image_fix, out_config, f"prev_image_fix_{i}.png") + prev_image = shrink_and_paste_on_blank(current_image, mask_width, mask_height) + save2Collect(prev_image, out_config, f"prev_image_{1}.png") + current_image = prev_image # create mask (black image with white mask_width width edges) mask_image = np.array(current_image)[:, :, 3] mask_image = Image.fromarray(255 - mask_image).convert("RGB") + save2Collect(mask_image, out_config, f"mask_image_{i}.png") + # inpainting step current_image = current_image.convert("RGB") @@ -209,6 +256,7 @@ def create_zoom_single( (width, height), resample=Image.LANCZOS ) print("using Custom Exit Image") + save2Collect(current_image, out_config, f"exit_img.png") else: pr = prompts[max(k for k in prompts.keys() if k <= i)] processed, newseed = renderImg2Img( @@ -233,10 +281,12 @@ def create_zoom_single( current_seed = newseed if(len(processed.images) > 0): current_image.paste(prev_image, mask=prev_image) + save2Collect(current_image, out_config, f"curr_prev_paste_{i}.png") # interpolation steps between 2 inpainted images (=sequential zoom and crop) for j in range(num_interpol_frames - 1): interpol_image = current_image + save2Collect(interpol_image, out_config, f"interpol_img_{i}_{j}].png") interpol_width = round( ( @@ -266,8 +316,10 @@ def create_zoom_single( height - interpol_height, ) ) + save2Collect(interpol_image, out_config, f"interpol_crop_{i}_{j}.png") interpol_image = interpol_image.resize((width, height)) + save2Collect(interpol_image, out_config, f"interpol_resize_{i}_{j}.png") # paste the higher resolution previous image in the middle to avoid drop in quality caused by zooming interpol_width2 = round( @@ -285,8 +337,10 @@ def create_zoom_single( prev_image_fix_crop = shrink_and_paste_on_blank( prev_image_fix, interpol_width2, interpol_height2 ) + save2Collect(prev_image_fix, out_config, f"prev_image_fix_crop_{i}_{j}.png") interpol_image.paste(prev_image_fix_crop, mask=prev_image_fix_crop) + save2Collect(interpol_image, out_config, f"interpol_prevcrop_{i}_{j}.png") if upscale_do and progress: progress(((i + 1) / num_outpainting_steps), desc="upscaling interpol") @@ -306,18 +360,10 @@ def create_zoom_single( else current_image ) - video_file_name = "infinite_zoom_" + str(int(time.time())) + ".mp4" - output_path = shared.opts.data.get( - "infzoom_outpath", shared.opts.data.get("outdir_img2img_samples") - ) - save_path = os.path.join( - output_path, shared.opts.data.get("infzoom_outSUBpath", "infinite-zooms") - ) - if not os.path.exists(save_path): - os.makedirs(save_path) - out = os.path.join(save_path, video_file_name) + frames2Collect(all_frames, out_config) + write_video( - out, + out_config["video_filename"], all_frames, video_frame_rate, video_zoom_mode, @@ -326,7 +372,7 @@ def create_zoom_single( ) return ( - out, + out_config["video_filename"], processed.images, processed.js(), plaintext_to_html(processed.info), diff --git a/iz_helpers/settings.py b/iz_helpers/settings.py index 8c6af78..4338526 100644 --- a/iz_helpers/settings.py +++ b/iz_helpers/settings.py @@ -1,19 +1,19 @@ +import gradio as gr import modules.shared as shared from .static_variables import default_prompt -import gradio as gr def on_ui_settings(): section = ("infinite-zoom", "Infinite Zoom") shared.opts.add_option( - "outputs" "infzoom_outpath", + "infzoom_outpath", shared.OptionInfo( - "", + "outputs", "Path where to store your infinite video. Default is Outputs", gr.Textbox, {"interactive": True}, - section=section, + section=section, ), ) @@ -93,3 +93,18 @@ def on_ui_settings(): section=section, ), ) + + + shared.opts.add_option( + "infzoom_collectAllResources", + shared.OptionInfo( + False, + "Store all images (txt2img, init_image,exit_image, inpainting, interpolation) and the movie into one folder in your OUTPUT Path", + gr.Checkbox, + {"interactive": True}, + section=section, + ), + ) + + + diff --git a/scripts/infinite-zoom.py b/scripts/infinite-zoom.py index 1aaadab..50892d1 100644 --- a/scripts/infinite-zoom.py +++ b/scripts/infinite-zoom.py @@ -1,6 +1,7 @@ -from iz_helpers import on_ui_tabs, on_ui_settings from modules import script_callbacks +from iz_helpers import on_ui_tabs, on_ui_settings script_callbacks.on_ui_tabs(on_ui_tabs) script_callbacks.on_ui_settings(on_ui_settings) + From e41d103791e394f17f7a029f347ece58f640beb8 Mon Sep 17 00:00:00 2001 From: "vahid K. nejad" Date: Thu, 27 Apr 2023 12:36:04 +0400 Subject: [PATCH 17/44] fixing import --- iz_helpers/__init__.py | 4 ++-- scripts/infinite-zoom.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/iz_helpers/__init__.py b/iz_helpers/__init__.py index 076e90a..19c0211 100644 --- a/iz_helpers/__init__.py +++ b/iz_helpers/__init__.py @@ -1,2 +1,2 @@ -from .ui import on_ui_tabs -from .settings import on_ui_settings +# from .ui import on_ui_tabs +# from .settings import on_ui_settings diff --git a/scripts/infinite-zoom.py b/scripts/infinite-zoom.py index 50892d1..a68461f 100644 --- a/scripts/infinite-zoom.py +++ b/scripts/infinite-zoom.py @@ -1,7 +1,7 @@ from modules import script_callbacks -from iz_helpers import on_ui_tabs, on_ui_settings +from iz_helpers.ui import on_ui_tabs +from iz_helpers.settings import on_ui_settings script_callbacks.on_ui_tabs(on_ui_tabs) script_callbacks.on_ui_settings(on_ui_settings) - From 44114c225240dd2ddceff94a21920af2e78a94ed Mon Sep 17 00:00:00 2001 From: "vahid K. nejad" Date: Thu, 27 Apr 2023 14:19:53 +0400 Subject: [PATCH 18/44] hiding exit image for now --- iz_helpers/ui.py | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/iz_helpers/ui.py b/iz_helpers/ui.py index c67a53a..0b7b998 100644 --- a/iz_helpers/ui.py +++ b/iz_helpers/ui.py @@ -42,7 +42,7 @@ def on_ui_tabs(): maximum=100, step=1, value=8, - label="Total Outpaint Steps" + label="Total Outpaint Steps", ) # safe reading json prompt @@ -87,12 +87,22 @@ def on_ui_tabs(): exportPrompts_button.click( None, _js="exportPrompts", - inputs=[main_common_prompt_pre, main_prompts, main_common_prompt_suf, main_negative_prompt], - outputs=None + inputs=[ + main_common_prompt_pre, + main_prompts, + main_common_prompt_suf, + main_negative_prompt, + ], + outputs=None, ) importPrompts_button.upload( fn=putPrompts, - outputs=[main_common_prompt_pre, main_prompts,main_common_prompt_suf , main_negative_prompt], + outputs=[ + main_common_prompt_pre, + main_prompts, + main_common_prompt_suf, + main_negative_prompt, + ], inputs=[importPrompts_button], ) @@ -105,10 +115,15 @@ def on_ui_tabs(): clearPrompts_button.click( fn=clearPrompts, inputs=[], - outputs=[main_prompts, main_negative_prompt, main_common_prompt_pre, main_common_prompt_suf], + outputs=[ + main_prompts, + main_negative_prompt, + main_common_prompt_pre, + main_common_prompt_suf, + ], ) - with gr.Accordion("Render settings"): + with gr.Accordion("Render settings"): with gr.Row(): seed = gr.Number( label="Seed", value=-1, precision=0, interactive=True @@ -150,9 +165,12 @@ def on_ui_tabs(): label="Sampling Steps for each outpaint", ) with gr.Row(): - init_image = gr.Image(type="pil", label="Custom initial image") - exit_image = gr.Image(type="pil", label="Custom exit image") - + init_image = gr.Image( + type="pil", label="Custom initial image" + ) + exit_image = gr.Image( + type="pil", label="Custom exit image", visible=False + ) with gr.Tab("Video"): video_frame_rate = gr.Slider( label="Frames per second", From f48d29b1beb17c9887ee5541f8a15ff64af9e647 Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Fri, 28 Apr 2023 03:22:54 +0200 Subject: [PATCH 19/44] filtering in settings paint/inpainting models in dropdown --- iz_helpers/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iz_helpers/settings.py b/iz_helpers/settings.py index 4338526..e201588 100644 --- a/iz_helpers/settings.py +++ b/iz_helpers/settings.py @@ -67,7 +67,7 @@ def on_ui_settings(): None, "Name of your desired model to render keyframes (txt2img)", gr.Dropdown, - lambda: {"choices": shared.list_checkpoint_tiles()}, + lambda: {"choices": [x for x in list(shared.list_checkpoint_tiles()) if "inpainting" not in x]}, section=section, ), ) @@ -78,7 +78,7 @@ def on_ui_settings(): None, "Name of your desired inpaint model (img2img-inpaint). Default is vanilla sd-v1-5-inpainting.ckpt ", gr.Dropdown, - lambda: {"choices": shared.list_checkpoint_tiles()}, + lambda: {"choices": [x for x in list(shared.list_checkpoint_tiles()) if "inpainting" in x]}, section=section, ), ) From bb7d7082d7bb50c53958361c3c72437d527e44b7 Mon Sep 17 00:00:00 2001 From: "vahid K. nejad" Date: Fri, 28 Apr 2023 06:58:56 +0400 Subject: [PATCH 20/44] Frame correction starting point --- iz_helpers/run.py | 98 +++++++++++++++++++++++++++++++---------------- 1 file changed, 64 insertions(+), 34 deletions(-) diff --git a/iz_helpers/run.py b/iz_helpers/run.py index fcb05d3..bfdae03 100644 --- a/iz_helpers/run.py +++ b/iz_helpers/run.py @@ -14,6 +14,7 @@ from .sd_helpers import renderImg2Img, renderTxt2Img from .image import shrink_and_paste_on_blank from .video import write_video + def create_zoom( common_prompt_pre, prompts_array, @@ -78,40 +79,65 @@ def create_zoom( return result - def prepare_output_path(): + isCollect = shared.opts.data.get("infzoom_collectAllResources", False) + output_path = shared.opts.data.get("infzoom_outpath", "output") - isCollect = shared.opts.data.get("infzoom_collectAllResources",False) - output_path = shared.opts.data.get( - "infzoom_outpath", "output" - ) - save_path = os.path.join( output_path, shared.opts.data.get("infzoom_outSUBpath", "infinite-zooms") ) if isCollect: - save_path = os.path.join(save_path,"iz_collect" + str(int(time.time()))) + save_path = os.path.join(save_path, "iz_collect" + str(int(time.time()))) if not os.path.exists(save_path): os.makedirs(save_path) - video_filename = os.path.join(save_path,"infinite_zoom_" + str(int(time.time())) + ".mp4") + video_filename = os.path.join( + save_path, "infinite_zoom_" + str(int(time.time())) + ".mp4" + ) - return {"isCollect":isCollect,"save_path":save_path,"video_filename":video_filename} - + return { + "isCollect": isCollect, + "save_path": save_path, + "video_filename": video_filename, + } -def save2Collect(img, out_config, name): + +def save2Collect(img, out_config, name): if out_config["isCollect"]: img.save(f'{out_config["save_path"]}/{name}.png') + def frame2Collect(all_frames, out_config): save2Collect(all_frames[-1], out_config, f"frame_{len(all_frames)}.png") + def frames2Collect(all_frames, out_config): - for i,f in enumerate(all_frames): + for i, f in enumerate(all_frames): save2Collect(f, out_config, f"frame_{i}.png") - + + +def crop_inner_image(outpainted_img, width_offset, height_offset): + width, height = outpainted_img.size + + center_x, center_y = int(width / 2), int(height / 2) + + # Crop the image to the center + cropped_img = outpainted_img.crop( + ( + center_x - width_offset, + center_y - height_offset, + center_x + width_offset, + center_y + height_offset, + ) + ) + prev_step_img = cropped_img.resize((width, height), resample=Image.LANCZOS) + # resized_img = resized_img.filter(ImageFilter.SHARPEN) + + return prev_step_img + + def create_zoom_single( common_prompt_pre, prompts_array, @@ -148,11 +174,10 @@ def create_zoom_single( # except Exception: # pass fix_env_Path_ffprobe() - out_config = prepare_output_path() prompts = {} - + for x in prompts_array: try: key = int(x[0]) @@ -160,7 +185,7 @@ def create_zoom_single( prompts[key] = value except ValueError: pass - + assert len(prompts_array) > 0, "prompts is empty" width = closest_upper_divisible_by_eight(outputsizeW) @@ -195,10 +220,9 @@ def create_zoom_single( width, height, ) - if(len(processed.images) > 0): + if len(processed.images) > 0: current_image = processed.images[0] save2Collect(current_image, out_config, f"txt2img.png") - current_seed = newseed mask_width = math.trunc(width / 4) # was initially 512px => 128px @@ -211,12 +235,6 @@ def create_zoom_single( if upscale_do and progress: progress(0, desc="upscaling inital image") - all_frames.append( - do_upscaleImg(current_image, upscale_do, upscaler_name, upscale_by) - if upscale_do - else current_image - ) - load_model_from_setting( "infzoom_inpainting_model", progress, "Loading Model for inpainting/img2img: " ) @@ -238,7 +256,7 @@ def create_zoom_single( save2Collect(prev_image_fix, out_config, f"prev_image_fix_{i}.png") prev_image = shrink_and_paste_on_blank(current_image, mask_width, mask_height) - save2Collect(prev_image, out_config, f"prev_image_{1}.png") + save2Collect(prev_image, out_config, f"prev_image_{i}.png") current_image = prev_image @@ -247,7 +265,6 @@ def create_zoom_single( mask_image = Image.fromarray(255 - mask_image).convert("RGB") save2Collect(mask_image, out_config, f"mask_image_{i}.png") - # inpainting step current_image = current_image.convert("RGB") @@ -276,13 +293,26 @@ def create_zoom_single( inpainting_full_res, inpainting_padding, ) - if(len(processed.images) > 0): + if len(processed.images) > 0: current_image = processed.images[0] current_seed = newseed - if(len(processed.images) > 0): - current_image.paste(prev_image, mask=prev_image) + if len(processed.images) > 0: + # current_image.paste(prev_image, mask=prev_image) save2Collect(current_image, out_config, f"curr_prev_paste_{i}.png") - + if True or i > 0: + correction_crop = crop_inner_image(current_image, mask_width, mask_height) + prev_image_fix = correction_crop + # paste_x = (current_image.width - prev_image.width) // 2 + # paste_y = (current_image.height - prev_image.height) // 2 + # current_image.paste(prev_image, (paste_x, paste_y), mask=prev_image) + # replace the prev frame with current croped + all_frames.append( + do_upscaleImg( + prev_image_fix.convert("RGB"), upscale_do, upscaler_name, upscale_by + ) + if upscale_do + else prev_image_fix.convert("RGB") + ) # interpolation steps between 2 inpainted images (=sequential zoom and crop) for j in range(num_interpol_frames - 1): interpol_image = current_image @@ -316,10 +346,10 @@ def create_zoom_single( height - interpol_height, ) ) - save2Collect(interpol_image, out_config, f"interpol_crop_{i}_{j}.png") + # save2Collect(interpol_image, out_config, f"interpol_crop_{i}_{j}.png") interpol_image = interpol_image.resize((width, height)) - save2Collect(interpol_image, out_config, f"interpol_resize_{i}_{j}.png") + # save2Collect(interpol_image, out_config, f"interpol_resize_{i}_{j}.png") # paste the higher resolution previous image in the middle to avoid drop in quality caused by zooming interpol_width2 = round( @@ -337,10 +367,10 @@ def create_zoom_single( prev_image_fix_crop = shrink_and_paste_on_blank( prev_image_fix, interpol_width2, interpol_height2 ) - save2Collect(prev_image_fix, out_config, f"prev_image_fix_crop_{i}_{j}.png") + # save2Collect(prev_image_fix, out_config, f"prev_image_fix_crop_{i}_{j}.png") interpol_image.paste(prev_image_fix_crop, mask=prev_image_fix_crop) - save2Collect(interpol_image, out_config, f"interpol_prevcrop_{i}_{j}.png") + # save2Collect(interpol_image, out_config, f"interpol_prevcrop_{i}_{j}.png") if upscale_do and progress: progress(((i + 1) / num_outpainting_steps), desc="upscaling interpol") From e72cfcd33d5a89c7ec3ea7d0b02ad05cc0ba9101 Mon Sep 17 00:00:00 2001 From: "vahid K. nejad" Date: Fri, 28 Apr 2023 12:28:17 +0400 Subject: [PATCH 21/44] Seperating the outpaint and interpolation, and apply frame correction --- iz_helpers/run.py | 215 ++++++++++++++++++++++++++++------------------ 1 file changed, 133 insertions(+), 82 deletions(-) diff --git a/iz_helpers/run.py b/iz_helpers/run.py index bfdae03..6e1d034 100644 --- a/iz_helpers/run.py +++ b/iz_helpers/run.py @@ -15,6 +15,99 @@ from .image import shrink_and_paste_on_blank from .video import write_video +def outpaint_steps( + width, + height, + common_prompt_pre, + common_prompt_suf, + prompts, + negative_prompt, + seed, + sampler, + num_inference_steps, + guidance_scale, + inpainting_denoising_strength, + inpainting_mask_blur, + inpainting_fill_mode, + inpainting_full_res, + inpainting_padding, + init_img, + outpaint_steps, + out_config, + mask_width, + mask_height, + custom_exit_image, + frame_correction=True, +): + main_frames = [init_img.convert("RGB")] + for i in range(outpaint_steps): + print_out = ( + "Outpaint step: " + + str(i + 1) + + " / " + + str(outpaint_steps) + + " Seed: " + + str(seed) + ) + print(print_out) + current_image = main_frames[-1] + prev_image_fix = current_image + # save2Collect(prev_image_fix, out_config, f"prev_image_fix_{i}.png") + + prev_image = shrink_and_paste_on_blank(current_image, mask_width, mask_height) + # save2Collect(prev_image, out_config, f"prev_image_{i}.png") + + current_image = prev_image + + # create mask (black image with white mask_width width edges) + mask_image = np.array(current_image)[:, :, 3] + mask_image = Image.fromarray(255 - mask_image).convert("RGB") + # save2Collect(mask_image, out_config, f"mask_image_{i}.png") + + if custom_exit_image and ((i + 1) == outpaint_steps): + current_image = custom_exit_image.resize( + (width, height), resample=Image.LANCZOS + ) + main_frames.append(current_image.convert("RGB")) + print("using Custom Exit Image") + # save2Collect(current_image, out_config, f"exit_img.png") + else: + pr = prompts[max(k for k in prompts.keys() if k <= i)] + processed, newseed = renderImg2Img( + f"{common_prompt_pre}\n{pr}\n{common_prompt_suf}".strip(), + negative_prompt, + sampler, + num_inference_steps, + guidance_scale, + seed, + width, + height, + current_image, + mask_image, + inpainting_denoising_strength, + inpainting_mask_blur, + inpainting_fill_mode, + inpainting_full_res, + inpainting_padding, + ) + if len(processed.images) > 0: + main_frames.append(processed.images[0].convert("RGB")) + seed = newseed + if frame_correction and inpainting_mask_blur > 0: + corrected_frame = crop_inner_image( + main_frames[i + 1], mask_width, mask_height + ) + save2Collect(current_image, out_config, f"corrected_{i}") + main_frames[i] = corrected_frame + + # else TEST pasting differance + # current_image.paste(prev_image, mask=prev_image) + frames2Collect(main_frames, out_config) + print(out_config) + print("length: ", len(main_frames)) + return main_frames + + def create_zoom( common_prompt_pre, prompts_array, @@ -110,12 +203,12 @@ def save2Collect(img, out_config, name): def frame2Collect(all_frames, out_config): - save2Collect(all_frames[-1], out_config, f"frame_{len(all_frames)}.png") + save2Collect(all_frames[-1], out_config, f"frame_{len(all_frames)}") def frames2Collect(all_frames, out_config): for i, f in enumerate(all_frames): - save2Collect(f, out_config, f"frame_{i}.png") + save2Collect(f, out_config, f"frame_{i}") def crop_inner_image(outpainted_img, width_offset, height_offset): @@ -201,7 +294,7 @@ def create_zoom_single( current_image = custom_init_image.resize( (width, height), resample=Image.LANCZOS ) - save2Collect(current_image, out_config, f"init_img.png") + # save2Collect(current_image, out_config, f"init_img.png") print("using Custom Initial Image") else: @@ -222,7 +315,7 @@ def create_zoom_single( ) if len(processed.images) > 0: current_image = processed.images[0] - save2Collect(current_image, out_config, f"txt2img.png") + # save2Collect(current_image, out_config, f"txt2img.png") current_seed = newseed mask_width = math.trunc(width / 4) # was initially 512px => 128px @@ -238,85 +331,43 @@ def create_zoom_single( load_model_from_setting( "infzoom_inpainting_model", progress, "Loading Model for inpainting/img2img: " ) - - for i in range(num_outpainting_steps): - print_out = ( - "Outpaint step: " - + str(i + 1) - + " / " - + str(num_outpainting_steps) - + " Seed: " - + str(current_seed) - ) - print(print_out) - if progress: - progress(((i + 1) / num_outpainting_steps), desc=print_out) - - prev_image_fix = current_image - save2Collect(prev_image_fix, out_config, f"prev_image_fix_{i}.png") - - prev_image = shrink_and_paste_on_blank(current_image, mask_width, mask_height) - save2Collect(prev_image, out_config, f"prev_image_{i}.png") - - current_image = prev_image - - # create mask (black image with white mask_width width edges) - mask_image = np.array(current_image)[:, :, 3] - mask_image = Image.fromarray(255 - mask_image).convert("RGB") - save2Collect(mask_image, out_config, f"mask_image_{i}.png") - - # inpainting step - current_image = current_image.convert("RGB") - - if custom_exit_image and ((i + 1) == num_outpainting_steps): - current_image = custom_exit_image.resize( - (width, height), resample=Image.LANCZOS - ) - print("using Custom Exit Image") - save2Collect(current_image, out_config, f"exit_img.png") - else: - pr = prompts[max(k for k in prompts.keys() if k <= i)] - processed, newseed = renderImg2Img( - f"{common_prompt_pre}\n{pr}\n{common_prompt_suf}".strip(), - negative_prompt, - sampler, - num_inference_steps, - guidance_scale, - current_seed, - width, - height, - current_image, - mask_image, - inpainting_denoising_strength, - inpainting_mask_blur, - inpainting_fill_mode, - inpainting_full_res, - inpainting_padding, - ) - if len(processed.images) > 0: - current_image = processed.images[0] - current_seed = newseed - if len(processed.images) > 0: - # current_image.paste(prev_image, mask=prev_image) - save2Collect(current_image, out_config, f"curr_prev_paste_{i}.png") - if True or i > 0: - correction_crop = crop_inner_image(current_image, mask_width, mask_height) - prev_image_fix = correction_crop - # paste_x = (current_image.width - prev_image.width) // 2 - # paste_y = (current_image.height - prev_image.height) // 2 - # current_image.paste(prev_image, (paste_x, paste_y), mask=prev_image) - # replace the prev frame with current croped - all_frames.append( - do_upscaleImg( - prev_image_fix.convert("RGB"), upscale_do, upscaler_name, upscale_by - ) - if upscale_do - else prev_image_fix.convert("RGB") - ) + main_frames = outpaint_steps( + width, + height, + common_prompt_pre, + common_prompt_suf, + prompts, + negative_prompt, + seed, + sampler, + num_inference_steps, + guidance_scale, + inpainting_denoising_strength, + inpainting_mask_blur, + inpainting_fill_mode, + inpainting_full_res, + inpainting_padding, + current_image, + num_outpainting_steps, + out_config, + mask_width, + mask_height, + custom_exit_image, + ) + for i in range(len(main_frames) - 1): + # TODO: fix upscale + # all_frames.append( + # do_upscaleImg( + # prev_image_fix.convert("RGB"), upscale_do, upscaler_name, upscale_by + # ) + # if upscale_do + # else prev_image_fix.convert("RGB") + # ) # interpolation steps between 2 inpainted images (=sequential zoom and crop) for j in range(num_interpol_frames - 1): + current_image = main_frames[i + 1] interpol_image = current_image - save2Collect(interpol_image, out_config, f"interpol_img_{i}_{j}].png") + # save2Collect(interpol_image, out_config, f"interpol_img_{i}_{j}].png") interpol_width = round( ( @@ -365,7 +416,7 @@ def create_zoom_single( ) prev_image_fix_crop = shrink_and_paste_on_blank( - prev_image_fix, interpol_width2, interpol_height2 + main_frames[i], interpol_width2, interpol_height2 ) # save2Collect(prev_image_fix, out_config, f"prev_image_fix_crop_{i}_{j}.png") @@ -390,7 +441,7 @@ def create_zoom_single( else current_image ) - frames2Collect(all_frames, out_config) + # frames2Collect(all_frames, out_config) write_video( out_config["video_filename"], From c284fa24e5318d0878aa1567e42c8095f1e13b8a Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Fri, 28 Apr 2023 21:48:56 +0200 Subject: [PATCH 22/44] changed JSON schema: negPrompt, prePrompt, postPrompt, less name length --- iz_helpers/ui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iz_helpers/ui.py b/iz_helpers/ui.py index 0b7b998..51ce9d2 100644 --- a/iz_helpers/ui.py +++ b/iz_helpers/ui.py @@ -50,7 +50,7 @@ def on_ui_tabs(): jpr = readJsonPrompt(pr, True) main_common_prompt_pre = gr.Textbox( - value=jpr["commonPromptPrefix"], label="Common Prompt Prefix" + value=jpr["prePrompt"], label="Common Prompt Prefix" ) main_prompts = gr.Dataframe( @@ -64,7 +64,7 @@ def on_ui_tabs(): ) main_common_prompt_suf = gr.Textbox( - value=jpr["commonPromptSuffix"], label="Common Prompt Suffix" + value=jpr["postPrompt"], label="Common Prompt Suffix" ) main_negative_prompt = gr.Textbox( From 14b2f4edaa3ca89acca3f61191e52841a528e087 Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Fri, 28 Apr 2023 21:49:41 +0200 Subject: [PATCH 23/44] shorter names in json schema (neg/pre/postPrompt) --- iz_helpers/helpers.py | 4 ++-- iz_helpers/prompt_util.py | 44 ++++++++++------------------------ iz_helpers/promptschema.json | 9 +++---- iz_helpers/static_variables.py | 10 ++++---- scripts/infinite-zoom.py | 2 -- 5 files changed, 23 insertions(+), 46 deletions(-) diff --git a/iz_helpers/helpers.py b/iz_helpers/helpers.py index 4373bac..72b8f34 100644 --- a/iz_helpers/helpers.py +++ b/iz_helpers/helpers.py @@ -100,9 +100,9 @@ def putPrompts(files): data = readJsonPrompt(file_contents,False) return [ - gr.Textbox.update(data["commonPromptPrefix"]), + gr.Textbox.update(data["prePrompt"]), gr.DataFrame.update(data["prompts"]), - gr.Textbox.update(data["commonPromptSuffix"]), + gr.Textbox.update(data["postPromt"]), gr.Textbox.update(data["negPrompt"]) ] diff --git a/iz_helpers/prompt_util.py b/iz_helpers/prompt_util.py index a1bd445..95519a8 100644 --- a/iz_helpers/prompt_util.py +++ b/iz_helpers/prompt_util.py @@ -7,40 +7,21 @@ from .static_variables import ( jsonprompt_schemafile ) -""" - json is valid, but not our current schema. - lets try something. - does it look like something usable? -def fixJson(j): - fixedJ = empty_prompt - try: - if isinstance(j, dict): - if "prompts" in j: - if "data" in j["prompts"]: - if isinstance (j["prompts"]["data"],list): - fixedJ["prompts"]["data"] = j["prompts"]["data"] - if not isinstance (fixedJ["prompts"]["data"][0]. - - if "headers" not in j["prompts"]: - fixedJ["prompts"]["headers"] = ["outpaint steps","prompt"] - else: - fixedJ["prompts"]["headers"] = j["prompts"]["headers"] - - if "negPrompt" in j: - fixedJ["prompts"]["headers"] - - if "commonPrompt" in j: - return j - except Exception: - raise "JsonFix: Failed on recovering json prompt" - return j -""" - -def fixHeaders(j): +def completeOptionals(j): if isinstance(j, dict): if "prompts" in j: if "headers" not in j["prompts"]: j["prompts"]["headers"] = ["outpaint steps","prompt"] + + if "negPrompt" not in j: + j["prompts"]["negPrompt"]="" + + if "prePrompt" not in j: + j["prompts"]["prePrompt"]="" + + if "postPrompt" not in j: + j["prompts"]["postPrompt"]="" + return j @@ -49,11 +30,12 @@ def validatePromptJson_throws(data): schema = json.load(s) try: validate(instance=data, schema=schema) + except Exception: raise "Your prompts are not schema valid." #fixJson(data) - return fixHeaders(data) + return completeOptionals(data) def readJsonPrompt(txt, returnFailPrompt=False): diff --git a/iz_helpers/promptschema.json b/iz_helpers/promptschema.json index 396a554..ec513e0 100644 --- a/iz_helpers/promptschema.json +++ b/iz_helpers/promptschema.json @@ -47,17 +47,14 @@ "negPrompt": { "type": "string" }, - "commonPromptPrefix": { + "prePrompt": { "type": "string" }, - "commonPromptSuffix": { + "postPrompt": { "type": "string" } }, "required": [ - "prompts", - "negPrompt", - "commonPromptPrefix", - "commonPromptSuffix" + "prompts" ] } \ No newline at end of file diff --git a/iz_helpers/static_variables.py b/iz_helpers/static_variables.py index b52b31d..503dec3 100644 --- a/iz_helpers/static_variables.py +++ b/iz_helpers/static_variables.py @@ -4,20 +4,20 @@ import modules.sd_samplers default_prompt = """ { - "commonPromptPrefix":" ", + "prePrompt":" ", "prompts":{ "headers":["outpaint steps","prompt","img"], "data":[ [0,"Huge spectacular Waterfall in a dense tropical forest,epic perspective,(vegetation overgrowth:1.3)(intricate, ornamentation:1.1),(baroque:1.1), fantasy, (realistic:1) digital painting , (magical,mystical:1.2) , (wide angle shot:1.4), (landscape composed:1.2)(medieval:1.1), divine,cinematic,(tropical forest:1.4),(river:1.3)mythology,india, volumetric lighting, Hindu ,epic"] ] }, - "commonPromptSuffix":"style by Alex Horley Wenjun Lin greg rutkowski Ruan Jia (Wayne Barlowe:1.2)", + "postPrompt":"style by Alex Horley Wenjun Lin greg rutkowski Ruan Jia (Wayne Barlowe:1.2)", "negPrompt":"frames, border, edges, borderline, text, character, duplicate, error, out of frame, watermark, low quality, ugly, deformed, blur, bad-artist" } """ empty_prompt = ( - '{"prompts":{"data":[],"headers":["outpaint steps","prompt"]},"negPrompt":"", commonPromptPrefix:"", commonPromptSuffix}' + '{"prompts":{"data":[],"headers":["outpaint steps","prompt"]},"negPrompt":"", prePrompt:"", postPrompt:""}' ) invalid_prompt = { @@ -26,8 +26,8 @@ invalid_prompt = { "headers": ["outpaint steps", "prompt"], }, "negPrompt": "Invalid prompt-json", - "commonPromptPrefix": "Invalid prompt", - "commonPromptSuffix": "Invalid prompt" + "prePromp": "Invalid prompt", + "postPrompt": "Invalid prompt" } available_samplers = [ diff --git a/scripts/infinite-zoom.py b/scripts/infinite-zoom.py index a68461f..ab2764d 100644 --- a/scripts/infinite-zoom.py +++ b/scripts/infinite-zoom.py @@ -1,7 +1,5 @@ from modules import script_callbacks from iz_helpers.ui import on_ui_tabs from iz_helpers.settings import on_ui_settings - - script_callbacks.on_ui_tabs(on_ui_tabs) script_callbacks.on_ui_settings(on_ui_settings) From a3e59697bdf4067d88ccd9aab5c47e0208de0e28 Mon Sep 17 00:00:00 2001 From: "vahid K. nejad" Date: Sun, 30 Apr 2023 16:50:34 +0400 Subject: [PATCH 24/44] Corrected frame quality enhanced changing round to math.ceil for interpolation --- iz_helpers/run.py | 89 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 12 deletions(-) diff --git a/iz_helpers/run.py b/iz_helpers/run.py index 6e1d034..d1b5aa7 100644 --- a/iz_helpers/run.py +++ b/iz_helpers/run.py @@ -1,6 +1,6 @@ import math, time, os import numpy as np -from PIL import Image +from PIL import Image, ImageFilter, ImageDraw from modules.ui import plaintext_to_html import modules.shared as shared @@ -15,6 +15,58 @@ from .image import shrink_and_paste_on_blank from .video import write_video +def crop_fethear_ellipse(image, feather_margin=30, width_offset=0, height_offset=0): + # Create a blank mask image with the same size as the original image + mask = Image.new("L", image.size, 0) + draw = ImageDraw.Draw(mask) + + # Calculate the ellipse's bounding box + ellipse_box = ( + width_offset, + height_offset, + image.width - width_offset, + image.height - height_offset, + ) + + # Draw the ellipse on the mask + draw.ellipse(ellipse_box, fill=255) + + # Apply the mask to the original image + result = Image.new("RGBA", image.size) + result.paste(image, mask=mask) + + # Crop the resulting image to the ellipse's bounding box + cropped_image = result.crop(ellipse_box) + + # Create a new mask image with a black background (0) + mask = Image.new("L", cropped_image.size, 0) + draw = ImageDraw.Draw(mask) + + # Draw an ellipse on the mask image + draw.ellipse( + ( + 0 + feather_margin, + 0 + feather_margin, + cropped_image.width - feather_margin, + cropped_image.height - feather_margin, + ), + fill=255, + outline=0, + ) + + # Apply a Gaussian blur to the mask image + mask = mask.filter(ImageFilter.GaussianBlur(radius=feather_margin / 2)) + cropped_image.putalpha(mask) + res = Image.new(cropped_image.mode, (image.width, image.height)) + paste_pos = ( + int((res.width - cropped_image.width) / 2), + int((res.height - cropped_image.height) / 2), + ) + res.paste(cropped_image, paste_pos) + + return res + + def outpaint_steps( width, height, @@ -69,7 +121,7 @@ def outpaint_steps( (width, height), resample=Image.LANCZOS ) main_frames.append(current_image.convert("RGB")) - print("using Custom Exit Image") + # print("using Custom Exit Image") # save2Collect(current_image, out_config, f"exit_img.png") else: pr = prompts[max(k for k in prompts.keys() if k <= i)] @@ -97,14 +149,20 @@ def outpaint_steps( corrected_frame = crop_inner_image( main_frames[i + 1], mask_width, mask_height ) - save2Collect(current_image, out_config, f"corrected_{i}") - main_frames[i] = corrected_frame - # else TEST pasting differance + enhanced_img = crop_fethear_ellipse( + main_frames[i], + 30, + inpainting_mask_blur / 3 // 2, + inpainting_mask_blur / 3 // 2, + ) + + # save2Collect(enhanced_img, out_config, f"enhanced_frame_{i}") + corrected_frame.paste(enhanced_img, mask=enhanced_img) + main_frames[i] = corrected_frame + # else :TEST # current_image.paste(prev_image, mask=prev_image) - frames2Collect(main_frames, out_config) - print(out_config) - print("length: ", len(main_frames)) + # frames2Collect(main_frames, out_config) return main_frames @@ -354,6 +412,12 @@ def create_zoom_single( mask_height, custom_exit_image, ) + all_frames.append(main_frames[0]) + all_frames.append( + do_upscaleImg(main_frames[0], upscale_do, upscaler_name, upscale_by) + if upscale_do + else main_frames[0] + ) for i in range(len(main_frames) - 1): # TODO: fix upscale # all_frames.append( @@ -363,13 +427,14 @@ def create_zoom_single( # if upscale_do # else prev_image_fix.convert("RGB") # ) + # interpolation steps between 2 inpainted images (=sequential zoom and crop) for j in range(num_interpol_frames - 1): current_image = main_frames[i + 1] interpol_image = current_image # save2Collect(interpol_image, out_config, f"interpol_img_{i}_{j}].png") - interpol_width = round( + interpol_width = math.ceil( ( 1 - (1 - 2 * mask_width / width) @@ -379,7 +444,7 @@ def create_zoom_single( / 2 ) - interpol_height = round( + interpol_height = math.ceil( ( 1 - (1 - 2 * mask_height / height) @@ -403,13 +468,13 @@ def create_zoom_single( # save2Collect(interpol_image, out_config, f"interpol_resize_{i}_{j}.png") # paste the higher resolution previous image in the middle to avoid drop in quality caused by zooming - interpol_width2 = round( + interpol_width2 = math.ceil( (1 - (width - 2 * mask_width) / (width - 2 * interpol_width)) / 2 * width ) - interpol_height2 = round( + interpol_height2 = math.ceil( (1 - (height - 2 * mask_height) / (height - 2 * interpol_height)) / 2 * height From 6369ecc5b695ade09030113c281ab075f0a5ad4b Mon Sep 17 00:00:00 2001 From: "vahid K. nejad" Date: Sun, 30 Apr 2023 22:29:42 +0400 Subject: [PATCH 25/44] fix gallery view --- iz_helpers/run.py | 54 ++++++++++++++++++----------------------------- 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/iz_helpers/run.py b/iz_helpers/run.py index d1b5aa7..de03053 100644 --- a/iz_helpers/run.py +++ b/iz_helpers/run.py @@ -89,9 +89,10 @@ def outpaint_steps( mask_width, mask_height, custom_exit_image, - frame_correction=True, + frame_correction=True, # TODO: add frame_Correction in UI ): main_frames = [init_img.convert("RGB")] + for i in range(outpaint_steps): print_out = ( "Outpaint step: " @@ -103,18 +104,13 @@ def outpaint_steps( ) print(print_out) current_image = main_frames[-1] - prev_image_fix = current_image - # save2Collect(prev_image_fix, out_config, f"prev_image_fix_{i}.png") + current_image = shrink_and_paste_on_blank( + current_image, mask_width, mask_height + ) - prev_image = shrink_and_paste_on_blank(current_image, mask_width, mask_height) - # save2Collect(prev_image, out_config, f"prev_image_{i}.png") - - current_image = prev_image - - # create mask (black image with white mask_width width edges) mask_image = np.array(current_image)[:, :, 3] mask_image = Image.fromarray(255 - mask_image).convert("RGB") - # save2Collect(mask_image, out_config, f"mask_image_{i}.png") + # create mask (black image with white mask_width width edges) if custom_exit_image and ((i + 1) == outpaint_steps): current_image = custom_exit_image.resize( @@ -122,7 +118,7 @@ def outpaint_steps( ) main_frames.append(current_image.convert("RGB")) # print("using Custom Exit Image") - # save2Collect(current_image, out_config, f"exit_img.png") + save2Collect(current_image, out_config, f"exit_img.png") else: pr = prompts[max(k for k in prompts.keys() if k <= i)] processed, newseed = renderImg2Img( @@ -142,9 +138,13 @@ def outpaint_steps( inpainting_full_res, inpainting_padding, ) + if len(processed.images) > 0: main_frames.append(processed.images[0].convert("RGB")) + save2Collect(processed.images[0], out_config, f"outpain_step_{i}.png") seed = newseed + # TODO: seed behavior + if frame_correction and inpainting_mask_blur > 0: corrected_frame = crop_inner_image( main_frames[i + 1], mask_width, mask_height @@ -156,13 +156,12 @@ def outpaint_steps( inpainting_mask_blur / 3 // 2, inpainting_mask_blur / 3 // 2, ) - - # save2Collect(enhanced_img, out_config, f"enhanced_frame_{i}") + save2Collect(main_frames[i], out_config, f"main_frame_{i}") + save2Collect(enhanced_img, out_config, f"main_frame_enhanced_{i}") corrected_frame.paste(enhanced_img, mask=enhanced_img) main_frames[i] = corrected_frame # else :TEST # current_image.paste(prev_image, mask=prev_image) - # frames2Collect(main_frames, out_config) return main_frames @@ -352,8 +351,7 @@ def create_zoom_single( current_image = custom_init_image.resize( (width, height), resample=Image.LANCZOS ) - # save2Collect(current_image, out_config, f"init_img.png") - print("using Custom Initial Image") + save2Collect(current_image, out_config, f"init_custom.png") else: load_model_from_setting( @@ -373,7 +371,7 @@ def create_zoom_single( ) if len(processed.images) > 0: current_image = processed.images[0] - # save2Collect(current_image, out_config, f"txt2img.png") + save2Collect(current_image, out_config, f"init_txt2img.png") current_seed = newseed mask_width = math.trunc(width / 4) # was initially 512px => 128px @@ -412,27 +410,17 @@ def create_zoom_single( mask_height, custom_exit_image, ) - all_frames.append(main_frames[0]) all_frames.append( do_upscaleImg(main_frames[0], upscale_do, upscaler_name, upscale_by) if upscale_do else main_frames[0] ) for i in range(len(main_frames) - 1): - # TODO: fix upscale - # all_frames.append( - # do_upscaleImg( - # prev_image_fix.convert("RGB"), upscale_do, upscaler_name, upscale_by - # ) - # if upscale_do - # else prev_image_fix.convert("RGB") - # ) - # interpolation steps between 2 inpainted images (=sequential zoom and crop) for j in range(num_interpol_frames - 1): current_image = main_frames[i + 1] interpol_image = current_image - # save2Collect(interpol_image, out_config, f"interpol_img_{i}_{j}].png") + save2Collect(interpol_image, out_config, f"interpol_img_{i}_{j}].png") interpol_width = math.ceil( ( @@ -462,10 +450,9 @@ def create_zoom_single( height - interpol_height, ) ) - # save2Collect(interpol_image, out_config, f"interpol_crop_{i}_{j}.png") interpol_image = interpol_image.resize((width, height)) - # save2Collect(interpol_image, out_config, f"interpol_resize_{i}_{j}.png") + save2Collect(interpol_image, out_config, f"interpol_resize_{i}_{j}.png") # paste the higher resolution previous image in the middle to avoid drop in quality caused by zooming interpol_width2 = math.ceil( @@ -483,10 +470,9 @@ def create_zoom_single( prev_image_fix_crop = shrink_and_paste_on_blank( main_frames[i], interpol_width2, interpol_height2 ) - # save2Collect(prev_image_fix, out_config, f"prev_image_fix_crop_{i}_{j}.png") interpol_image.paste(prev_image_fix_crop, mask=prev_image_fix_crop) - # save2Collect(interpol_image, out_config, f"interpol_prevcrop_{i}_{j}.png") + save2Collect(interpol_image, out_config, f"interpol_prevcrop_{i}_{j}.png") if upscale_do and progress: progress(((i + 1) / num_outpainting_steps), desc="upscaling interpol") @@ -506,7 +492,7 @@ def create_zoom_single( else current_image ) - # frames2Collect(all_frames, out_config) + frames2Collect(all_frames, out_config) write_video( out_config["video_filename"], @@ -519,7 +505,7 @@ def create_zoom_single( return ( out_config["video_filename"], - processed.images, + main_frames, processed.js(), plaintext_to_html(processed.info), plaintext_to_html(""), From 519944ddd014d6d3a080c7b107d9d11ed053bf6d Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Sun, 30 Apr 2023 21:43:50 +0200 Subject: [PATCH 26/44] Fix steps2, promp1 and init img --- iz_helpers/run.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iz_helpers/run.py b/iz_helpers/run.py index de03053..25ca93e 100644 --- a/iz_helpers/run.py +++ b/iz_helpers/run.py @@ -162,7 +162,7 @@ def outpaint_steps( main_frames[i] = corrected_frame # else :TEST # current_image.paste(prev_image, mask=prev_image) - return main_frames + return main_frames, processed def create_zoom( @@ -387,7 +387,7 @@ def create_zoom_single( load_model_from_setting( "infzoom_inpainting_model", progress, "Loading Model for inpainting/img2img: " ) - main_frames = outpaint_steps( + main_frames, processed = outpaint_steps( width, height, common_prompt_pre, From 4114c85b973eb8d0389c47b7adace344de6402c2 Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Sun, 30 Apr 2023 21:57:05 +0200 Subject: [PATCH 27/44] Total Steps minimun is now 1 (init+1x outpaint ) --- iz_helpers/ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iz_helpers/ui.py b/iz_helpers/ui.py index 0b7b998..1f7bb0a 100644 --- a/iz_helpers/ui.py +++ b/iz_helpers/ui.py @@ -38,7 +38,7 @@ def on_ui_tabs(): ) main_outpaint_steps = gr.Slider( - minimum=2, + minimum=1, maximum=100, step=1, value=8, From 8da2fb1ba7ce7f46b47aab20b0982aa580fcf353 Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Sun, 30 Apr 2023 22:38:51 +0200 Subject: [PATCH 28/44] Hotfix: copy paste, post/preprompts on main level of jason Signed-off-by: GeorgLegato --- iz_helpers/prompt_util.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iz_helpers/prompt_util.py b/iz_helpers/prompt_util.py index 95519a8..d246829 100644 --- a/iz_helpers/prompt_util.py +++ b/iz_helpers/prompt_util.py @@ -17,10 +17,10 @@ def completeOptionals(j): j["prompts"]["negPrompt"]="" if "prePrompt" not in j: - j["prompts"]["prePrompt"]="" + j["prePrompt"]="" if "postPrompt" not in j: - j["prompts"]["postPrompt"]="" + j["postPrompt"]="" return j @@ -56,4 +56,4 @@ def readJsonPrompt(txt, returnFailPrompt=False): if returnFailPrompt: return invalid_prompt pass - \ No newline at end of file + From b7a0cb318a17bf87463485ba43bd8519225bf11d Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Sun, 30 Apr 2023 22:39:31 +0200 Subject: [PATCH 29/44] added check --- iz_helpers/ui.py | 66 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/iz_helpers/ui.py b/iz_helpers/ui.py index 51ce9d2..f6db773 100644 --- a/iz_helpers/ui.py +++ b/iz_helpers/ui.py @@ -256,7 +256,7 @@ Our best experience and trade-off is the R-ERSGAn4x upscaler. "infinite-zoom", shared.opts.outdir_img2img_samples ) generate_btn.click( - fn=wrap_gradio_gpu_call(create_zoom, extra_outputs=[None, "", ""]), + fn=wrap_gradio_gpu_call(check_create_zoom, extra_outputs=[None, "", ""]), inputs=[ main_common_prompt_pre, main_prompts, @@ -291,3 +291,67 @@ Our best experience and trade-off is the R-ERSGAn4x upscaler. interrupt.click(fn=lambda: shared.state.interrupt(), inputs=[], outputs=[]) infinite_zoom_interface.queue() return [(infinite_zoom_interface, "Infinite Zoom", "iz_interface")] + + + +def check_create_zoom( + main_common_prompt_pre, + main_prompts, + main_common_prompt_suf, + main_negative_prompt, + main_outpaint_steps, + main_guidance_scale, + sampling_step, + init_image, + exit_image, + video_frame_rate, + video_zoom_mode, + video_start_frame_dupe_amount, + video_last_frame_dupe_amount, + inpainting_denoising_strength, + inpainting_mask_blur, + inpainting_fill_mode, + inpainting_full_res, + inpainting_padding, + video_zoom_speed, + seed, + main_width, + main_height, + batchcount_slider, + main_sampler, + upscale_do, + upscaler_name, + upscale_by, +): + keys = main_prompts.keys() + if 0 not in keys: + raise gr.Error("Ensure your prompt table has a step 9 (zero) prompt") + + return create_zoom( main_common_prompt_pre, + main_prompts, + main_common_prompt_suf, + main_negative_prompt, + main_outpaint_steps, + main_guidance_scale, + sampling_step, + init_image, + exit_image, + video_frame_rate, + video_zoom_mode, + video_start_frame_dupe_amount, + video_last_frame_dupe_amount, + inpainting_denoising_strength, + inpainting_mask_blur, + inpainting_fill_mode, + inpainting_full_res, + inpainting_padding, + video_zoom_speed, + seed, + main_width, + main_height, + batchcount_slider, + main_sampler, + upscale_do, + upscaler_name, + upscale_by, + ) \ No newline at end of file From 7591a9615531721d6b5a38a6fb3e20ec2563540a Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Sun, 30 Apr 2023 22:40:52 +0200 Subject: [PATCH 30/44] hotfix: fix prompts neg/post/pre Signed-off-by: GeorgLegato --- iz_helpers/prompt_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iz_helpers/prompt_util.py b/iz_helpers/prompt_util.py index d246829..ab978ee 100644 --- a/iz_helpers/prompt_util.py +++ b/iz_helpers/prompt_util.py @@ -14,7 +14,7 @@ def completeOptionals(j): j["prompts"]["headers"] = ["outpaint steps","prompt"] if "negPrompt" not in j: - j["prompts"]["negPrompt"]="" + j["negPrompt"]="" if "prePrompt" not in j: j["prePrompt"]="" From 443b3256f5cff5d5653a4b96c4c6cfa91749c71b Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Mon, 1 May 2023 01:07:57 +0200 Subject: [PATCH 31/44] fix json complete. do not accept headers --- iz_helpers/prompt_util.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/iz_helpers/prompt_util.py b/iz_helpers/prompt_util.py index 95519a8..b42d81e 100644 --- a/iz_helpers/prompt_util.py +++ b/iz_helpers/prompt_util.py @@ -9,18 +9,19 @@ from .static_variables import ( def completeOptionals(j): if isinstance(j, dict): + # Remove header information, user dont pimp our ui if "prompts" in j: - if "headers" not in j["prompts"]: - j["prompts"]["headers"] = ["outpaint steps","prompt"] + if "headers" in j["prompts"]: + del j["prompts"]["headers"] if "negPrompt" not in j: - j["prompts"]["negPrompt"]="" + j["negPrompt"]="" if "prePrompt" not in j: - j["prompts"]["prePrompt"]="" + j["prePrompt"]="" if "postPrompt" not in j: - j["prompts"]["postPrompt"]="" + j["postPrompt"]="" return j @@ -32,8 +33,7 @@ def validatePromptJson_throws(data): validate(instance=data, schema=schema) except Exception: - raise "Your prompts are not schema valid." - #fixJson(data) + raise Exception("Your prompts are not schema valid.") return completeOptionals(data) From 700b1c9560ac30ae1ab1909154d57c0b6d41713b Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Mon, 1 May 2023 01:29:11 +0200 Subject: [PATCH 32/44] changed wording to help users understand concept very easily. disabling generate button on invalid table --- iz_helpers/ui.py | 80 ++++++------------------------- javascript/infinite-zoom-hints.js | 7 ++- 2 files changed, 17 insertions(+), 70 deletions(-) diff --git a/iz_helpers/ui.py b/iz_helpers/ui.py index f6db773..f9d52e7 100644 --- a/iz_helpers/ui.py +++ b/iz_helpers/ui.py @@ -42,7 +42,7 @@ def on_ui_tabs(): maximum=100, step=1, value=8, - label="Total Outpaint Steps", + label="Total video length [s]", ) # safe reading json prompt @@ -55,7 +55,7 @@ def on_ui_tabs(): main_prompts = gr.Dataframe( type="array", - headers=["outpaint step", "prompt"], + headers=["Start at second [0,1,...]", "Prompt"], datatype=["number", "str"], row_count=1, col_count=(2, "fixed"), @@ -255,8 +255,9 @@ Our best experience and trade-off is the R-ERSGAn4x upscaler. ) = create_output_panel( "infinite-zoom", shared.opts.outdir_img2img_samples ) + generate_btn.click( - fn=wrap_gradio_gpu_call(check_create_zoom, extra_outputs=[None, "", ""]), + fn=wrap_gradio_gpu_call(create_zoom, extra_outputs=[None, "", ""]), inputs=[ main_common_prompt_pre, main_prompts, @@ -288,70 +289,17 @@ Our best experience and trade-off is the R-ERSGAn4x upscaler. ], outputs=[output_video, out_image, generation_info, html_info, html_log], ) - interrupt.click(fn=lambda: shared.state.interrupt(), inputs=[], outputs=[]) + + main_prompts.change(fn=checkPrompts,inputs=[main_prompts], outputs=[generate_btn]) + + interrupt.click(fn=shared.state.interrupt(), inputs=[], outputs=[]) infinite_zoom_interface.queue() return [(infinite_zoom_interface, "Infinite Zoom", "iz_interface")] - - -def check_create_zoom( - main_common_prompt_pre, - main_prompts, - main_common_prompt_suf, - main_negative_prompt, - main_outpaint_steps, - main_guidance_scale, - sampling_step, - init_image, - exit_image, - video_frame_rate, - video_zoom_mode, - video_start_frame_dupe_amount, - video_last_frame_dupe_amount, - inpainting_denoising_strength, - inpainting_mask_blur, - inpainting_fill_mode, - inpainting_full_res, - inpainting_padding, - video_zoom_speed, - seed, - main_width, - main_height, - batchcount_slider, - main_sampler, - upscale_do, - upscaler_name, - upscale_by, -): - keys = main_prompts.keys() - if 0 not in keys: - raise gr.Error("Ensure your prompt table has a step 9 (zero) prompt") - - return create_zoom( main_common_prompt_pre, - main_prompts, - main_common_prompt_suf, - main_negative_prompt, - main_outpaint_steps, - main_guidance_scale, - sampling_step, - init_image, - exit_image, - video_frame_rate, - video_zoom_mode, - video_start_frame_dupe_amount, - video_last_frame_dupe_amount, - inpainting_denoising_strength, - inpainting_mask_blur, - inpainting_fill_mode, - inpainting_full_res, - inpainting_padding, - video_zoom_speed, - seed, - main_width, - main_height, - batchcount_slider, - main_sampler, - upscale_do, - upscaler_name, - upscale_by, +def checkPrompts(p): + return gr.Button.update( + interactive= + any(0 in sublist for sublist in p) + or + any('0' in sublist for sublist in p) ) \ No newline at end of file diff --git a/javascript/infinite-zoom-hints.js b/javascript/infinite-zoom-hints.js index ef7be89..e046f65 100644 --- a/javascript/infinite-zoom-hints.js +++ b/javascript/infinite-zoom-hints.js @@ -2,7 +2,7 @@ infzoom_titles = { "Batch Count":"How many separate videos to create", - "Total Outpaint Steps":"The more it is, the longer your videos will be. Each step generates frame for 1 second at your FPS, while cycling through your array of prompts", + "Total video length [s]":"For each seconds frame (FPS) will be generated. Define prompts at which time they should start wihtin this duration.", "Common Prompt Prefix":"Prompt inserted before each step", "Common Prompt Suffix":"Prompt inserted after each step", "Negative Prompt":"What your model shall avoid", @@ -12,9 +12,8 @@ infzoom_titles = { "Custom initial image":"An image at the end resp. begin of your movie, depending or ZoomIn or Out", "Custom exit image":"An image at the end resp. begin of your movie, depending or ZoomIn or Out", "Zoom Speed":"Varies additional frames per second", - - - + "Start at second [0,1,...]": "At which time the prompt has to be occure. We need at least one prompt starting at time 0", + "Generate video": "Start rendering. If it´s disabled the prompt table is invalid, check we have a start prompt at time 0" } From d8703520ecda951a8060451e668cf51fd42d9ed1 Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Sun, 30 Apr 2023 22:39:31 +0200 Subject: [PATCH 33/44] added check --- iz_helpers/ui.py | 66 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/iz_helpers/ui.py b/iz_helpers/ui.py index 51ce9d2..f6db773 100644 --- a/iz_helpers/ui.py +++ b/iz_helpers/ui.py @@ -256,7 +256,7 @@ Our best experience and trade-off is the R-ERSGAn4x upscaler. "infinite-zoom", shared.opts.outdir_img2img_samples ) generate_btn.click( - fn=wrap_gradio_gpu_call(create_zoom, extra_outputs=[None, "", ""]), + fn=wrap_gradio_gpu_call(check_create_zoom, extra_outputs=[None, "", ""]), inputs=[ main_common_prompt_pre, main_prompts, @@ -291,3 +291,67 @@ Our best experience and trade-off is the R-ERSGAn4x upscaler. interrupt.click(fn=lambda: shared.state.interrupt(), inputs=[], outputs=[]) infinite_zoom_interface.queue() return [(infinite_zoom_interface, "Infinite Zoom", "iz_interface")] + + + +def check_create_zoom( + main_common_prompt_pre, + main_prompts, + main_common_prompt_suf, + main_negative_prompt, + main_outpaint_steps, + main_guidance_scale, + sampling_step, + init_image, + exit_image, + video_frame_rate, + video_zoom_mode, + video_start_frame_dupe_amount, + video_last_frame_dupe_amount, + inpainting_denoising_strength, + inpainting_mask_blur, + inpainting_fill_mode, + inpainting_full_res, + inpainting_padding, + video_zoom_speed, + seed, + main_width, + main_height, + batchcount_slider, + main_sampler, + upscale_do, + upscaler_name, + upscale_by, +): + keys = main_prompts.keys() + if 0 not in keys: + raise gr.Error("Ensure your prompt table has a step 9 (zero) prompt") + + return create_zoom( main_common_prompt_pre, + main_prompts, + main_common_prompt_suf, + main_negative_prompt, + main_outpaint_steps, + main_guidance_scale, + sampling_step, + init_image, + exit_image, + video_frame_rate, + video_zoom_mode, + video_start_frame_dupe_amount, + video_last_frame_dupe_amount, + inpainting_denoising_strength, + inpainting_mask_blur, + inpainting_fill_mode, + inpainting_full_res, + inpainting_padding, + video_zoom_speed, + seed, + main_width, + main_height, + batchcount_slider, + main_sampler, + upscale_do, + upscaler_name, + upscale_by, + ) \ No newline at end of file From 430b2d2310e9b8867d1c1fdf4a4f7e93bbe10cee Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Mon, 1 May 2023 01:07:57 +0200 Subject: [PATCH 34/44] fix json complete. do not accept headers --- iz_helpers/prompt_util.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/iz_helpers/prompt_util.py b/iz_helpers/prompt_util.py index ab978ee..415a629 100644 --- a/iz_helpers/prompt_util.py +++ b/iz_helpers/prompt_util.py @@ -9,9 +9,10 @@ from .static_variables import ( def completeOptionals(j): if isinstance(j, dict): + # Remove header information, user dont pimp our ui if "prompts" in j: - if "headers" not in j["prompts"]: - j["prompts"]["headers"] = ["outpaint steps","prompt"] + if "headers" in j["prompts"]: + del j["prompts"]["headers"] if "negPrompt" not in j: j["negPrompt"]="" @@ -32,8 +33,7 @@ def validatePromptJson_throws(data): validate(instance=data, schema=schema) except Exception: - raise "Your prompts are not schema valid." - #fixJson(data) + raise Exception("Your prompts are not schema valid.") return completeOptionals(data) From 78d1e488568ad6a1730c02f43b13d38ec874f53a Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Mon, 1 May 2023 01:29:11 +0200 Subject: [PATCH 35/44] changed wording to help users understand concept very easily. disabling generate button on invalid table --- iz_helpers/ui.py | 80 ++++++------------------------- javascript/infinite-zoom-hints.js | 7 ++- 2 files changed, 17 insertions(+), 70 deletions(-) diff --git a/iz_helpers/ui.py b/iz_helpers/ui.py index f6db773..f9d52e7 100644 --- a/iz_helpers/ui.py +++ b/iz_helpers/ui.py @@ -42,7 +42,7 @@ def on_ui_tabs(): maximum=100, step=1, value=8, - label="Total Outpaint Steps", + label="Total video length [s]", ) # safe reading json prompt @@ -55,7 +55,7 @@ def on_ui_tabs(): main_prompts = gr.Dataframe( type="array", - headers=["outpaint step", "prompt"], + headers=["Start at second [0,1,...]", "Prompt"], datatype=["number", "str"], row_count=1, col_count=(2, "fixed"), @@ -255,8 +255,9 @@ Our best experience and trade-off is the R-ERSGAn4x upscaler. ) = create_output_panel( "infinite-zoom", shared.opts.outdir_img2img_samples ) + generate_btn.click( - fn=wrap_gradio_gpu_call(check_create_zoom, extra_outputs=[None, "", ""]), + fn=wrap_gradio_gpu_call(create_zoom, extra_outputs=[None, "", ""]), inputs=[ main_common_prompt_pre, main_prompts, @@ -288,70 +289,17 @@ Our best experience and trade-off is the R-ERSGAn4x upscaler. ], outputs=[output_video, out_image, generation_info, html_info, html_log], ) - interrupt.click(fn=lambda: shared.state.interrupt(), inputs=[], outputs=[]) + + main_prompts.change(fn=checkPrompts,inputs=[main_prompts], outputs=[generate_btn]) + + interrupt.click(fn=shared.state.interrupt(), inputs=[], outputs=[]) infinite_zoom_interface.queue() return [(infinite_zoom_interface, "Infinite Zoom", "iz_interface")] - - -def check_create_zoom( - main_common_prompt_pre, - main_prompts, - main_common_prompt_suf, - main_negative_prompt, - main_outpaint_steps, - main_guidance_scale, - sampling_step, - init_image, - exit_image, - video_frame_rate, - video_zoom_mode, - video_start_frame_dupe_amount, - video_last_frame_dupe_amount, - inpainting_denoising_strength, - inpainting_mask_blur, - inpainting_fill_mode, - inpainting_full_res, - inpainting_padding, - video_zoom_speed, - seed, - main_width, - main_height, - batchcount_slider, - main_sampler, - upscale_do, - upscaler_name, - upscale_by, -): - keys = main_prompts.keys() - if 0 not in keys: - raise gr.Error("Ensure your prompt table has a step 9 (zero) prompt") - - return create_zoom( main_common_prompt_pre, - main_prompts, - main_common_prompt_suf, - main_negative_prompt, - main_outpaint_steps, - main_guidance_scale, - sampling_step, - init_image, - exit_image, - video_frame_rate, - video_zoom_mode, - video_start_frame_dupe_amount, - video_last_frame_dupe_amount, - inpainting_denoising_strength, - inpainting_mask_blur, - inpainting_fill_mode, - inpainting_full_res, - inpainting_padding, - video_zoom_speed, - seed, - main_width, - main_height, - batchcount_slider, - main_sampler, - upscale_do, - upscaler_name, - upscale_by, +def checkPrompts(p): + return gr.Button.update( + interactive= + any(0 in sublist for sublist in p) + or + any('0' in sublist for sublist in p) ) \ No newline at end of file diff --git a/javascript/infinite-zoom-hints.js b/javascript/infinite-zoom-hints.js index ef7be89..e046f65 100644 --- a/javascript/infinite-zoom-hints.js +++ b/javascript/infinite-zoom-hints.js @@ -2,7 +2,7 @@ infzoom_titles = { "Batch Count":"How many separate videos to create", - "Total Outpaint Steps":"The more it is, the longer your videos will be. Each step generates frame for 1 second at your FPS, while cycling through your array of prompts", + "Total video length [s]":"For each seconds frame (FPS) will be generated. Define prompts at which time they should start wihtin this duration.", "Common Prompt Prefix":"Prompt inserted before each step", "Common Prompt Suffix":"Prompt inserted after each step", "Negative Prompt":"What your model shall avoid", @@ -12,9 +12,8 @@ infzoom_titles = { "Custom initial image":"An image at the end resp. begin of your movie, depending or ZoomIn or Out", "Custom exit image":"An image at the end resp. begin of your movie, depending or ZoomIn or Out", "Zoom Speed":"Varies additional frames per second", - - - + "Start at second [0,1,...]": "At which time the prompt has to be occure. We need at least one prompt starting at time 0", + "Generate video": "Start rendering. If it´s disabled the prompt table is invalid, check we have a start prompt at time 0" } From a50c1046501b475d1f6adbb92ab489d83ab058f1 Mon Sep 17 00:00:00 2001 From: "vahid K. nejad" Date: Mon, 1 May 2023 16:04:50 +0400 Subject: [PATCH 36/44] default prompts changed --- iz_helpers/static_variables.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/iz_helpers/static_variables.py b/iz_helpers/static_variables.py index b52b31d..61f6cd0 100644 --- a/iz_helpers/static_variables.py +++ b/iz_helpers/static_variables.py @@ -4,21 +4,22 @@ import modules.sd_samplers default_prompt = """ { - "commonPromptPrefix":" ", - "prompts":{ - "headers":["outpaint steps","prompt","img"], - "data":[ - [0,"Huge spectacular Waterfall in a dense tropical forest,epic perspective,(vegetation overgrowth:1.3)(intricate, ornamentation:1.1),(baroque:1.1), fantasy, (realistic:1) digital painting , (magical,mystical:1.2) , (wide angle shot:1.4), (landscape composed:1.2)(medieval:1.1), divine,cinematic,(tropical forest:1.4),(river:1.3)mythology,india, volumetric lighting, Hindu ,epic"] - ] - }, - "commonPromptSuffix":"style by Alex Horley Wenjun Lin greg rutkowski Ruan Jia (Wayne Barlowe:1.2)", - "negPrompt":"frames, border, edges, borderline, text, character, duplicate, error, out of frame, watermark, low quality, ugly, deformed, blur, bad-artist" + "commonPromptPrefix": "Huge spectacular Waterfall in ", + "prompts": { + "headers": ["outpaint steps", "prompt"], + "data": [ + [0, "a dense tropical forest"], + [2, "a Lush jungle"], + [3, "a Thick rainforest"], + [5, "a Verdant canopy"] + ] + }, + "commonPromptSuffix": "epic perspective,(vegetation overgrowth:1.3)(intricate, ornamentation:1.1),(baroque:1.1), fantasy, (realistic:1) digital painting , (magical,mystical:1.2) , (wide angle shot:1.4), (landscape composed:1.2)(medieval:1.1),(tropical forest:1.4),(river:1.3) volumetric lighting ,epic, style by Alex Horley Wenjun Lin greg rutkowski Ruan Jia (Wayne Barlowe:1.2)", + "negPrompt": "frames, border, edges, borderline, text, character, duplicate, error, out of frame, watermark, low quality, ugly, deformed, blur, bad-artist" } """ -empty_prompt = ( - '{"prompts":{"data":[],"headers":["outpaint steps","prompt"]},"negPrompt":"", commonPromptPrefix:"", commonPromptSuffix}' -) +empty_prompt = '{"prompts":{"data":[],"headers":["outpaint steps","prompt"]},"negPrompt":"", commonPromptPrefix:"", commonPromptSuffix}' invalid_prompt = { "prompts": { @@ -27,7 +28,7 @@ invalid_prompt = { }, "negPrompt": "Invalid prompt-json", "commonPromptPrefix": "Invalid prompt", - "commonPromptSuffix": "Invalid prompt" + "commonPromptSuffix": "Invalid prompt", } available_samplers = [ From bf41a308c3af3de89ba44e24b1ca856b909fce51 Mon Sep 17 00:00:00 2001 From: "vahid K. nejad" Date: Mon, 1 May 2023 16:29:32 +0400 Subject: [PATCH 37/44] Printing path to saved video, typo fix --- iz_helpers/run.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iz_helpers/run.py b/iz_helpers/run.py index 25ca93e..7f1fb50 100644 --- a/iz_helpers/run.py +++ b/iz_helpers/run.py @@ -3,7 +3,7 @@ import numpy as np from PIL import Image, ImageFilter, ImageDraw from modules.ui import plaintext_to_html import modules.shared as shared - +from modules.paths_internal import script_path from .helpers import ( fix_env_Path_ffprobe, closest_upper_divisible_by_eight, @@ -231,7 +231,7 @@ def create_zoom( def prepare_output_path(): isCollect = shared.opts.data.get("infzoom_collectAllResources", False) - output_path = shared.opts.data.get("infzoom_outpath", "output") + output_path = shared.opts.data.get("infzoom_outpath", "outputs") save_path = os.path.join( output_path, shared.opts.data.get("infzoom_outSUBpath", "infinite-zooms") @@ -502,7 +502,7 @@ def create_zoom_single( int(video_start_frame_dupe_amount), int(video_last_frame_dupe_amount), ) - + print("Video saved in: " + os.path.join(script_path, out_config["video_filename"])) return ( out_config["video_filename"], main_frames, From fdb8eba2fade31a54f6613b56296614ecb507e10 Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Mon, 1 May 2023 18:20:50 +0200 Subject: [PATCH 38/44] stupid coding, not using a const string, i mistyped, of course. --- iz_helpers/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iz_helpers/helpers.py b/iz_helpers/helpers.py index 72b8f34..c7b110e 100644 --- a/iz_helpers/helpers.py +++ b/iz_helpers/helpers.py @@ -102,7 +102,7 @@ def putPrompts(files): return [ gr.Textbox.update(data["prePrompt"]), gr.DataFrame.update(data["prompts"]), - gr.Textbox.update(data["postPromt"]), + gr.Textbox.update(data["postPrompt"]), gr.Textbox.update(data["negPrompt"]) ] From 90a5aab9dd01108422c4a8dcdf018aa6b1414f8f Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Mon, 1 May 2023 21:45:10 +0200 Subject: [PATCH 39/44] fix; remeaining old keywords in JSON; common vs pre/post --- iz_helpers/static_variables.py | 8 +++----- javascript/infinite-zoom.js | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/iz_helpers/static_variables.py b/iz_helpers/static_variables.py index 314c3b0..214b5e8 100644 --- a/iz_helpers/static_variables.py +++ b/iz_helpers/static_variables.py @@ -4,9 +4,8 @@ import modules.sd_samplers default_prompt = """ { - "commonPromptPrefix": "Huge spectacular Waterfall in ", + "prePrompt": "Huge spectacular Waterfall in ", "prompts": { - "headers": ["outpaint steps", "prompt"], "data": [ [0, "a dense tropical forest"], [2, "a Lush jungle"], @@ -14,17 +13,16 @@ default_prompt = """ [5, "a Verdant canopy"] ] }, - "commonPromptSuffix": "epic perspective,(vegetation overgrowth:1.3)(intricate, ornamentation:1.1),(baroque:1.1), fantasy, (realistic:1) digital painting , (magical,mystical:1.2) , (wide angle shot:1.4), (landscape composed:1.2)(medieval:1.1),(tropical forest:1.4),(river:1.3) volumetric lighting ,epic, style by Alex Horley Wenjun Lin greg rutkowski Ruan Jia (Wayne Barlowe:1.2)", + "postPrompt": "epic perspective,(vegetation overgrowth:1.3)(intricate, ornamentation:1.1),(baroque:1.1), fantasy, (realistic:1) digital painting , (magical,mystical:1.2) , (wide angle shot:1.4), (landscape composed:1.2)(medieval:1.1),(tropical forest:1.4),(river:1.3) volumetric lighting ,epic, style by Alex Horley Wenjun Lin greg rutkowski Ruan Jia (Wayne Barlowe:1.2)", "negPrompt": "frames, border, edges, borderline, text, character, duplicate, error, out of frame, watermark, low quality, ugly, deformed, blur, bad-artist" } """ -empty_prompt = '{"prompts":{"data":[],"headers":["outpaint steps","prompt"]},"negPrompt":"", commonPromptPrefix:"", commonPromptSuffix}' +empty_prompt = '{"prompts":{"data":[],"negPrompt":"", prePrompt:"", postPrompt:""}' invalid_prompt = { "prompts": { "data": [[0, "Your prompt-json is invalid, please check Settings"]], - "headers": ["outpaint steps", "prompt"], }, "negPrompt": "Invalid prompt-json", "prePromp": "Invalid prompt", diff --git a/javascript/infinite-zoom.js b/javascript/infinite-zoom.js index f4c1692..5709977 100644 --- a/javascript/infinite-zoom.js +++ b/javascript/infinite-zoom.js @@ -1,7 +1,7 @@ // Function to download data to a file function exportPrompts(cppre,p, cpsuf,np, filename = "infinite-zoom-prompts.json") { - let J = { prompts: p, negPrompt: np, commonPromptPrefix: cppre, commonPromptSuffix: cpsuf } + let J = { prompts: p, negPrompt: np, prePrompt: cppre, postPrompt: cpsuf } var file = new Blob([JSON.stringify(J,null,2)], { type: "text/csv" }); if (window.navigator.msSaveOrOpenBlob) // IE10+ From 38355dfab86237da37c517c0321c8792edbb666a Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Mon, 1 May 2023 22:17:41 +0200 Subject: [PATCH 40/44] fix and clean json handling, commonX considered, headers not 0,1 anymore --- iz_helpers/prompt_util.py | 16 ++++++++++++---- iz_helpers/static_variables.py | 2 ++ iz_helpers/ui.py | 3 ++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/iz_helpers/prompt_util.py b/iz_helpers/prompt_util.py index 415a629..4ef5433 100644 --- a/iz_helpers/prompt_util.py +++ b/iz_helpers/prompt_util.py @@ -4,7 +4,8 @@ from jsonschema import validate from .static_variables import ( empty_prompt, invalid_prompt, - jsonprompt_schemafile + jsonprompt_schemafile, + promptTableHeaders ) def completeOptionals(j): @@ -13,15 +14,22 @@ def completeOptionals(j): if "prompts" in j: if "headers" in j["prompts"]: del j["prompts"]["headers"] - + j["prompts"]["headers"]=promptTableHeaders + if "negPrompt" not in j: j["negPrompt"]="" if "prePrompt" not in j: - j["prePrompt"]="" + if "commonPromptPrefix" in j: + j["prePrompt"]=j["commonPromptPrefix"] + else: + j["prePrompt"]="" if "postPrompt" not in j: - j["postPrompt"]="" + if "commonPromptSuffix" in j: + j["postPrompt"]=j["commonPromptSuffix"] + else: + j["postPrompt"]="" return j diff --git a/iz_helpers/static_variables.py b/iz_helpers/static_variables.py index 214b5e8..3b25463 100644 --- a/iz_helpers/static_variables.py +++ b/iz_helpers/static_variables.py @@ -2,6 +2,8 @@ import os from modules import scripts import modules.sd_samplers +promptTableHeaders=["Start at second [0,1,...]","Prompt"] + default_prompt = """ { "prePrompt": "Huge spectacular Waterfall in ", diff --git a/iz_helpers/ui.py b/iz_helpers/ui.py index 5f85ed8..a880f50 100644 --- a/iz_helpers/ui.py +++ b/iz_helpers/ui.py @@ -9,6 +9,7 @@ from .static_variables import ( ) from .helpers import putPrompts, clearPrompts from .prompt_util import readJsonPrompt +from .static_variables import promptTableHeaders def on_ui_tabs(): @@ -55,7 +56,7 @@ def on_ui_tabs(): main_prompts = gr.Dataframe( type="array", - headers=["Start at second [0,1,...]", "Prompt"], + headers= promptTableHeaders, datatype=["number", "str"], row_count=1, col_count=(2, "fixed"), From 81636a081bbabfd284a0982593fb61230e64a8aa Mon Sep 17 00:00:00 2001 From: GeorgLegato Date: Mon, 1 May 2023 22:44:27 +0200 Subject: [PATCH 41/44] addedd warning on SafeCollection setting --- iz_helpers/settings.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/iz_helpers/settings.py b/iz_helpers/settings.py index e201588..5b000c6 100644 --- a/iz_helpers/settings.py +++ b/iz_helpers/settings.py @@ -99,12 +99,10 @@ def on_ui_settings(): "infzoom_collectAllResources", shared.OptionInfo( False, - "Store all images (txt2img, init_image,exit_image, inpainting, interpolation) and the movie into one folder in your OUTPUT Path", + "!!! Store all images (txt2img, init_image,exit_image, inpainting, interpolation) into one folder in your OUTPUT Path. Very slow, a lot of data. Dont do this on long runs !!!", gr.Checkbox, {"interactive": True}, section=section, ), ) - - - + From 2ed87c1efff3f348026081b804118aa0a814fcc2 Mon Sep 17 00:00:00 2001 From: "vahid K. nejad" Date: Tue, 2 May 2023 09:50:42 +0400 Subject: [PATCH 42/44] Remove unnecessary params and change default params --- iz_helpers/run.py | 12 +++---- iz_helpers/static_variables.py | 9 +++-- iz_helpers/ui.py | 63 ++++++++++++++++------------------ 3 files changed, 43 insertions(+), 41 deletions(-) diff --git a/iz_helpers/run.py b/iz_helpers/run.py index 7f1fb50..d783f14 100644 --- a/iz_helpers/run.py +++ b/iz_helpers/run.py @@ -179,11 +179,8 @@ def create_zoom( video_zoom_mode, video_start_frame_dupe_amount, video_last_frame_dupe_amount, - inpainting_denoising_strength, inpainting_mask_blur, inpainting_fill_mode, - inpainting_full_res, - inpainting_padding, zoom_speed, seed, outputsizeW, @@ -193,6 +190,9 @@ def create_zoom( upscale_do, upscaler_name, upscale_by, + inpainting_denoising_strength=1, + inpainting_full_res=False, + inpainting_padding=0, progress=None, ): for i in range(batchcount): @@ -302,11 +302,8 @@ def create_zoom_single( video_zoom_mode, video_start_frame_dupe_amount, video_last_frame_dupe_amount, - inpainting_denoising_strength, inpainting_mask_blur, inpainting_fill_mode, - inpainting_full_res, - inpainting_padding, zoom_speed, seed, outputsizeW, @@ -315,6 +312,9 @@ def create_zoom_single( upscale_do, upscaler_name, upscale_by, + inpainting_denoising_strength=1, + inpainting_full_res=False, + inpainting_padding=0, progress=None, ): # try: diff --git a/iz_helpers/static_variables.py b/iz_helpers/static_variables.py index 3b25463..c330cd3 100644 --- a/iz_helpers/static_variables.py +++ b/iz_helpers/static_variables.py @@ -2,7 +2,12 @@ import os from modules import scripts import modules.sd_samplers -promptTableHeaders=["Start at second [0,1,...]","Prompt"] +default_sampling_steps = 35 +default_sampler = "DDIM" +default_cfg_scale = "DDIM" +default_mask_blur = 48 +default_total_outpaints = 5 +promptTableHeaders = ["Start at second [0,1,...]", "Prompt"] default_prompt = """ { @@ -28,7 +33,7 @@ invalid_prompt = { }, "negPrompt": "Invalid prompt-json", "prePromp": "Invalid prompt", - "postPrompt": "Invalid prompt" + "postPrompt": "Invalid prompt", } available_samplers = [ diff --git a/iz_helpers/ui.py b/iz_helpers/ui.py index a880f50..8961b3f 100644 --- a/iz_helpers/ui.py +++ b/iz_helpers/ui.py @@ -3,9 +3,15 @@ from .run import create_zoom import modules.shared as shared from webui import wrap_gradio_gpu_call from modules.ui import create_output_panel + from .static_variables import ( default_prompt, available_samplers, + default_total_outpaints, + default_sampling_steps, + default_cfg_scale, + default_mask_blur, + default_sampler, ) from .helpers import putPrompts, clearPrompts from .prompt_util import readJsonPrompt @@ -37,13 +43,11 @@ def on_ui_tabs(): step=1, label="Batch Count", ) - - main_outpaint_steps = gr.Slider( - minimum=1, - maximum=100, - step=1, - value=8, + main_outpaint_steps = gr.Number( label="Total video length [s]", + value=default_total_outpaints, + precision=0, + interactive=True, ) # safe reading json prompt @@ -56,7 +60,7 @@ def on_ui_tabs(): main_prompts = gr.Dataframe( type="array", - headers= promptTableHeaders, + headers=promptTableHeaders, datatype=["number", "str"], row_count=1, col_count=(2, "fixed"), @@ -72,7 +76,7 @@ def on_ui_tabs(): value=jpr["negPrompt"], label="Negative Prompt" ) - # these button will be moved using JS unde the dataframe view as small ones + # these button will be moved using JS under the dataframe view as small ones exportPrompts_button = gr.Button( value="Export prompts", variant="secondary", @@ -132,7 +136,7 @@ def on_ui_tabs(): main_sampler = gr.Dropdown( label="Sampler", choices=available_samplers, - value="Euler a", + value=default_sampler, type="value", ) with gr.Row(): @@ -155,14 +159,14 @@ def on_ui_tabs(): minimum=0.1, maximum=15, step=0.1, - value=7, + value=default_cfg_scale, label="Guidance Scale", ) sampling_step = gr.Slider( minimum=1, - maximum=100, + maximum=150, step=1, - value=50, + value=default_sampling_steps, label="Sampling Steps for each outpaint", ) with gr.Row(): @@ -209,11 +213,11 @@ def on_ui_tabs(): ) with gr.Tab("Outpaint"): - inpainting_denoising_strength = gr.Slider( - label="Denoising Strength", minimum=0.75, maximum=1, value=1 - ) inpainting_mask_blur = gr.Slider( - label="Mask Blur", minimum=0, maximum=64, value=0 + label="Mask Blur", + minimum=0, + maximum=64, + value=default_mask_blur, ) inpainting_fill_mode = gr.Radio( label="Masked content", @@ -221,10 +225,6 @@ def on_ui_tabs(): value="latent noise", type="index", ) - inpainting_full_res = gr.Checkbox(label="Inpaint Full Resolution") - inpainting_padding = gr.Slider( - label="masked padding", minimum=0, maximum=256, value=0 - ) with gr.Tab("Post proccess"): upscale_do = gr.Checkbox(False, label="Enable Upscale") @@ -234,7 +234,6 @@ def on_ui_tabs(): choices=[x.name for x in shared.sd_upscalers], value=shared.sd_upscalers[0].name, ) - upscale_by = gr.Slider( label="Upscale by factor", minimum=1, maximum=8, value=1 ) @@ -256,7 +255,7 @@ Our best experience and trade-off is the R-ERSGAn4x upscaler. ) = create_output_panel( "infinite-zoom", shared.opts.outdir_img2img_samples ) - + generate_btn.click( fn=wrap_gradio_gpu_call(create_zoom, extra_outputs=[None, "", ""]), inputs=[ @@ -273,11 +272,8 @@ Our best experience and trade-off is the R-ERSGAn4x upscaler. video_zoom_mode, video_start_frame_dupe_amount, video_last_frame_dupe_amount, - inpainting_denoising_strength, inpainting_mask_blur, inpainting_fill_mode, - inpainting_full_res, - inpainting_padding, video_zoom_speed, seed, main_width, @@ -290,17 +286,18 @@ Our best experience and trade-off is the R-ERSGAn4x upscaler. ], outputs=[output_video, out_image, generation_info, html_info, html_log], ) - - main_prompts.change(fn=checkPrompts,inputs=[main_prompts], outputs=[generate_btn]) - + + main_prompts.change( + fn=checkPrompts, inputs=[main_prompts], outputs=[generate_btn] + ) + interrupt.click(fn=shared.state.interrupt(), inputs=[], outputs=[]) infinite_zoom_interface.queue() return [(infinite_zoom_interface, "Infinite Zoom", "iz_interface")] + def checkPrompts(p): return gr.Button.update( - interactive= - any(0 in sublist for sublist in p) - or - any('0' in sublist for sublist in p) - ) \ No newline at end of file + interactive=any(0 in sublist for sublist in p) + or any("0" in sublist for sublist in p) + ) From 842c5b8b4bc1e289d3ebc10c90e101d4f9cf2d34 Mon Sep 17 00:00:00 2001 From: "vahid K. nejad" Date: Tue, 2 May 2023 09:56:13 +0400 Subject: [PATCH 43/44] typo --- iz_helpers/static_variables.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iz_helpers/static_variables.py b/iz_helpers/static_variables.py index c330cd3..a54c1f5 100644 --- a/iz_helpers/static_variables.py +++ b/iz_helpers/static_variables.py @@ -4,7 +4,7 @@ import modules.sd_samplers default_sampling_steps = 35 default_sampler = "DDIM" -default_cfg_scale = "DDIM" +default_cfg_scale = 8 default_mask_blur = 48 default_total_outpaints = 5 promptTableHeaders = ["Start at second [0,1,...]", "Prompt"] From 422e140b100f555c8a97cee08c26cd61bd6ce7b9 Mon Sep 17 00:00:00 2001 From: "vahid K. nejad" Date: Tue, 2 May 2023 10:22:44 +0400 Subject: [PATCH 44/44] fixes, 0.5 for upscale factor slider steps --- iz_helpers/run.py | 16 ++++++++-------- iz_helpers/ui.py | 6 +++++- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/iz_helpers/run.py b/iz_helpers/run.py index d783f14..d224af5 100644 --- a/iz_helpers/run.py +++ b/iz_helpers/run.py @@ -191,7 +191,7 @@ def create_zoom( upscaler_name, upscale_by, inpainting_denoising_strength=1, - inpainting_full_res=False, + inpainting_full_res=0, inpainting_padding=0, progress=None, ): @@ -211,11 +211,8 @@ def create_zoom( video_zoom_mode, video_start_frame_dupe_amount, video_last_frame_dupe_amount, - inpainting_denoising_strength, inpainting_mask_blur, inpainting_fill_mode, - inpainting_full_res, - inpainting_padding, zoom_speed, seed, outputsizeW, @@ -224,6 +221,9 @@ def create_zoom( upscale_do, upscaler_name, upscale_by, + inpainting_denoising_strength, + inpainting_full_res, + inpainting_padding, progress, ) return result @@ -312,10 +312,10 @@ def create_zoom_single( upscale_do, upscaler_name, upscale_by, - inpainting_denoising_strength=1, - inpainting_full_res=False, - inpainting_padding=0, - progress=None, + inpainting_denoising_strength, + inpainting_full_res, + inpainting_padding, + progress, ): # try: # if gr.Progress() is not None: diff --git a/iz_helpers/ui.py b/iz_helpers/ui.py index 8961b3f..7f28b48 100644 --- a/iz_helpers/ui.py +++ b/iz_helpers/ui.py @@ -235,7 +235,11 @@ def on_ui_tabs(): value=shared.sd_upscalers[0].name, ) upscale_by = gr.Slider( - label="Upscale by factor", minimum=1, maximum=8, value=1 + label="Upscale by factor", + minimum=1, + maximum=8, + step=0.5, + value=2, ) with gr.Accordion("Help", open=False): gr.Markdown(