Skip to content

Instantly share code, notes, and snippets.

@continue-revolution
Forked from KohakuBlueleaf/README.md
Last active January 5, 2024 21:30
Show Gist options
  • Select an option

  • Save continue-revolution/f656e1e1f5efb76a12409c6411859be1 to your computer and use it in GitHub Desktop.

Select an option

Save continue-revolution/f656e1e1f5efb76a12409c6411859be1 to your computer and use it in GitHub Desktop.
A standalone scripts to run img generating in stable-diffusion-webui

Sd-WebUI standalone script

just throw the script into the root folder of sd-webui. and then modify the content to what you want to run. And then run it.

If you want to use some extension, just use my implementation as example (to modify the args for extensions)

import sys
from typing import Optional
sys.argv.append('--xformers')
sys.argv.append('--no-half-vae')
from contextlib import closing
import numpy as np
import torch
torch.set_float32_matmul_precision('medium')
from fastapi import FastAPI
from modules import initialize_util
from modules import initialize
def create_api(app):
from modules.api.api import Api
from modules.call_queue import queue_lock
api = Api(app, queue_lock)
return api
t2i_script = None
args = None
api = None
initialize.imports()
initialize.check_versions()
def init():
global api, t2i_script, args
from modules import script_callbacks, scripts, ui
initialize.initialize()
app = FastAPI()
initialize_util.setup_middleware(app)
api = create_api(app)
from modules import script_callbacks, shared
script_callbacks.before_ui_callback()
script_callbacks.app_started_callback(None, app)
scripts.scripts_txt2img.initialize_scripts(False)
assert shared.opts is not None
ui.create_ui()
t2i_script = scripts.scripts_txt2img.alwayson_scripts
args = api.init_default_script_args(scripts.scripts_txt2img)
return t2i_script, args
def load_model(filename):
from modules import shared
from modules.sd_models import CheckpointInfo, reload_model_weights
if filename == shared.sd_model.sd_model_checkpoint:
return
reload_model_weights(shared.sd_model, CheckpointInfo(filename))
def enable_animatediff(
alwayson_scripts,
args,
enable=True,
model="mm_sd15_v3.safetensors",
video_length=0,
fps=8,
loop_number=0,
closed_loop='R-P',
batch_size=16,
stride=1,
overlap=-1,
format=["GIF", "PNG"],
interp='Off',
interp_x=10,
video_source=None,
video_path='',
latent_power=1,
latent_scale=32,
last_frame=None,
latent_power_last=1,
latent_scale_last=32,
):
for script in alwayson_scripts:
if script.name == 'animatediff':
target = script
break
else:
return args
assert model
# Thx to animatediff extension, we only have 1 arg here
animatediff_arg = args[target.args_from]
animatediff_arg.model = model
animatediff_arg.enable = enable
animatediff_arg.video_length = video_length
animatediff_arg.fps = fps
animatediff_arg.loop_number = loop_number
animatediff_arg.closed_loop = closed_loop
animatediff_arg.batch_size = batch_size
animatediff_arg.stride = stride
animatediff_arg.overlap = overlap
animatediff_arg.format = format
animatediff_arg.interp = interp
animatediff_arg.interp_x = interp_x
animatediff_arg.video_source = video_source
animatediff_arg.video_path = video_path
animatediff_arg.latent_power = latent_power
animatediff_arg.latent_scale = latent_scale
animatediff_arg.last_frame = last_frame
animatediff_arg.latent_power_last = latent_power_last
animatediff_arg.latent_scale_last = latent_scale_last
args[script.args_from] = animatediff_arg
return args
def enable_controlnet(
alwayson_scripts,
args,
input_mode: str = "batch",
batch_images: str = "",
module: Optional[str] = None,
model: Optional[str] = None,
weight: float = 1.0,
image: Optional[np.ndarray] = None,
resize_mode: str = "Crop and Resize",
processor_res: int = 512,
threshold_a: float = -1,
threshold_b: float = -1,
guidance_start: float = 0.0,
guidance_end: float = 1.0,
pixel_perfect: bool = True,
control_mode: str = 'Balanced',
):
assert model and module
for script in alwayson_scripts:
if script.name == 'controlnet':
target = script
break
else:
return args
if target.args_to - target.args_from:
remove = target.args_to - target.args_from
for cnet_arg in args[target.args_from:target.args_to]:
if cnet_arg.enabled:
remove -= 1
if remove:
for _ in range(remove):
args.pop(target.args_to-1)
target.args_to -= 1
shift = False
for script in alwayson_scripts:
if shift:
script.args_from -= remove
script.args_to -= remove
if script.name == 'controlnet':
shift = True
target.args_to += 1
shift = False
for script in alwayson_scripts:
if shift:
script.args_from += 1
script.args_to += 1
if script.name == 'controlnet':
shift = True
from scripts.controlnet_ui.controlnet_ui_group import UiControlNetUnit
from scripts.batch_hijack import InputMode
unit = UiControlNetUnit()
if image is not None:
unit.image = {'image': np.array(image), 'mask': np.zeros_like(np.array(image))}
unit.input_mode = InputMode(input_mode)
unit.batch_images = batch_images
unit.module = module
unit.model = model
unit.weight = weight
unit.image = image
unit.resize_mode = resize_mode
unit.processor_res = processor_res
unit.threshold_a = threshold_a
unit.threshold_b = threshold_b
unit.guidance_start = guidance_start
unit.guidance_end = guidance_end
unit.pixel_perfect = pixel_perfect
unit.control_mode = control_mode
args.insert(target.args_to-1, unit)
return args
def disable_all(
alwayson_scripts,
args,
):
for script in alwayson_scripts:
if script.name == 'controlnet':
for arg in args[script.args_from:script.args_to]:
arg.enabled = False
if script.name == 'animatediff':
for arg in args[script.args_from:script.args_to]:
arg.enable = False
def txt2img(
prompt: str = '1girl',
negative_prompt: str = '',
steps: int = 30,
sampler_name: str = 'DPM++ 2M SDE Heun Exponential',
n_iter: int = 1,
batch_size: int = 1,
cfg_scale: float = 5,
height: int = 960,
width: int = 576,
enable_hr = False,
denoising_strength: float = 0.6,
hr_scale: float = 0,
hr_upscaler: str = 'Lanczos',
hr_second_pass_steps: int = 10,
hr_resize_x: int = 1600,
hr_resize_y: int = 960,
hr_checkpoint_name: str = '',
hr_sampler_name: str = '',
hr_prompt: str = '',
hr_negative_prompt: str = '',
t2i_script = None,
args: tuple = tuple()
):
from modules import processing, shared, scripts
from modules.shared import opts
p = processing.StableDiffusionProcessingTxt2Img(
sd_model=shared.sd_model,
outpath_samples=opts.outdir_samples or opts.outdir_txt2img_samples,
prompt=prompt,
negative_prompt=negative_prompt,
sampler_name=sampler_name,
batch_size=batch_size,
n_iter=n_iter,
steps=steps,
cfg_scale=cfg_scale,
width=width,
height=height,
firstphase_width=width,
firstphase_height=height,
enable_hr=enable_hr,
denoising_strength=denoising_strength if enable_hr else None,
hr_scale=hr_scale,
hr_upscaler=hr_upscaler,
hr_second_pass_steps=hr_second_pass_steps,
hr_resize_x=hr_resize_x,
hr_resize_y=hr_resize_y,
hr_checkpoint_name=None if hr_checkpoint_name == 'Use same checkpoint' else hr_checkpoint_name,
hr_sampler_name=None if hr_sampler_name == 'Use same sampler' else hr_sampler_name,
hr_prompt=hr_prompt,
hr_negative_prompt=hr_negative_prompt,
do_not_save_grid=True,
do_not_save_samples=True
)
scripts.scripts_txt2img.alwayson_scripts = t2i_script
p.scripts = scripts.scripts_txt2img
p.script_args = args
with closing(p):
processed = scripts.scripts_txt2img.run(p, *args)
if processed is None:
processed = processing.process_images(p)
shared.total_tqdm.clear()
return processed.images
def img2img_batch(
prompt: str = '1girl',
negative_prompt: str = '',
steps: int = 30,
sampler_name: str = 'DPM++ 2M SDE Heun Exponential',
mask_blur: int = 4,
inpainting_fill: int = 1,
n_iter: int = 1,
batch_size: int = 1,
cfg_scale: float = 5,
image_cfg_scale: float = 5,
denoising_strength: float = 0.6,
height: int = 960,
width: int = 576,
scale_by: float = 0,
resize_mode: int = 0,
inpaint_full_res: bool = 1,
inpaint_full_res_padding: int = 32,
inpainting_mask_invert: int = 0,
input_dir: str = '',
inpaint_mask_dir = '',
args: tuple = tuple(),
use_png_info=False,
png_info_props=None,
png_info_dir=None
):
assert 0. <= denoising_strength <= 1., 'can only work with strength in [0.0, 1.0]'
from modules import processing, shared, scripts, img2img
from modules.shared import opts
p = processing.StableDiffusionProcessingImg2Img(
sd_model=shared.sd_model,
outpath_samples=opts.outdir_samples or opts.outdir_img2img_samples,
prompt=prompt,
negative_prompt=negative_prompt,
sampler_name=sampler_name,
batch_size=batch_size,
n_iter=n_iter,
steps=steps,
cfg_scale=cfg_scale,
width=width,
height=height,
init_images=[],
mask=None,
mask_blur=mask_blur,
inpainting_fill=inpainting_fill,
resize_mode=resize_mode,
denoising_strength=denoising_strength,
image_cfg_scale=image_cfg_scale,
inpaint_full_res=inpaint_full_res,
inpaint_full_res_padding=inpaint_full_res_padding,
inpainting_mask_invert=inpainting_mask_invert,
do_not_save_grid=True,
do_not_save_samples=True
)
scripts.scripts_txt2img.alwayson_scripts = t2i_script
p.scripts = scripts.scripts_img2img
p.script_args = args
if inpaint_mask_dir:
p.extra_generation_params["Mask blur"] = mask_blur
with closing(p):
processed = img2img.process_batch(p, input_dir, '', inpaint_mask_dir, args, to_scale=scale_by > 0, scale_by=scale_by, use_png_info=use_png_info, png_info_props=png_info_props, png_info_dir=png_info_dir)
shared.total_tqdm.clear()
return processed.images
def img2img_single(
prompt: str = '1girl',
negative_prompt: str = '',
image = None,
mask = None,
steps: int = 30,
sampler_name: str = 'DPM++ 2M SDE Heun Exponential',
mask_blur: int = 4,
inpainting_fill: int = 1,
n_iter: int = 1,
batch_size: int = 1,
cfg_scale: float = 5,
image_cfg_scale: float = 5,
denoising_strength: float = 0.6,
height: int = 960,
width: int = 576,
scale_by: float = 0,
resize_mode: int = 0,
inpaint_full_res: bool = 1,
inpaint_full_res_padding: int = 32,
inpainting_mask_invert: int = 0,
args: tuple = tuple()
):
from PIL import ImageOps
# Use the EXIF orientation of photos taken by smartphones.
if image is not None:
image = ImageOps.exif_transpose(image)
if scale_by > 0:
assert image, "Can't scale by because no image is selected"
width = int(image.width * scale_by)
height = int(image.height * scale_by)
assert 0. <= denoising_strength <= 1., 'can only work with strength in [0.0, 1.0]'
from modules import processing, shared, scripts
from modules.shared import opts
p = processing.StableDiffusionProcessingImg2Img(
sd_model=shared.sd_model,
outpath_samples=opts.outdir_samples or opts.outdir_img2img_samples,
prompt=prompt,
negative_prompt=negative_prompt,
sampler_name=sampler_name,
batch_size=batch_size,
n_iter=n_iter,
steps=steps,
cfg_scale=cfg_scale,
width=width,
height=height,
init_images=[image],
mask=mask,
mask_blur=mask_blur,
inpainting_fill=inpainting_fill,
resize_mode=resize_mode,
denoising_strength=denoising_strength,
image_cfg_scale=image_cfg_scale,
inpaint_full_res=inpaint_full_res,
inpaint_full_res_padding=inpaint_full_res_padding,
inpainting_mask_invert=inpainting_mask_invert,
do_not_save_grid=True,
do_not_save_samples=True
)
scripts.scripts_txt2img.alwayson_scripts = t2i_script
p.scripts = scripts.scripts_img2img
p.script_args = args
if mask:
p.extra_generation_params["Mask blur"] = mask_blur
with closing(p):
processed = scripts.scripts_img2img.run(p, *args)
if processed is None:
processed = processing.process_images(p)
shared.total_tqdm.clear()
return processed.images
mode_to_func = {
'img2img_batch': img2img_batch,
'img2img_single': img2img_single,
'txt2img': txt2img,
}
@torch.no_grad()
def process(
config = {
'mode': 'txt2img',
"model": "",
"webui": {
'prompt': '1girl',
'negative_prompt': 'easynegative',
'steps': 8,
'width': 512,
'height': 512,
},
'cn': [
{
'model': 'control_v11p_sd15_canny',
'module': 'canny',
'weight': 0.8,
'extra_kwargs': {
'threshold_a': 50,
'threshold_b': 150,
}
},
{
'model': 'control_v11f1p_sd15_depth',
'module': 'depth_midas',
'weight': 0.7
}
],
"ad": {
"video_length": 24,
}
}
):
load_model(config['model'])
global t2i_script, args
args = enable_animatediff(
alwayson_scripts = t2i_script,
args = args,
**config['ad'])
for cnet in config['cn']:
args = enable_controlnet(
alwayson_scripts = t2i_script,
args = args,
**cnet
)
from modules import devices
with devices.autocast():
test_imgs = mode_to_func[config["mode"]](
**config['webui'],
t2i_script = t2i_script,
args = args,
)
devices.torch_gc()
disable_all(t2i_script, args)
if __name__ == '__main__':
t2i_script, arg = init()
import time
t0 = time.time()
process(config={
"mode": "txt2img",
"model": "/home/conrevo/SD/stable-diffusion-webui/models/Stable-diffusion/AnythingV5Ink_ink.safetensors",
"webui": {
# "prompt": "1girl, yoimiya (genshin impact), origen, line, comet, wink, Masterpiece, BestQuality. UltraDetailed, <lora:LineLine2D:0.7>, <lora:yoimiya:0.8>\n0: closed mouth\n10: open mouth",
# "negative_prompt": "sketch, duplicate, ugly, huge eyes, text, logo, monochrome, worst face, (bad and mutated hands:1.3), (worst quality:2.0), (low quality:2.0), (blurry:2.0), horror, geometry, bad_prompt_v2, (bad hands), (missing fingers), multiple limbs, bad anatomy, (interlocked fingers:1.2), Ugly Fingers, (extra digit and hands and fingers and legs and arms:1.4), crown braid, ((2girl)), (deformed fingers:1.2), (long fingers:1.2),succubus wings,horn,succubus horn,succubus hairstyle, (bad-artist-anime), bad-artist, bad hand, grayscale, skin spots, acnes, skin blemishes",
# "sampler_name": "LCM",
"prompt": "masterpiece, best quality, 1girl, hatsune miku, <lora:LCM_LoRA_Weights_SD15:1>",
"negative_prompt": "easynegative",
"sampler_name": "Euler a",
"cfg_scale": 2,
"steps": 8,
"width": 512,
"height": 512
},
"cn": [{
"input_mode": "batch",
"module": "none",
"model": "control_v11p_sd15_lineart",
"batch_images": "/home/conrevo/SD/dataset/upperbodydataset/lineart/",
}],
"ad": {
"enable": True,
"video_length": 24,
}
})
t1 = time.time()
print(t1-t0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment