| import copy |
| import os |
| import shutil |
|
|
| import cv2 |
| import gradio as gr |
| import numpy as np |
| import modules.scripts as scripts |
|
|
| from modules import images, processing |
| from modules.processing import process_images, Processed |
| from modules.shared import opts |
| from PIL import Image, ImageFilter, ImageColor, ImageOps |
| from pathlib import Path |
| from typing import List, Tuple, Iterable |
|
|
|
|
| |
| def get_all_frames_from_path(path): |
| if not os.path.isdir(path): |
| return None |
| frame_list = [] |
| for filename in sorted(os.listdir(path)): |
| if filename.endswith(".jpg") or filename.endswith(".png"): |
| img_path = os.path.join(path, filename) |
| img = cv2.imread(img_path) |
| if img is not None: |
| frame_list.append(img) |
| frame_list.insert(0, frame_list[0]) |
| return frame_list |
| |
|
|
| |
| def get_images_from_path(path): |
| if not os.path.isdir(path): |
| return None |
| images = [] |
| for filename in os.listdir(path): |
| if filename.endswith('.jpg') or filename.endswith('.png'): |
| img_path = os.path.join(path, filename) |
| img = Image.open(img_path) |
| images.append(img) |
| images.append(images[-1]) |
| images.insert(0, images[0]) |
| return images |
|
|
| |
| def get_min_frame_num(video_list): |
| min_frame_num = -1 |
| for video in video_list: |
| if video is None: |
| continue |
| else: |
| frame_num = len(video) |
| print(frame_num) |
| if min_frame_num < 0: |
| min_frame_num = frame_num |
| elif frame_num < min_frame_num: |
| min_frame_num = frame_num |
| return min_frame_num |
|
|
|
|
| |
|
|
|
|
| def basic(target, blend, opacity): |
| return target * opacity + blend * (1-opacity) |
|
|
| def blender(func): |
| def blend(target, blend, opacity=1, *args): |
| res = func(target, blend, *args) |
| res = basic(res, blend, opacity) |
| return np.clip(res, 0, 1) |
| return blend |
|
|
|
|
| class Blend: |
| @classmethod |
| def method(cls, name): |
| return getattr(cls, name) |
| |
| normal = basic |
| |
| @staticmethod |
| @blender |
| def darken(target, blend, *args): |
| return np.minimum(target, blend) |
| |
| @staticmethod |
| @blender |
| def multiply(target, blend, *args): |
| return target * blend |
| |
| @staticmethod |
| @blender |
| def color_burn(target, blend, *args): |
| return 1 - (1-target)/blend |
| |
| @staticmethod |
| @blender |
| def linear_burn(target, blend, *args): |
| return target+blend-1 |
| |
| @staticmethod |
| @blender |
| def lighten(target, blend, *args): |
| return np.maximum(target, blend) |
| |
| @staticmethod |
| @blender |
| def screen(target, blend, *args): |
| return 1 - (1-target) * (1-blend) |
| |
| @staticmethod |
| @blender |
| def color_dodge(target, blend, *args): |
| return target/(1-blend) |
| |
| @staticmethod |
| @blender |
| def linear_dodge(target, blend, *args): |
| return target+blend |
| |
| @staticmethod |
| @blender |
| def overlay(target, blend, *args): |
| return (target>0.5) * (1-(2-2*target)*(1-blend)) +\ |
| (target<=0.5) * (2*target*blend) |
| |
| @staticmethod |
| @blender |
| def soft_light(target, blend, *args): |
| return (blend>0.5) * (1 - (1-target)*(1-(blend-0.5))) +\ |
| (blend<=0.5) * (target*(blend+0.5)) |
| |
| @staticmethod |
| @blender |
| def hard_light(target, blend, *args): |
| return (blend>0.5) * (1 - (1-target)*(2-2*blend)) +\ |
| (blend<=0.5) * (2*target*blend) |
| |
| @staticmethod |
| @blender |
| def vivid_light(target, blend, *args): |
| return (blend>0.5) * (1 - (1-target)/(2*blend-1)) +\ |
| (blend<=0.5) * (target/(1-2*blend)) |
| |
| @staticmethod |
| @blender |
| def linear_light(target, blend, *args): |
| return (blend>0.5) * (target + 2*(blend-0.5)) +\ |
| (blend<=0.5) * (target + 2*blend) |
| |
| @staticmethod |
| @blender |
| def pin_light(target, blend, *args): |
| return (blend>0.5) * np.maximum(target,2*(blend-0.5)) +\ |
| (blend<=0.5) * np.minimum(target,2*blend) |
| |
| @staticmethod |
| @blender |
| def difference(target, blend, *args): |
| return np.abs(target - blend) |
| |
| @staticmethod |
| @blender |
| def exclusion(target, blend, *args): |
| return 0.5 - 2*(target-0.5)*(blend-0.5) |
|
|
| blend_methods = [i for i in Blend.__dict__.keys() if i[0]!='_' and i!='method'] |
|
|
|
|
|
|
| def blend_images(base_img, blend_img, blend_method, blend_opacity, do_invert): |
| |
| img_base = np.array(base_img.convert("RGB")).astype(np.float64)/255 |
| |
| if do_invert: |
| img_to_blend = ImageOps.invert(blend_img.convert('RGB')) |
| else: |
| img_to_blend = blend_img |
| |
| img_to_blend = img_to_blend.resize((int(base_img.width), int(base_img.height))) |
| |
| img_to_blend = np.array(img_to_blend.convert("RGB")).astype(np.float64)/255 |
| |
| img_blended = Blend.method(blend_method)(img_to_blend, img_base, blend_opacity) |
| |
| img_blended *= 255 |
| |
| img_blended = Image.fromarray(img_blended.astype(np.uint8), mode='RGB') |
| |
| return img_blended |
|
|
|
|
| |
| class Script(scripts.Script): |
| |
| def title(self): |
| return "controlnet I2I sequence_toyxyz_v2" |
|
|
| def show(self, is_img2img): |
| return is_img2img |
|
|
| def ui(self, is_img2img): |
|
|
| ctrls_group = () |
| max_models = opts.data.get("control_net_max_models_num", 1) |
| |
| input_list = [] |
|
|
| with gr.Group(): |
| with gr.Accordion("ControlNet-I2I-sequence-toyxyz", open = True): |
| with gr.Column(): |
| |
| feed_prev_frame = gr.Checkbox(value=False, label="Feed previous frame / Reduce flickering by feeding the previous frame image generated by Img2Img") |
| |
| use_init_img = gr.Checkbox(value=False, label="Blend color image / Blend the color image sequence with the initial Img2Img image or previous frame") |
| |
| use_TemporalNet = gr.Checkbox(value=False, label="Use TemporalNet / Using TemporalNet to reduce flicker between image sequences. Add TemporalNet in addition to the multi-controlnet you need. It should be placed at the end of the controlnet list.") |
|
|
| blendmode = gr.Dropdown(blend_methods, value='normal', label='Blend mode / Choose how to blend the color image with the Previous frame or Img2Img initial image') |
| |
| opacityvalue = gr.Slider(0, 1, value=0, label="Opacity / Previous frame or Img2Img initial image + (color image * opacity)", info="Choose betwen 0 and 1") |
| |
|
|
| for i in range(max_models): |
| input_path = gr.Textbox(label=f"ControlNet-{i}", placeholder="image sequence path") |
| input_list.append(input_path) |
| |
| tone_image_path = gr.Textbox(label=f"Color_Image / Color images to be used for Img2Img in sequence", placeholder="image sequence path") |
| |
| output_path = gr.Textbox(label=f"Output_path / Deletes the contents located in the path, and creates a new path if it does not exist", placeholder="Output path") |
|
|
| ctrls_group += tuple(input_list) + (use_TemporalNet, use_init_img, opacityvalue, blendmode, feed_prev_frame, tone_image_path, output_path) |
|
|
| return ctrls_group |
| |
|
|
|
|
| |
| def run(self, p, *args): |
|
|
| path = p.outpath_samples |
| |
| output_path = args[-1] |
| |
| feedprev = args[-3] |
| |
| blendm = args[-4] |
| |
| opacityval = args[-5] |
| |
| useinit = args[-6] |
|
|
| usetempo = args[-7] |
| |
| |
| |
| if os.path.isdir(output_path): |
| for file in os.scandir(output_path): |
| os.remove(file.path) |
| else : |
| os.mkdir(output_path) |
|
|
| |
| video_num = opts.data.get("control_net_max_models_num", 1) |
|
|
| |
| image_list = [get_all_frames_from_path(image) for image in args[:video_num]] |
| |
| |
| color_image_list = get_images_from_path(args[-2]) |
| |
| |
| previmg = p.init_images |
|
|
| tempoimg = p.init_images[0] |
| |
| |
| initial_color_corrections = [processing.setup_color_correction(p.init_images[0])] |
| |
| |
| initial_image = p.init_images[0] |
| |
| |
| frame_num = get_min_frame_num(image_list) |
|
|
| |
| if frame_num > 0: |
| output_image_list = [] |
| |
| for frame in range(frame_num): |
| copy_p = copy.copy(p) |
| copy_p.control_net_input_image = [] |
| for video in image_list: |
| if video is None: |
| continue |
| copy_p.control_net_input_image.append(video[frame]) |
|
|
| if usetempo == True : |
| copy_p.control_net_input_image.append(tempoimg) |
|
|
| |
| if color_image_list and feedprev == False: |
| |
| if frame<len(color_image_list): |
| tone_image = color_image_list[frame+1] |
| |
| if useinit: |
| tone_image = blend_images(initial_image, tone_image, blendm, opacityval, False) |
| |
| p.init_images = [tone_image.convert("RGB")] |
| |
| proc = process_images(copy_p) |
| |
| |
| |
| if feedprev == True and useinit == False: |
| if previmg is None: |
| continue |
| else: |
| previmg = proc.images[0] |
| |
| if frame == 0: |
| previmg = initial_image |
| |
| p.init_images = [previmg] |
| |
| if opts.img2img_color_correction: |
| p.color_corrections = initial_color_corrections |
| |
| |
| if feedprev == True and color_image_list and useinit: |
| if previmg is None: |
| continue |
| else: |
| previmg = proc.images[0] |
| |
| if frame == 0: |
| previmg = initial_image |
| |
| previmg = blend_images(previmg, color_image_list[frame+1], blendm, opacityval, False) |
|
|
| |
| p.init_images = [previmg] |
|
|
| if opts.img2img_color_correction: |
| p.color_corrections = initial_color_corrections |
|
|
| img = proc.images[0] |
|
|
| if usetempo == True : |
| if frame > 0 : |
| tempoimg = proc.images[0] |
|
|
|
|
| |
| if(frame>0): |
| images.save_image(img, output_path, f"Frame_{frame}") |
| copy_p.close() |
| |
|
|
| else: |
| proc = process_images(p) |
| |
| return proc |