diff --git a/iz_helpers/image.py b/iz_helpers/image.py index d84901a..8be2578 100644 --- a/iz_helpers/image.py +++ b/iz_helpers/image.py @@ -1,4 +1,8 @@ from PIL import Image +import requests +import base64 +from io import BytesIO + def shrink_and_paste_on_blank(current_image, mask_width, mask_height): """ @@ -21,3 +25,20 @@ def shrink_and_paste_on_blank(current_image, mask_width, mask_height): blank_image.paste(prev_image, (mask_width, mask_height)) return blank_image + + +def open_image(image_path): + if image_path.startswith('http'): + # If the image path is a URL, download the image using requests + response = requests.get(image_path) + img = Image.open(BytesIO(response.content)) + elif image_path.startswith('data'): + # If the image path is a DataURL, decode the base64 string + encoded_data = image_path.split(',')[1] + decoded_data = base64.b64decode(encoded_data) + img = Image.open(BytesIO(decoded_data)) + else: + # Assume that the image path is a file path + img = Image.open(image_path) + + return img diff --git a/iz_helpers/promptschema.json b/iz_helpers/promptschema.json index b7fcfd4..ef0d1a1 100644 --- a/iz_helpers/promptschema.json +++ b/iz_helpers/promptschema.json @@ -21,6 +21,9 @@ } ] }, + { + "type": "string" + }, { "type": "string" } @@ -36,7 +39,7 @@ "items": { "type": "string" }, - "minItems": 2 + "minItems": 3 } }, "required": ["data", "headers"] diff --git a/iz_helpers/run.py b/iz_helpers/run.py index 3a8d97e..ba13af4 100644 --- a/iz_helpers/run.py +++ b/iz_helpers/run.py @@ -11,7 +11,7 @@ from .helpers import ( do_upscaleImg, ) from .sd_helpers import renderImg2Img, renderTxt2Img -from .image import shrink_and_paste_on_blank +from .image import shrink_and_paste_on_blank, open_image from .video import write_video @@ -111,14 +111,20 @@ def create_zoom_single( fix_env_Path_ffprobe() prompts = {} + prompt_images = {} + for x in prompts_array: try: key = int(x[0]) value = str(x[1]) + file_loc = str(x[2]) prompts[key] = value + prompt_images[key] = file_loc except ValueError: pass assert len(prompts_array) > 0, "prompts is empty" + print(str(len(prompts)) + " prompts found") + print(str(len(prompt_images)) + " prompts Images found") width = closest_upper_divisible_by_eight(outputsizeW) height = closest_upper_divisible_by_eight(outputsizeH) @@ -128,6 +134,7 @@ def create_zoom_single( mask_image = Image.fromarray(255 - mask_image).convert("RGB") current_image = current_image.convert("RGB") current_seed = seed + extra_frames = 0 if custom_init_image: current_image = custom_init_image.resize( @@ -135,23 +142,26 @@ def create_zoom_single( ) print("using Custom Initial Image") else: - load_model_from_setting( - "infzoom_txt2img_model", progress, "Loading Model for txt2img: " - ) + if prompt_images[min(k for k in prompt_images.keys() if k >= 0)] == "": + 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, - ) - if(len(processed.images) > 0): + processed, current_seed = 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 + else: + current_image = open_image(prompt_images[min(k for k in prompt_images.keys() if k >= 0)]).resize( + (width, height), resample=Image.LANCZOS + ) mask_width = math.trunc(width / 4) # was initially 512px => 128px mask_height = math.trunc(height / 4) # was initially 512px => 128px @@ -169,16 +179,17 @@ def create_zoom_single( else current_image ) - load_model_from_setting( - "infzoom_inpainting_model", progress, "Loading Model for inpainting/img2img: " - ) + load_model_from_setting("infzoom_inpainting_model", progress, "Loading Model for inpainting/img2img: " ) - for i in range(num_outpainting_steps): + if custom_exit_image: + extra_frames += 2 + + for i in range(num_outpainting_steps + extra_frames): print_out = ( "Outpaint step: " + str(i + 1) + " / " - + str(num_outpainting_steps) + + str(num_outpainting_steps + extra_frames) + " Seed: " + str(current_seed) ) @@ -197,34 +208,40 @@ def create_zoom_single( # inpainting step current_image = current_image.convert("RGB") - if custom_exit_image and ((i + 1) == num_outpainting_steps): + # Custom and specified images work like keyframes + if custom_exit_image and (i + 1) >= (num_outpainting_steps + extra_frames): 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, - ) - if(len(processed.images) > 0): + else: + if prompt_images[max(k for k in prompt_images.keys() if k <= (i + 1))] == "": + processed, current_seed = renderImg2Img( + prompts[max(k for k in prompts.keys() if k <= (i + 1))], + 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 - if(len(processed.images) > 0): - current_image.paste(prev_image, mask=prev_image) + # only paste previous image when generating a new image + current_image.paste(prev_image, mask=prev_image) + else: + current_image = open_image(prompt_images[max(k for k in prompt_images.keys() if k <= (i + 1))]).resize( + (width, height), resample=Image.LANCZOS + ) + + # interpolation steps between 2 inpainted images (=sequential zoom and crop) for j in range(num_interpol_frames - 1): @@ -305,6 +322,7 @@ def create_zoom_single( save_path = os.path.join( output_path, shared.opts.data.get("infzoom_outSUBpath", "infinite-zooms") ) + print("save to: " + save_path) if not os.path.exists(save_path): os.makedirs(save_path) out = os.path.join(save_path, video_file_name) diff --git a/iz_helpers/static_variables.py b/iz_helpers/static_variables.py index ac25cb1..9328eff 100644 --- a/iz_helpers/static_variables.py +++ b/iz_helpers/static_variables.py @@ -5,9 +5,9 @@ import modules.sd_samplers default_prompt = """ { "prompts":{ - "headers":["outpaint steps","prompt"], + "headers":["outpaint steps","prompt","image location"], "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, Alex Horley Wenjun Lin greg rutkowski Ruan Jia (Wayne Barlowe:1.2) ","C:\\path\\to\\image.png"] ] }, "negPrompt":"frames, borderline, text, character, duplicate, error, out of frame, watermark, low quality, ugly, deformed, blur bad-artist" @@ -18,13 +18,13 @@ available_samplers = [ ] empty_prompt = ( - '{"prompts":{"data":[],"headers":["outpaint steps","prompt"]},"negPrompt":""}' + '{"prompts":{"data":[],"headers":["outpaint steps","prompt","image location"]},"negPrompt":""}' ) invalid_prompt = { "prompts": { - "data": [[0, "Your prompt-json is invalid, please check Settings"]], - "headers": ["outpaint steps", "prompt"], + "data": [[0, "Your prompt-json is invalid, please check Settings",""]], + "headers": ["outpaint steps", "prompt","image location"], }, "negPrompt": "Invalid prompt-json", } diff --git a/iz_helpers/ui.py b/iz_helpers/ui.py index 9729631..0d12101 100644 --- a/iz_helpers/ui.py +++ b/iz_helpers/ui.py @@ -51,10 +51,10 @@ def on_ui_tabs(): main_prompts = gr.Dataframe( type="array", - headers=["outpaint step", "prompt"], - datatype=["number", "str"], + headers=["outpaint step", "prompt", "image location"], + datatype=["number", "str", "str"], row_count=1, - col_count=(2, "fixed"), + col_count=(3, "fixed"), value=jpr["prompts"], wrap=True, )