infinite-zoom-automatic1111.../iz_helpers/ui.py

583 lines
26 KiB
Python

import json
from msilib.schema import File
import gradio as gr
import modules.shared as shared
from webui import wrap_gradio_gpu_call
from modules.ui import create_output_panel
from .run_interface import createZoom
from .static_variables import (
default_prompt,
empty_prompt,
invalid_prompt,
available_samplers,
default_total_outpaints,
default_sampling_steps,
default_cfg_scale,
default_mask_blur,
default_sampler,
default_overmask,
default_gradient_size,
default_outpaint_amount,
default_lut_example_img,
default_Luma_wipe_img,
)
from .helpers import validatePromptJson_throws, putPrompts, clearPrompts, renumberDataframe, closest_upper_divisible_by_eight, recalcPromptKeys
from .prompt_util import readJsonPrompt
from .static_variables import promptTableHeaders
from PIL import Image
from .image import apply_lut, open_image
def on_ui_tabs():
main_seed = gr.Number()
audio_filename = gr.Textbox(None)
with gr.Blocks(analytics_enabled=False) as infinite_zoom_interface:
gr.HTML(
"""
<p style="text-align: center;">
<a target="_blank" href="https://github.com/v8hid/infinite-zoom-automatic1111-webui"><img src="https://img.shields.io/static/v1?label=github&message=repository&color=blue&style=flat&logo=github&logoColor=white" style="display: inline;" alt="GitHub Repo"/></a>
<a href="https://discord.gg/v2nHqSrWdW"><img src="https://img.shields.io/discord/1095469311830806630?color=blue&label=discord&logo=discord&logoColor=white" style="display: inline;" alt="Discord server"></a>
</p>
"""
)
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"):
with gr.Row():
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=120,
step=1,
label="Total video length [s]",
value=default_total_outpaints,
precision=0,
interactive=True,
)
# safe reading json prompt
pr = shared.opts.data.get("infzoom_defPrompt", default_prompt)
jpr = readJsonPrompt(pr, True)
main_common_prompt_pre = gr.Textbox(
value=jpr["prePrompt"], label="Common Prompt Prefix"
)
main_prompts = gr.Dataframe(
type="array",
headers=promptTableHeaders[0],
datatype=promptTableHeaders[1],
row_count=1,
col_count=(5, "fixed"),
value=jpr["prompts"],
wrap=True,
elem_id = "infzoom_prompt_table",
)
main_common_prompt_suf = gr.Textbox(
value=jpr["postPrompt"], label="Common Prompt Suffix"
)
main_negative_prompt = gr.Textbox(
value=jpr["negPrompt"], label="Negative Prompt"
)
with gr.Accordion("Render settings"):
with gr.Row():
main_seed = gr.Number(
label="Seed",
value=jpr["seed"],
elem_id="infzoom_main_seed",
precision=0,
interactive=True
)
gr.Button('\U0001f3b2\ufe0f').style(full_width=False).click(fn=lambda: -1, outputs=[main_seed], queue=False)
reuse_seed = gr.Button('\u267b\ufe0f').style(full_width=False)
main_sampler = gr.Dropdown(
label="Sampler",
choices=available_samplers,
value=default_sampler,
type="value",
)
with gr.Row():
main_width = gr.Slider(
minimum=16,
maximum=2048,
value=jpr["width"] if jpr["width"] != shared.opts.data.get("infzoom_outsizeW", 512) else shared.opts.data.get("infzoom_outsizeW", 512),
step=8,
label="Output Width",
)
main_height = gr.Slider(
minimum=16,
maximum=2048,
value=jpr["height"] if jpr["height"] != shared.opts.data.get("infzoom_outsizeH", 512) else shared.opts.data.get("infzoom_outsizeH", 512),
step=8,
label="Output Height",
)
with gr.Row():
main_guidance_scale = gr.Slider(
minimum=0.1,
maximum=15,
step=0.1,
value=jpr["guidanceScale"],
label="Guidance Scale",
)
sampling_step = gr.Slider(
minimum=1,
maximum=150,
step=1,
value=jpr["steps"],
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",
value=30,
minimum=1,
maximum=60,
step=1
)
video_zoom_mode = gr.Radio(
label="Zoom mode",
choices=["Zoom-out", "Zoom-in"],
value=jpr["zoomMode"],
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=jpr["startFrames"],
minimum=1,
maximum=120,
step=1
)
video_last_frame_dupe_amount = gr.Slider(
label="number of last frame dupe",
info="Frames to freeze at the end of the video",
value=jpr["lastFrames"],
minimum=1,
maximum=120,
step=1
)
with gr.Row():
video_zoom_speed = gr.Slider(
label="Zoom Speed",
value=jpr["zoomSpeed"],
minimum=0.1,
maximum=20.0,
step=0.1,
info="Zoom speed in seconds (higher values create slower zoom)",
)
video_est_length = gr.Number(
label="Estimated video length [s]",
info="a basic estimation of the video length",
value=1.0,
precision=1,
readonly=True,
id="infzoom_est_length",
)
with gr.Accordion("FFMPEG Expert", open=False):
gr.Markdown(
"""# I need FFMPEG control
You can put CLI options here as documented <a href='https://ffmpeg.org/ffmpeg.html#Options'>FFMPEG OPTIONS</a> and <a href='https://ffmpeg.org/ffmpeg-filters.html'>FILTER OPTIONS</a>
## Examples:
* ```-vf crop=200:200``` crop down to 200x200 pixel from center (useful to cutoff jumpy borders)
* ```-vf scale=320:240``` scales your video to 320x240
* ```-c:v libx264 -preset veryslow -qp 0``` uses lossless compression
You might give multiple options in one line.
"""
)
video_ffmpeg_opts=gr.Textbox(
value="", label="FFMPEG Opts"
)
with gr.Accordion("Blend settings"):
with gr.Row():
blend_image = gr.Image(type="pil", label="Custom in/out Blend Image", value=default_Luma_wipe_img)
blend_mode = gr.Radio(
label="Blend Mode",
choices=["Not Used", "Simple Blend", "Alpha Composite", "Luma Wipe"],
value=0, #jpr["blendMode"]
type="index",
elem_id="infzoom_blend_mode",
)
blend_invert_do = gr.Checkbox(jpr["blendInvert"], label="Reverse Blend/Wipe")
with gr.Row():
blend_gradient_size = gr.Slider(
label="Blend Gradient size",
minimum=25,
maximum=75,
value=jpr["blendGradient"],
step=1
)
blend_color = gr.ColorPicker(
label='Blend Edge Color',
value=jpr["blendColor"]
)
main_prompts.change(recalcPromptKeys,inputs=[main_prompts], outputs=[main_outpaint_steps])
video_zoom_speed.change(calc_est_video_length,inputs=[blend_mode,video_zoom_speed, video_start_frame_dupe_amount,video_last_frame_dupe_amount,video_frame_rate,main_outpaint_steps],outputs=[video_est_length])
main_outpaint_steps.change(calc_est_video_length,inputs=[blend_mode,video_zoom_speed, video_start_frame_dupe_amount,video_last_frame_dupe_amount,video_frame_rate,main_outpaint_steps],outputs=[video_est_length])
video_frame_rate.change(calc_est_video_length,inputs=[blend_mode,video_zoom_speed, video_start_frame_dupe_amount,video_last_frame_dupe_amount,video_frame_rate,main_outpaint_steps],outputs=[video_est_length])
video_start_frame_dupe_amount.change(calc_est_video_length,inputs=[blend_mode,video_zoom_speed, video_start_frame_dupe_amount,video_last_frame_dupe_amount,video_frame_rate,main_outpaint_steps],outputs=[video_est_length])
video_last_frame_dupe_amount.change(calc_est_video_length,inputs=[blend_mode,video_zoom_speed, video_start_frame_dupe_amount,video_last_frame_dupe_amount,video_frame_rate,main_outpaint_steps],outputs=[video_est_length])
blend_mode.change(calc_est_video_length,inputs=[blend_mode,video_zoom_speed, video_start_frame_dupe_amount,video_last_frame_dupe_amount,video_frame_rate,main_outpaint_steps],outputs=[video_est_length])
with gr.Accordion("Blend Info", open=False):
gr.Markdown(
"""# Important Blend Info:
Number of Start and Stop Frame Duplication number of frames used for the blend/wipe effect. At 30 Frames per second, 30 frames is 1 second.
Blend Gradient size determines if blends extend to the border of the images. 61 is typical, higher values may result in frames around steps of your video
Free to use grayscale blend images can be found here: https://github.com/Oncorporation/obs-studio/tree/master/plugins/obs-transitions/data/luma_wipes
Ideas for custom blend images: https://www.pexels.com/search/gradient/
"""
)
with gr.Row():
lut_filename = gr.Textbox(
value=jpr["lutFileName"],
label="Look Up Table (LUT) File Name",
elem_id="infzoom_lutFileName")
lut_file = gr.File(
value=None,
file_count="single",
file_types=[".cube"],
type="file",
label="LUT cube File")
lut_example_image = gr.Image(type="pil", label="LUT Example Image", value=default_lut_example_img)
lut_file.change(get_filename, inputs=[lut_file], outputs=[lut_filename])
lut_filename.change(show_lut, inputs=[lut_filename, lut_example_image], outputs=[lut_example_image])
with gr.Tab("Audio"):
with gr.Row():
audio_filename = gr.Textbox(
value=jpr["audioFileName"],
label="Audio File Name",
elem_id="infzoom_audioFileName")
audio_file = gr.File(
value=None,
file_count="single",
file_types=["audio"],
type="file",
label="Audio File")
audio_file.change(get_filename, inputs=[audio_file], outputs=[audio_filename])
with gr.Row():
audio_volume = gr.Slider(
label="Audio volume",
minimum=0.0,
maximum=2.0,
step=.05,
value=1.0)
with gr.Tab("Outpaint"):
outpaint_amount_px = gr.Slider(
label="Outpaint pixels",
minimum=8,
maximum=512,
step=8,
value=jpr["outpaintAmount"],
elem_id="infzoom_outpaintAmount"
)
inpainting_mask_blur = gr.Slider(
label="Mask Blur",
minimum=0,
maximum=64,
step=1,
value=jpr["maskBlur"],
)
overmask = gr.Slider(
label="Overmask (px) paint a bit into centered image",
minimum=0,
maximum=64,
step=1,
value=jpr["overmask"],
elem_id="infzoom_outpaintOvermask"
)
inpainting_fill_mode = gr.Radio(
label="Masked content",
choices=["fill", "original", "latent noise", "latent nothing"],
value="latent noise",
type="index",
)
outpaintStrategy= gr.Radio(
label="Outpaint Strategy",
choices=["Center", "Corners"],
value=jpr["outpaintStrategy"],
type="value",
elem_id="infzoom_outpaintStrategy"
)
main_width.change(get_min_outpaint_amount,inputs=[main_width, outpaint_amount_px, outpaintStrategy],outputs=[outpaint_amount_px])
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,
step=0.25,
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.
"""
)
# these buttons will be moved using JS under 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,
audio_filename,
main_seed,
main_width,
main_height,
main_sampler,
main_guidance_scale,
sampling_step,
lut_filename,
outpaint_amount_px,
inpainting_mask_blur,
overmask,
outpaintStrategy,
video_zoom_mode,
video_frame_rate,
video_zoom_speed,
video_start_frame_dupe_amount,
video_last_frame_dupe_amount,
blend_mode,
blend_color,
blend_gradient_size,
blend_invert_do,
],
outputs=None,
)
importPrompts_button.upload(
fn=putPrompts,
outputs=[
main_common_prompt_pre,
main_prompts,
main_common_prompt_suf,
main_negative_prompt,
main_outpaint_steps,
audio_filename,
main_seed,
main_width,
main_height,
main_sampler,
main_guidance_scale,
sampling_step,
lut_filename,
outpaint_amount_px,
inpainting_mask_blur,
overmask,
outpaintStrategy,
video_zoom_mode,
video_frame_rate,
video_zoom_speed,
video_start_frame_dupe_amount,
video_last_frame_dupe_amount,
blend_mode,
blend_color,
blend_gradient_size,
blend_invert_do,
],
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,
audio_filename,
main_seed,
main_width,
main_height,
main_sampler,
main_guidance_scale,
sampling_step,
lut_filename,
outpaint_amount_px,
inpainting_mask_blur,
overmask,
outpaintStrategy,
video_zoom_mode,
video_frame_rate,
video_zoom_speed,
video_start_frame_dupe_amount,
video_last_frame_dupe_amount,
blend_mode,
blend_color,
blend_gradient_size,
blend_invert_do,
],
)
renumberPrompts_button = gr.Button(
value= "Renumber Prompts",
variant="secondary",
elem_classes="sm infzoom_tab_butt",
elem_id="infzoom_rnP_butt",
)
renumberPrompts_button.click(
fn=renumberDataframe,
inputs=[main_prompts],
outputs=[main_prompts]
)
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
)
seed_used = gr.Number(label='Seed used', value=-1, interactive=False)
generate_btn.click(
fn=wrap_gradio_gpu_call(createZoom, extra_outputs=[None, None, -1, "", ""]),
inputs=[
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,
video_ffmpeg_opts,
inpainting_mask_blur,
inpainting_fill_mode,
video_zoom_speed,
main_seed,
main_width,
main_height,
batchcount_slider,
main_sampler,
upscale_do,
upscaler_name,
upscale_by,
overmask,
outpaintStrategy,
outpaint_amount_px,
blend_image,
blend_mode,
blend_gradient_size,
blend_invert_do,
blend_color,
lut_filename,
audio_filename,
audio_volume,
],
outputs=[output_video, out_image, seed_used, generation_info, html_info, html_log],
)
main_prompts.change(
fn=checkPrompts, inputs=[main_prompts], outputs=[generate_btn]
)
reuse_seed.click(fn=lambda x: x, inputs=[seed_used], outputs=[main_seed], queue=False)
interrupt.click(fn=lambda: 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)
)
def get_filename(file):
filename = None
if file is not None:
filename = file.name
return filename
def show_lut(lut_filename: str, lut_example_image: Image = default_lut_example_img) -> Image:
if lut_filename is not None:
try:
lut_example_image = apply_lut(lut_example_image, lut_filename)
except Exception as e:
print(f"BAD LUT: Error applying LUT {str(e)}.")
else:
lut_example_image = open_image(default_lut_example_img)
return lut_example_image
def get_min_outpaint_amount(width, outpaint_amount, strategy):
#automatically sets the minimum outpaint amount based on the width for Center strategy
min_outpaint_px = outpaint_amount
if strategy == "Center":
min_outpaint_px = closest_upper_divisible_by_eight(max(outpaint_amount, width // 4))
return min_outpaint_px
def calc_est_video_length(blend_mode, video_zoom_speed, video_start_frame_dupe_amount,video_last_frame_dupe_amount,fps, main_outpaint_steps):
#calculates the estimated video length based on the blend mode, zoom speed, and outpaint steps
#this is just an estimate, the actual length will vary
steps = main_outpaint_steps
estimate = (steps * video_zoom_speed) + ((video_start_frame_dupe_amount + video_last_frame_dupe_amount) / fps)
if blend_mode != 0:
steps = (main_outpaint_steps - 3)
estimate = (steps * video_zoom_speed) + (((video_start_frame_dupe_amount + video_last_frame_dupe_amount) / fps) - 1.0)
return estimate