| | import modules.scripts as scripts
|
| | import gradio as gr
|
| | import os
|
| | import math
|
| | from modules import images
|
| | from modules.processing import process_images, Processed
|
| | from modules.shared import opts, cmd_opts, state
|
| | from math import floor
|
| |
|
| | class ProcessedImagesWrapper:
|
| | def __init__(self, images, schedule, script_title):
|
| | self.images = images
|
| | self.schedule = schedule
|
| | self.script_title = script_title
|
| |
|
| | def js(self):
|
| | flat_images = []
|
| | for item in self.images:
|
| | if isinstance(item, list):
|
| | flat_images.extend([img for img in item if hasattr(img, 'js')])
|
| | elif hasattr(item, 'js'):
|
| | flat_images.append(item)
|
| | return [image.js() for image in flat_images]
|
| |
|
| | @property
|
| | def info(self):
|
| | info_texts = []
|
| | if isinstance(self.images, list):
|
| | for index, image in enumerate(self.images):
|
| | if hasattr(image, 'info'):
|
| |
|
| | original_params = image.info.get('parameters', 'Parameter Missing')
|
| |
|
| | params_with_schedule = f"{original_params}, Script: {self.script_title}, Schedule: {self.schedule[index]}"
|
| | info_texts.append(params_with_schedule)
|
| | else:
|
| | return "self.images is not a list"
|
| | return "\n".join(info_texts)
|
| |
|
| | @property
|
| | def comments(self):
|
| | comments = [image.comments for image in self.images if hasattr(image, 'comments')]
|
| | return "\n".join(comments) if comments else ""
|
| |
|
| | class Script(scripts.Script):
|
| | def title(self):
|
| | return "epiCFG Schedule Type"
|
| |
|
| | def show(self, is_img2img):
|
| | return not(is_img2img)
|
| |
|
| | def ui(self, is_img2img):
|
| | schedule_options = [
|
| | 'Constant', 'Linear', 'Clamp-Linear (c=4.0)', 'Clamp-Linear (c=2.0)',
|
| | 'Clamp-Linear (c=1.0)', 'Inverse-Linear', 'PCS (s=0.01)', 'PCS (s=0.1)',
|
| | 'PCS (s=1.0)', 'PCS (s=2.0)', 'PCS (s=4.0)', 'Clamp-Cosine (c=4.0)',
|
| | 'Clamp-Cosine (c=2.0)', 'Clamp-Cosine (c=1.0)', 'Cosine', 'Sine',
|
| | 'V-Shape', 'A-Shape', 'Interval'
|
| | ]
|
| | schedule_multiselect_dropdown = gr.components.Dropdown(label="Schedule", choices=schedule_options, default="Inverse-Linear", multiselect=True)
|
| | return [schedule_multiselect_dropdown]
|
| |
|
| | def run(self, p, schedules):
|
| | strength = 1.0
|
| | processed_images = []
|
| | all_processed_images = []
|
| | script_title = self.title()
|
| |
|
| | if p.sampler_name in ('Euler a', 'Euler', 'LMS', 'DPM++ 2M', 'DPM fast', 'LMS Karras', 'DPM++ 2M Karras','DPM++ 2M SDE','DPM++ 3M SDE','Restart'):
|
| | max_mul_count = p.steps * p.batch_size
|
| | steps_per_mul = p.batch_size
|
| | elif p.sampler_name in ('Heun', 'DPM2', 'DPM2 a', 'DPM++ 2S a', 'DPM2 Karras', 'DPM2 a Karras', 'DPM++ 2S a Karras', 'DPM++ SDE', 'DPM++ SDE Karras','UniPC'):
|
| | max_mul_count = ((p.steps * 2) - 1) * p.batch_size
|
| | steps_per_mul = 2 * p.batch_size
|
| | elif p.sampler_name == 'DDIM':
|
| | max_mul_count = fix_ddim_step_count(p.steps)
|
| | steps_per_mul = 1
|
| | elif p.sampler_name == 'UniPC':
|
| | max_mul_count = fix_ddim_step_count(p.steps)
|
| | steps_per_mul = 1
|
| | elif p.sampler_name == 'PLMS':
|
| | max_mul_count = fix_ddim_step_count(p.steps) + 1
|
| | steps_per_mul = 1
|
| | else:
|
| | print('!!!warning: unsupported sampler ', p.sampler_name)
|
| | return
|
| | target_value = p.cfg_scale * (1 - strength)
|
| | saved_obj = p.cfg_scale
|
| |
|
| | for schedule in schedules:
|
| | print('\nepiCFG: ', schedule, end='\n')
|
| | p.cfg_scale = Fake_float(p.cfg_scale, target_value, max_mul_count, steps_per_mul, p.steps, schedule)
|
| | proc = process_images(p)
|
| | processed_images = process_image_to_array(proc)
|
| | all_processed_images.extend(processed_images)
|
| | p.cfg_scale = saved_obj
|
| | return ProcessedImagesWrapper(all_processed_images, schedules, script_title)
|
| |
|
| | class Fake_float(float):
|
| | def __new__(self, orig_value, target_value, max_mul_count, steps_per_mul, max_steps, schedule):
|
| | return float.__new__(self, orig_value)
|
| |
|
| | def __init__(self, orig_value, target_value, max_mul_count, steps_per_mul, max_steps, schedule):
|
| | float.__init__(orig_value)
|
| | self.orig_value = orig_value
|
| | self.target_value = target_value
|
| | self.max_mul_count = max_mul_count
|
| | self.current_mul = 0
|
| | self.steps_per_mul = steps_per_mul
|
| | self.current_step = 0
|
| | self.max_step_count = (max_mul_count // steps_per_mul) + (max_mul_count % steps_per_mul > 0)
|
| | self.max_steps = max_steps
|
| | self.schedule = schedule
|
| |
|
| | def __mul__(self,other):
|
| | return self.fake_mul(other)
|
| |
|
| | def __rmul__(self,other):
|
| | return self.fake_mul(other)
|
| |
|
| | def fake_mul(self,other):
|
| | if (self.max_step_count==1):
|
| | fake_value= self.orig_value
|
| | else:
|
| | if self.schedule == 'Constant':
|
| | fake_value = constant_schedule(self.current_step, self.max_steps, self.orig_value)
|
| | elif self.schedule == 'Linear':
|
| | fake_value = linear_schedule(self.current_step, self.max_steps, self.orig_value)
|
| | elif self.schedule == 'Clamp-Linear (c=4.0)':
|
| | fake_value = clamp_linear_schedule(self.current_step, self.max_steps, self.orig_value, 4.0)
|
| | elif self.schedule == 'Clamp-Linear (c=2.0)':
|
| | fake_value = clamp_linear_schedule(self.current_step, self.max_steps, self.orig_value, 2.0)
|
| | elif self.schedule == 'Clamp-Linear (c=1.0)':
|
| | fake_value = clamp_linear_schedule(self.current_step, self.max_steps, self.orig_value, 1.0)
|
| | elif self.schedule == 'Inverse-Linear':
|
| | fake_value = invlinear_schedule(self.current_step, self.max_steps, self.orig_value)
|
| | elif self.schedule == 'PCS (s=0.01)':
|
| | fake_value = powered_cosine_schedule(self.current_step, self.max_steps, self.orig_value, 0.01)
|
| | elif self.schedule == 'PCS (s=0.1)':
|
| | fake_value = powered_cosine_schedule(self.current_step, self.max_steps, self.orig_value, 0.1)
|
| | elif self.schedule == 'PCS (s=1.0)':
|
| | fake_value = powered_cosine_schedule(self.current_step, self.max_steps, self.orig_value, 1.0)
|
| | elif self.schedule == 'PCS (s=2.0)':
|
| | fake_value = powered_cosine_schedule(self.current_step, self.max_steps, self.orig_value, 2.0)
|
| | elif self.schedule == 'PCS (s=4.0)':
|
| | fake_value = powered_cosine_schedule(self.current_step, self.max_steps, self.orig_value, 4.0)
|
| | elif self.schedule == 'Clamp-Cosine (c=4.0)':
|
| | fake_value = clamp_cosine_schedule(self.current_step, self.max_steps, self.orig_value, 4.0)
|
| | elif self.schedule == 'Clamp-Cosine (c=2.0)':
|
| | fake_value = clamp_cosine_schedule(self.current_step, self.max_steps, self.orig_value, 2.0)
|
| | elif self.schedule == 'Clamp-Cosine (c=1.0)':
|
| | fake_value = clamp_cosine_schedule(self.current_step, self.max_steps, self.orig_value, 1.0)
|
| | elif self.schedule == 'Cosine':
|
| | fake_value = cosine_schedule(self.current_step, self.max_steps, self.orig_value)
|
| | elif self.schedule == 'Sine':
|
| | fake_value = sine_schedule(self.current_step, self.max_steps, self.orig_value)
|
| | elif self.schedule == 'V-Shape':
|
| | fake_value = v_shape_schedule(self.current_step, self.max_steps, self.orig_value)
|
| | elif self.schedule == 'A-Shape':
|
| | fake_value = a_shape_schedule(self.current_step, self.max_steps, self.orig_value)
|
| | elif self.schedule == 'Interval':
|
| | fake_value = interval_schedule(self.current_step, self.max_steps, self.orig_value, 0.25, 5.42)
|
| | else:
|
| | print(f"Invalid CFG schedule: {self.schedule}")
|
| | fake_value = self.orig_value
|
| | self.current_mul = (self.current_mul+1) % self.max_mul_count
|
| | self.current_step = (self.current_mul) // self.steps_per_mul
|
| | return fake_value * other
|
| |
|
| | def process_image_to_array(processed):
|
| | if hasattr(processed, 'images') and isinstance(processed.images, list):
|
| | return processed.images
|
| | else:
|
| | print("Processed object does not contain an iterable list of images.")
|
| | return []
|
| |
|
| | def fix_ddim_step_count(steps):
|
| | valid_step = 999 / (1000 // steps)
|
| | if valid_step == floor(valid_step): steps=int(valid_step)+1
|
| | if ((1000 % steps)!=0): steps +=1
|
| | return steps
|
| |
|
| | def constant_schedule(step: int, max_steps: int, w0: float):
|
| | return w0
|
| |
|
| | def linear_schedule(step: int, max_steps: int, w0: float):
|
| | return w0 * 2 * (1 - step / max_steps)
|
| |
|
| | def clamp_linear_schedule(step: int, max_steps: int, w0: float, c: float):
|
| | return max(c, linear_schedule(step, max_steps, w0))
|
| |
|
| | def clamp_cosine_schedule(step: int, max_steps: int, w0: float, c: float):
|
| | return max(c, cosine_schedule(step, max_steps, w0))
|
| |
|
| | def invlinear_schedule(step: int, max_steps: int, w0: float):
|
| | return w0 * 2 * (step / max_steps)
|
| |
|
| | def powered_cosine_schedule(step: int, max_steps: int, w0: float, s: float):
|
| | return w0 * ((1 - math.cos(math.pi * ((max_steps - step) / max_steps)**s))/2.0)
|
| |
|
| | def cosine_schedule(step: int, max_steps: int, w0: float):
|
| | return w0 * (1 + math.cos(math.pi * step / max_steps))
|
| |
|
| | def sine_schedule(step: int, max_steps: int, w0: float):
|
| | return w0 * (math.sin((math.pi * step / max_steps) - (math.pi / 2)) + 1)
|
| |
|
| | def v_shape_schedule(step: int, max_steps: int, w0: float):
|
| | if step < max_steps / 2:
|
| | return invlinear_schedule(step, max_steps, w0)
|
| | return linear_schedule(step, max_steps, w0)
|
| |
|
| | def a_shape_schedule(step: int, max_steps: int, w0: float):
|
| | if step < max_steps / 2:
|
| | return linear_schedule(step, max_steps, w0)
|
| | return invlinear_schedule(step, max_steps, w0)
|
| |
|
| | def interval_schedule(step: int, max_steps: int, w0: float, low: float, high: float):
|
| | if low <= step <= high:
|
| | return w0
|
| | return 1.0
|
| |
|