diff --git a/iz_helpers/InfZoomConfig.py b/iz_helpers/InfZoomConfig.py new file mode 100644 index 0000000..9cdfa78 --- /dev/null +++ b/iz_helpers/InfZoomConfig.py @@ -0,0 +1,36 @@ +from dataclasses import dataclass +from PIL import Image +@dataclass +class InfZoomConfig(): + common_prompt_pre:str + prompts_array:list[str] + common_prompt_suf:str + negative_prompt:str + num_outpainting_steps: int + guidance_scale:float + num_inference_steps:int + custom_init_image:Image + custom_exit_image:Image + video_frame_rate:int + video_zoom_mode:int #0: ZoomOut, 1: ZoomIn + video_start_frame_dupe_amount:int + video_last_frame_dupe_amount:int + video_ffmpeg_opts: str + inpainting_mask_blur:int + inpainting_fill_mode:int + zoom_speed:float + seed:int + outputsizeW:int + outputsizeH:int + batchcount:int + sampler:str + upscale_do:bool + upscaler_name:str + upscale_by:float + overmask:int + outpaintStrategy: str + inpainting_denoising_strength:float=1 + inpainting_full_res:int =0 + inpainting_padding:int=0 + outpaint_amount_px: int=64 + progress:any=None diff --git a/iz_helpers/image.py b/iz_helpers/image.py index 8876c02..a2adc58 100644 --- a/iz_helpers/image.py +++ b/iz_helpers/image.py @@ -42,6 +42,8 @@ def open_image(image_path): Returns: Image: A PIL Image object of the opened image. """ + # Strip leading and trailing double quotation marks, if present + image_path = image_path.strip('"') if image_path.startswith('http'): # If the image path is a URL, download the image using requests response = requests.get(image_path) diff --git a/iz_helpers/run.py b/iz_helpers/run.py index 9246da9..d5b2d8a 100644 --- a/iz_helpers/run.py +++ b/iz_helpers/run.py @@ -1,5 +1,8 @@ import math, time, os import numpy as np +from scipy.signal import savgol_filter +from typing import Callable +import cv2 from PIL import Image, ImageFilter, ImageDraw, ImageColor from modules.ui import plaintext_to_html import modules.shared as shared @@ -13,7 +16,15 @@ from .helpers import ( ) from .sd_helpers import renderImg2Img, renderTxt2Img from .image import shrink_and_paste_on_blank, open_image, apply_alpha_mask, draw_gradient_ellipse, resize_and_crop_image, crop_fethear_ellipse, crop_inner_image -from .video import write_video, add_audio_to_video +from .video import write_video, add_audio_to_video, ContinuousVideoWriter +from .InfZoomConfig import InfZoomConfig + +class InfZoomer: + def __init__(self, config: InfZoomConfig) -> None: + self.C = config + self.prompts = {} + self.main_frames = [] + self.out_config = {} def outpaint_steps( width, @@ -193,6 +204,7 @@ def create_zoom( video_zoom_mode, video_start_frame_dupe_amount, video_last_frame_dupe_amount, + video_ffmpeg_opts, inpainting_mask_blur, inpainting_fill_mode, zoom_speed, @@ -204,6 +216,9 @@ def create_zoom( upscale_do, upscaler_name, upscale_by, + overmask, + outpaintStrategy, + outpaint_amount_px, blend_image, blend_mode, blend_gradient_size, diff --git a/iz_helpers/run_interface.py b/iz_helpers/run_interface.py new file mode 100644 index 0000000..9403128 --- /dev/null +++ b/iz_helpers/run_interface.py @@ -0,0 +1,77 @@ +from .run import (InfZoomer) +from .InfZoomConfig import InfZoomConfig +from PIL import Image + +# to be called from Gradio or other client +def createZoom( + common_prompt_pre:str, + prompts_array:list[str], + common_prompt_suf:str, + negative_prompt:str, + num_outpainting_steps: int, + guidance_scale:float, + num_inference_steps:int, + custom_init_image:Image, + custom_exit_image:Image, + video_frame_rate:int, + video_zoom_mode:int, + video_start_frame_dupe_amount:int, + video_last_frame_dupe_amount:int, + video_ffmpeg_opts:str, + inpainting_mask_blur:int, + inpainting_fill_mode:int, + zoom_speed:float, + seed:int, + outputsizeW:int, + outputsizeH:int, + batchcount:int, + sampler:str, + upscale_do:bool, + upscaler_name:str, + upscale_by:float, + overmask:int, + outpaintStrategy:str, + outpaint_amount_px: int, + inpainting_denoising_strength:float=1, + inpainting_full_res:int =0, + inpainting_padding:int=0, + progress:any=None +): + izc = InfZoomConfig( + common_prompt_pre, + prompts_array, + common_prompt_suf, + 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, + video_ffmpeg_opts, + inpainting_mask_blur, + inpainting_fill_mode, + zoom_speed, + seed, + outputsizeW, + outputsizeH, + batchcount, + sampler, + upscale_do, + upscaler_name, + upscale_by, + overmask, + outpaintStrategy, + inpainting_denoising_strength, + inpainting_full_res, + inpainting_padding, + outpaint_amount_px, + progress + ) + iz= InfZoomer(izc) + r = iz.create_zoom() + del iz + return r diff --git a/iz_helpers/ui.py b/iz_helpers/ui.py index 751a7a4..e014abe 100644 --- a/iz_helpers/ui.py +++ b/iz_helpers/ui.py @@ -5,6 +5,7 @@ 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 .run_interface import createZoom from .static_variables import ( default_prompt, @@ -16,6 +17,7 @@ from .static_variables import ( default_cfg_scale, default_mask_blur, default_sampler, + default_overmask, default_gradient_size, ) from .helpers import validatePromptJson_throws, putPrompts, clearPrompts, renumberDataframe @@ -175,6 +177,23 @@ def on_ui_tabs(): step=0.1, info="Zoom speed in seconds (higher values create slower zoom)", ) + with gr.Accordion("FFMPEG Expert", open=False): + gr.Markdown( + """# I need FFMPEG control +You can put CLI options here as documented FFMPEG OPTIONS and FILTER OPTIONS + +## 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") @@ -223,12 +242,27 @@ Ideas for custom blend images: https://www.pexels.com/search/gradient/ audio_file.change(get_filename, inputs=[audio_file], outputs=[audio_filename]) with gr.Tab("Outpaint"): + outpaint_amount_px = gr.Slider( + label="Outpaint pixels", + minimum=4, + maximum=508, + step=8, + value=64, + ) + inpainting_mask_blur = gr.Slider( label="Mask Blur", minimum=0, maximum=64, + step=1, value=default_mask_blur, - step=1 + ) + overmask = gr.Slider( + label="Overmask (px) paint a bit into centered image", + minimum=0, + maximum=64, + step=1, + value=default_overmask, ) inpainting_fill_mode = gr.Radio( label="Masked content", @@ -237,6 +271,15 @@ Ideas for custom blend images: https://www.pexels.com/search/gradient/ type="index", ) + outpaintStrategy= gr.Radio( + label="Outpaint Strategy", + choices=["Center", "Corners"], + value="Corners", + type="value" + ) + + + with gr.Tab("Post proccess"): upscale_do = gr.Checkbox(False, label="Enable Upscale") upscaler_name = gr.Dropdown( @@ -358,6 +401,7 @@ Our best experience and trade-off is the R-ERSGAn4x upscaler. 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, @@ -369,6 +413,9 @@ Our best experience and trade-off is the R-ERSGAn4x upscaler. upscale_do, upscaler_name, upscale_by, + overmask, + outpaintStrategy, + outpaint_amount_px, blend_image, blend_mode, blend_gradient_size, diff --git a/iz_helpers/video.py b/iz_helpers/video.py index 9f06ca9..af5f715 100644 --- a/iz_helpers/video.py +++ b/iz_helpers/video.py @@ -70,41 +70,44 @@ def write_video(file_path, frames, fps, reversed=True, start_frame_dupe_amount=1 writer.close() - class ContinuousVideoWriter: +class ContinuousVideoWriter: - _writer = None + _writer = None - def __init__(self, file_path, initframe, fps, start_frame_dupe_amount=15): - """ - Writes initial frame to a new mp4 video file - :param file_path: Path to output video, must end with .mp4 - :param frame: Start image PIL.Image objects - :param fps: Desired frame rate - :param reversed: if order of images to be reversed (default = True) - """ + def __init__(self, file_path, initframe, fps, start_frame_dupe_amount=15, video_ffmpeg_opts="" ): + """ + Writes initial frame to a new mp4 video file + :param file_path: Path to output video, must end with .mp4 + :param frame: Start image PIL.Image objects + :param fps: Desired frame rate + :param reversed: if order of images to be reversed (default = True) + """ + ffopts = [] + if video_ffmpeg_opts is not "": + ffopts= video_ffmpeg_opts.split(" ") - writer = imageio.get_writer(file_path, fps=fps, macro_block_size=None) - start_frames = [initframe] * start_frame_dupe_amount - for f in start_frames: - writer.append_data(np.array(f)) - self._writer = writer + writer = imageio.get_writer(file_path, fps=fps, macro_block_size=None, ffmpeg_params=ffopts) + start_frames = [initframe] * start_frame_dupe_amount + for f in start_frames: + writer.append_data(np.array(f)) + self._writer = writer - def append(self, frames): - """ - Append a list of image PIL.Image objects to the end of the file. - :param frames: List of image PIL.Image objects - """ - for i,f in enumerate(frames): - self._writer.append_data(np.array(f)) + def append(self, frames): + """ + Append a list of image PIL.Image objects to the end of the file. + :param frames: List of image PIL.Image objects + """ + for i,f in enumerate(frames): + self._writer.append_data(np.array(f)) - def finish(self, frame, last_frame_dupe_amount=30 ): - """ - Closes the file writer. - """ - for i in range(last_frame_dupe_amount): - self._writer.append_data(np.array(frame)) + def finish(self, frame, last_frame_dupe_amount=30 ): + """ + Closes the file writer. + """ + for i in range(last_frame_dupe_amount): + self._writer.append_data(np.array(frame)) - self._writer.close() + self._writer.close() def add_audio_to_video(video_path, audio_path, output_path, ffmpeg_location = 'ffmpeg'): command = [ffmpeg_location, '-i', video_path, '-i', audio_path, '-c:v', 'copy', '-c:a', 'aac', '-map', '0:v:0', '-map', '1:a:0', '-shortest', output_path]