| | import numpy as np |
| | from tqdm import trange |
| | from PIL import Image, ImageEnhance |
| |
|
| | import modules.scripts as scripts |
| | import gradio as gr |
| |
|
| | from modules import processing, shared, sd_samplers, images |
| | from modules.processing import Processed |
| | from modules.sd_samplers import samplers |
| | from modules.shared import opts, cmd_opts, state |
| | from copy import deepcopy |
| | from math import sin, pi |
| |
|
| | class Script(scripts.Script): |
| | def title(self): |
| | return "Advanced loopback blend" |
| |
|
| | def show(self, is_img2img): |
| | return is_img2img |
| |
|
| | def ui(self, is_img2img): |
| | loops = gr.Number(minimum=1, step=1, label='Loops', value=4) |
| | use_first_image_colors = gr.Checkbox(label='Use first image colors (custom color correction) ', value=False) |
| | denoising_strength_change_factor = gr.Slider(minimum=0.9, maximum=1.1, step=0.01, label='Denoising strength change factor (overridden if proportional used)', value=1) |
| | with gr.Row(): |
| | zoom_level = gr.Slider(minimum=0, maximum=50, step=1, label='Zoom level ', value=0) |
| | zoom_blend = gr.Checkbox(label='Blend 50/50 with original when zoomed. Doesn\'t work with sine variation.', value=False) |
| | |
| | |
| | |
| | with gr.Row(): |
| | denoising_strength_first_image = gr.Number(minimum=0, step=1, label='Denoising strength start ', value=0) |
| | denoising_strength_last_image = gr.Number(minimum=0, step=1, label='Denoising strength end ', value=4) |
| | denoising_strength_min = gr.Slider(minimum=0.1, maximum=1, step=0.01, label='Denoising strength proportional change starting value ', value=0.1) |
| | denoising_strength_max = gr.Slider(minimum=0.1, maximum=1, step=0.01, label='Denoising strength proportional change ending value (0.1 = disabled) ', value=0.1) |
| | cfg_scale_min = gr.Slider(minimum=0.1, maximum=30, step=0.1, label='CFG scale proportional change starting value ', value=0.1) |
| | cfg_scale_max = gr.Slider(minimum=0.1, maximum=30, step=0.1, label='CFG scale proportional change ending value (0.1 = disabled) ', value=0.1) |
| | saturation_per_image = gr.Slider(minimum=0.99, maximum=1.01, step=0.001, label='Saturation enhancement per image ', value=1) |
| | with gr.Row(): |
| | use_sine_variation_dns = gr.Checkbox(label='Use sine denoising strength variation (CFG will be scaled with it if the slider is > 0.1)', value=True) |
| | phase_diff_denoising = gr.Slider(minimum=0, maximum=1, step=0.05, label='Phase difference', value=0) |
| | amplify_sine_variation_denoise = gr.Slider(minimum=1, maximum=10, step=1, label='Denoising strength exponentiation ', value=1) |
| | with gr.Row(): |
| | use_sine_variation_zoom = gr.Checkbox(label='Use sine zoom variation', value=False) |
| | phase_diff_zoom = gr.Slider(minimum=0, maximum=1, step=0.05, label='Phase difference', value=0) |
| | amplify_sine_variation_zoom = gr.Slider(minimum=1, maximum=10, step=1, label='Zoom exponentiation ', value=1) |
| | with gr.Row(): |
| | use_multi_prompts = gr.Checkbox(label='Use multiple prompts', value=False) |
| | same_seed_per_prompt = gr.Checkbox(label='Same seed per prompt', value=False) |
| | same_seed_always = gr.Checkbox(label='Same seed for everything', value=False) |
| | same_init_image = gr.Checkbox(label='Original init image for everything', value=False) |
| | multi_prompts = gr.Textbox(label="Multiple prompts : 1 line positive, 1 line negative, leave a blank line for no negative", lines=2, max_lines=2000) |
| | return [ |
| | loops, |
| | denoising_strength_change_factor, |
| | zoom_level, |
| | zoom_blend, |
| | |
| | |
| | |
| | denoising_strength_first_image, |
| | denoising_strength_last_image, |
| | denoising_strength_min, |
| | denoising_strength_max, |
| | cfg_scale_min, |
| | cfg_scale_max, |
| | saturation_per_image, |
| | use_first_image_colors, |
| | use_sine_variation_dns, |
| | use_sine_variation_zoom, |
| | phase_diff_zoom, |
| | use_multi_prompts, |
| | multi_prompts, |
| | amplify_sine_variation_zoom, |
| | same_seed_per_prompt, |
| | phase_diff_denoising, |
| | amplify_sine_variation_denoise, |
| | same_seed_always, |
| | same_init_image |
| | ] |
| |
|
| | def zoom_into(self, img, zoom): |
| | w, h = img.size |
| | img = img.crop((zoom,zoom,w-zoom,h-zoom)) |
| | return img.resize((w, h), Image.LANCZOS) |
| |
|
| | def run(self, p, |
| | loops, |
| | denoising_strength_change_factor, |
| | zoom_level, |
| | zoom_blend, |
| | |
| | |
| | |
| | denoising_strength_first_image, |
| | denoising_strength_last_image, |
| | denoising_strength_min, |
| | denoising_strength_max, |
| | cfg_scale_min, |
| | cfg_scale_max, |
| | saturation_per_image, |
| | use_first_image_colors, |
| | use_sine_variation_dns, |
| | use_sine_variation_zoom, |
| | phase_diff_zoom, |
| | use_multi_prompts, |
| | multi_prompts, |
| | amplify_sine_variation_zoom, |
| | same_seed_per_prompt, |
| | phase_diff_denoising, |
| | amplify_sine_variation_denoise, |
| | same_seed_always, |
| | same_init_image |
| | ): |
| |
|
| | ppos = [] |
| | pneg = [] |
| | if use_multi_prompts : |
| | prompts_list = multi_prompts.splitlines() |
| | oddeven = lambda x: 1 if x%2==0 else 0 |
| | for x in range(len(prompts_list)) : |
| | if oddeven(x): |
| | ppos.append(prompts_list[x]) |
| | else: |
| | pneg.append(prompts_list[x]) |
| | if len(pneg) < len(ppos) : |
| | pneg.append("") |
| |
|
| | def remap_range(value, minIn, MaxIn, minOut, maxOut): |
| | if value > MaxIn: value = MaxIn; |
| | if value < minIn: value = minIn; |
| | finalValue = ((value - minIn) / (MaxIn - minIn)) * (maxOut - minOut) + minOut; |
| | return finalValue; |
| |
|
| | def get_sin_steps(i,amplify,phase_diff=0): |
| | i -= denoising_strength_first_image |
| | range = (denoising_strength_last_image - denoising_strength_first_image) |
| | x = i % (range) |
| | y = remap_range(x,0,range,0,1) |
| | y = y ** amplify |
| | z = sin((y+phase_diff/2)*pi) |
| | return z |
| |
|
| | processing.fix_seed(p) |
| | batch_count = p.n_iter |
| | p.extra_generation_params = { |
| | "Denoising strength change factor": denoising_strength_change_factor, |
| | 'Denoising strength proportional change start image':denoising_strength_first_image, |
| | 'Denoising strength proportional change end image':denoising_strength_last_image, |
| | 'Denoising strength proportional change starting value':denoising_strength_min, |
| | 'Denoising strength proportional change ending value':denoising_strength_max, |
| | 'CFG min':cfg_scale_min, |
| | 'CFG max':cfg_scale_max, |
| | 'use first image colors': use_first_image_colors, |
| | 'Saturation enhancement per image':saturation_per_image, |
| | 'Zoom level':zoom_level, |
| | } |
| |
|
| | p.batch_size = 1 |
| | p.n_iter = 1 |
| |
|
| | output_images, info = None, None |
| | initial_seed = None |
| | initial_info = None |
| |
|
| | grids = [] |
| | all_images = [] |
| | state.job_count = loops * batch_count |
| |
|
| | original_image = p.init_images[0].copy() |
| | original_image_for_zoom = p.init_images[0].copy() |
| | if opts.img2img_color_correction: |
| | p.color_corrections = [processing.setup_color_correction(p.init_images[0])] |
| |
|
| |
|
| | for n in range(batch_count): |
| | history = [] |
| | multi_prompts_index = 0 |
| | loops = round(loops) |
| | for i in range(loops): |
| | p.n_iter = 1 |
| | p.batch_size = 1 |
| | p.do_not_save_grid = True |
| |
|
| | if use_multi_prompts : |
| | image_range = (denoising_strength_last_image - denoising_strength_first_image) |
| | il = i % (image_range) |
| | if i == 0: |
| | p.prompt = ppos[multi_prompts_index] |
| | p.negative_prompt = pneg[multi_prompts_index] |
| | print("Prompt :",p.prompt) |
| | print("Negative prompt :",p.negative_prompt) |
| | if il == 0 and i > 0: |
| | multi_prompts_index+=1 |
| | try: |
| | if same_seed_per_prompt: |
| | if not same_seed_always: |
| | p.subseed = p.subseed + 1 if p.subseed_strength > 0 else p.subseed |
| | p.seed = p.seed + 1 if p.subseed_strength == 0 else p.seed |
| | p.prompt = ppos[multi_prompts_index] |
| | p.negative_prompt = pneg[multi_prompts_index] |
| | except Exception as e: |
| | multi_prompts_index = 0 |
| | if same_seed_per_prompt: |
| | if not same_seed_always: |
| | p.subseed = p.subseed + 1 if p.subseed_strength > 0 else p.subseed |
| | p.seed = p.seed + 1 if p.subseed_strength == 0 else p.seed |
| | p.prompt = ppos[multi_prompts_index] |
| | p.negative_prompt = pneg[multi_prompts_index] |
| | |
| | |
| |
|
| | if use_first_image_colors: |
| | p.color_corrections = [processing.setup_color_correction(original_image)] |
| |
|
| | state.job = f"Iteration {i + 1}/{loops}, batch {n + 1}/{batch_count}" |
| |
|
| | if denoising_strength_max > 0.1 : |
| | if use_sine_variation_dns : |
| | ds = remap_range(get_sin_steps(i,amplify_sine_variation_denoise,phase_diff_denoising),0,1,denoising_strength_min,denoising_strength_max) |
| | else: |
| | ds = remap_range(i+1,denoising_strength_first_image,denoising_strength_last_image,denoising_strength_min,denoising_strength_max) |
| | p.denoising_strength = round(ds,3) |
| | print("Denoising strength : "+str(p.denoising_strength)) |
| |
|
| | if cfg_scale_max > 0.1 : |
| | if use_sine_variation_dns : |
| | cfgs = remap_range(get_sin_steps(i,amplify_sine_variation_denoise,phase_diff_denoising),0,1,cfg_scale_min,cfg_scale_max) |
| | else: |
| | cfgs = remap_range(i+1,denoising_strength_first_image,denoising_strength_last_image,cfg_scale_min,cfg_scale_max) |
| | p.cfg_scale = round(cfgs,2) |
| | print("CFG scale : "+str(p.cfg_scale)) |
| |
|
| | processed = processing.process_images(p) |
| | if zoom_level > 0: |
| | if use_sine_variation_zoom : |
| | if loops >= denoising_strength_first_image : |
| | z = remap_range(get_sin_steps(i,amplify_sine_variation_zoom,phase_diff_zoom),0,1,1,zoom_level) |
| | processed.images[0] = self.zoom_into(processed.images[0], z) |
| | print("Zoom level :",z) |
| | else: |
| | processed.images[0] = self.zoom_into(processed.images[0], zoom_level) |
| | if zoom_blend: |
| | |
| | |
| | original_image_zoomed = self.zoom_into(original_image_for_zoom.copy(), zoom_level*(i+1)) |
| | processed.images[0] = Image.blend(original_image_zoomed.copy().convert('RGB').resize(processed.images[0].size, Image.LANCZOS), processed.images[0].copy().convert('RGB'), alpha=0.5) |
| |
|
| |
|
| | if initial_seed is None: |
| | initial_seed = processed.seed |
| | initial_info = processed.info |
| |
|
| | if not same_init_image : |
| | init_img = processed.images[0] |
| | else: |
| | init_img = original_image |
| |
|
| | if saturation_per_image != 1 : |
| | init_img = ImageEnhance.Color(init_img).enhance(saturation_per_image) |
| |
|
| | p.init_images = [init_img] |
| | if not same_seed_per_prompt: |
| | if not same_seed_always: |
| | p.subseed = p.subseed + 1 if p.subseed_strength > 0 else p.subseed |
| | p.seed = p.seed + 1 if p.subseed_strength == 0 else p.seed |
| | p.denoising_strength = min(max(p.denoising_strength * denoising_strength_change_factor, 0.1), 1) |
| | history.append(processed.images[0]) |
| | if state.interrupted: |
| | break |
| |
|
| | grid = images.image_grid(history, rows=1) |
| | if opts.grid_save: |
| | images.save_image(grid, p.outpath_grids, "grid", initial_seed, p.prompt, opts.grid_format, info=info, short_filename=not opts.grid_extended_filename, grid=True, p=p) |
| |
|
| | grids.append(grid) |
| | all_images += history |
| |
|
| | if opts.return_grid: |
| | all_images = grids + all_images |
| |
|
| | processed = Processed(p, all_images, initial_seed, initial_info) |
| |
|
| | return processed |
| |
|