import diffusers
import gradio as gr
from modules import scripts, processing, shared, devices, sd_models
class Script(scripts.Script):
def title(self):
return 'LEdits++'
def show(self, is_img2img):
return is_img2img if shared.backend == shared.Backend.DIFFUSERS else False
# return signature is array of gradio components
def ui(self, _is_img2img):
with gr.Row():
gr.HTML('  LEdits++
')
with gr.Row():
edit_start = gr.Slider(label='Edit start', minimum=0.0, maximum=1.0, step=0.01, value=0.1)
edit_stop = gr.Slider(label='Edit stop', minimum=0.0, maximum=1.0, step=0.01, value=1.0)
intersect_mask = gr.Checkbox(label='Smooth mask', value=True)
with gr.Row():
prompt1 = gr.Textbox(show_label=False, placeholder='Positive prompt')
scale1 = gr.Slider(label='Scale', minimum=0.0, maximum=1.0, step=0.01, value=0.5)
threshold1 = gr.Slider(label='Threshold', minimum=0.0, maximum=1.0, step=0.01, value=0.9)
with gr.Row():
prompt2 = gr.Textbox(show_label=False, placeholder='Negative prompt')
scale2 = gr.Slider(label='Scale', minimum=0.0, maximum=1.0, step=0.01, value=0.5)
threshold2 = gr.Slider(label='Threshold', minimum=0.0, maximum=1.0, step=0.01, value=0.9)
return [edit_start, edit_stop, intersect_mask, prompt1, scale1, threshold1, prompt2, scale2, threshold2]
def run(self, p: processing.StableDiffusionProcessing, edit_start, edit_stop, intersect_mask, prompt1, scale1, threshold1, prompt2, scale2, threshold2): # pylint: disable=arguments-differ, unused-argument
image = getattr(p, 'init_images', None)
if len(prompt1) == 0 and len(prompt2) == 0:
shared.log.error('LEdits: no prompts')
return None
if image is None or len(image) == 0:
shared.log.error('LEdits: no init_images')
return None
if shared.sd_model_type != 'sd' and shared.sd_model_type != 'sdxl':
shared.log.error(f'LEdits: invalid model type: {shared.sd_model_type}')
return None
orig_pipeline = shared.sd_model
orig_offload = shared.opts.diffusers_model_cpu_offload
orig_prompt_attention = shared.opts.prompt_attention
shared.opts.data['diffusers_model_cpu_offload'] = False
shared.opts.data['prompt_attention'] = 'Fixed attention'
# shared.sd_model.maybe_free_model_hooks() # ledits is not compatible with offloading
# shared.sd_model.has_accelerate = False
sd_models.move_model(shared.sd_model, devices.device, force=True)
if shared.sd_model_type == 'sd':
shared.sd_model = sd_models.switch_pipe(diffusers.LEditsPPPipelineStableDiffusion, shared.sd_model)
elif shared.sd_model_type == 'sdxl':
shared.sd_model = sd_models.switch_pipe(diffusers.LEditsPPPipelineStableDiffusionXL, shared.sd_model)
if str(devices.dtype) == 'torch.float16':
shared.sd_model.vae.config.force_upcast = False # not compatible
shared.sd_model.scheduler = diffusers.DPMSolverMultistepScheduler.from_config(shared.sd_model.scheduler.config, algorithm_type="sde-dpmsolver++", solver_order=2) # ledits is very picky
p.sampler_name = 'Default'
p.init() # run init early to take care of resizing
invert_args = {
'image': p.init_images[0],
'source_prompt': p.prompt,
'source_guidance_scale': p.cfg_scale,
'num_inversion_steps': p.steps,
'skip': 1.0 - p.denoising_strength, # invert start
'generator': None, # not supported
}
shared.log.info(f'LEdits invert: {invert_args}')
_output = shared.sd_model.invert(**invert_args)
p.task_args = {
'editing_prompt': [],
'reverse_editing_direction': [],
'edit_guidance_scale': [],
'edit_threshold': [],
'edit_warmup_steps': int(edit_start * p.steps),
'edit_cooldown_steps': int((1.0 - edit_stop) * p.steps) if edit_stop < 1.0 else None,
'use_intersect_mask': intersect_mask, # smoothing?
'generator': None,
'guidance_rescale': 0.0, # bug in pipeline if guidance rescale is enabled
}
if len(prompt1) > 0:
p.task_args['editing_prompt'].append(prompt1)
p.task_args['reverse_editing_direction'].append(False)
p.task_args['edit_guidance_scale'].append(10.0 * scale1)
p.task_args['edit_threshold'].append(threshold1)
if len(prompt2) > 0:
p.task_args['editing_prompt'].append(prompt2)
p.task_args['reverse_editing_direction'].append(True)
p.task_args['edit_guidance_scale'].append(10.0 * scale2)
p.task_args['edit_threshold'].append(threshold2)
shared.log.info(f'LEdits: {p.task_args}')
processed = processing.process_images(p)
# restore pipeline
shared.sd_model = orig_pipeline
shared.opts.data['prompt_attention'] = orig_prompt_attention
shared.opts.data['diffusers_model_cpu_offload'] = orig_offload
return processed