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