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 # Store the 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): # Check if self.images is indeed a list for index, image in enumerate(self.images): if hasattr(image, 'info'): # Append the schedule type to the existing parameters original_params = image.info.get('parameters', 'Parameter Missing') # Add the schedule type to the existing parameters 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 # Fixed strength value processed_images = [] all_processed_images = [] script_title = self.title() # Get the title from the title method 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