shared_options.py 54.5 KB
Newer Older
D
d8ahazard 已提交
1
import datetime
2 3
import json
import os
4
import re
D
d8ahazard 已提交
5
import sys
6
import threading
7
import time
8
import logging
D
d8ahazard 已提交
9

10
import gradio as gr
11
import torch
12
import tqdm
13

14
import launch
A
AUTOMATIC 已提交
15
import modules.interrogate
E
EyeDeck 已提交
16
import modules.memmon
D
d8ahazard 已提交
17
import modules.styles
B
brkirch 已提交
18
import modules.devices as devices
19
from modules import localization, script_loading, errors, ui_components, shared_items, cmd_args, rng  # noqa: F401
A
AUTOMATIC 已提交
20
from modules.paths_internal import models_path, script_path, data_path, sd_configs_path, sd_default_config, sd_model_file, default_sd_model_file, extensions_dir, extensions_builtin_dir  # noqa: F401
21
from ldm.models.diffusion.ddpm import LatentDiffusion
22
from typing import Optional
23

24 25
log = logging.getLogger(__name__)

26 27
demo = None

28
parser = cmd_args.parser
29

30
script_loading.preload_extensions(extensions_dir, parser, extension_list=launch.list_extensions(launch.args.ui_settings_file))
31
script_loading.preload_extensions(extensions_builtin_dir, parser)
32

A
AUTOMATIC 已提交
33 34 35 36
if os.environ.get('IGNORE_CMD_ARGS_ERRORS', None) is None:
    cmd_opts = parser.parse_args()
else:
    cmd_opts, _ = parser.parse_known_args()
D
d8ahazard 已提交
37

38

39
restricted_opts = {
40
    "samples_filename_pattern",
41
    "directories_filename_pattern",
42 43 44 45 46 47 48
    "outdir_samples",
    "outdir_txt2img_samples",
    "outdir_img2img_samples",
    "outdir_extras_samples",
    "outdir_grids",
    "outdir_txt2img_grids",
    "outdir_save",
49
    "outdir_init_images"
50
}
B
brkirch 已提交
51

S
space-nuko 已提交
52 53
# https://huggingface.co/datasets/freddyaboulton/gradio-theme-subdomains/resolve/main/subdomains.json
gradio_hf_hub_themes = [
D
dhwz 已提交
54
    "gradio/base",
S
space-nuko 已提交
55 56 57 58 59 60
    "gradio/glass",
    "gradio/monochrome",
    "gradio/seafoam",
    "gradio/soft",
    "gradio/dracula_test",
    "abidlabs/dracula_test",
D
dhwz 已提交
61
    "abidlabs/Lime",
S
space-nuko 已提交
62
    "abidlabs/pakistan",
D
dhwz 已提交
63
    "Ama434/neutral-barlow",
S
space-nuko 已提交
64
    "dawood/microsoft_windows",
D
dhwz 已提交
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
    "finlaymacklon/smooth_slate",
    "Franklisi/darkmode",
    "freddyaboulton/dracula_revamped",
    "freddyaboulton/test-blue",
    "gstaff/xkcd",
    "Insuz/Mocha",
    "Insuz/SimpleIndigo",
    "JohnSmith9982/small_and_pretty",
    "nota-ai/theme",
    "nuttea/Softblue",
    "ParityError/Anime",
    "reilnuud/polite",
    "remilia/Ghostly",
    "rottenlittlecreature/Moon_Goblin",
    "step-3-profit/Midnight-Deep",
    "Taithrah/Minimal",
    "ysharma/huggingface",
S
space-nuko 已提交
82 83 84 85
    "ysharma/steampunk"
]


86
cmd_opts.disable_extension_access = (cmd_opts.share or cmd_opts.listen or cmd_opts.server_name) and not cmd_opts.enable_insecure_extension_access
87

A
AUTOMATIC 已提交
88 89
devices.device, devices.device_interrogate, devices.device_gfpgan, devices.device_esrgan, devices.device_codeformer = \
    (devices.cpu if any(y in cmd_opts.use_cpu for y in [x, 'all']) else devices.get_optimal_device() for x in ['sd', 'interrogate', 'gfpgan', 'esrgan', 'codeformer'])
90 91 92

devices.dtype = torch.float32 if cmd_opts.no_half else torch.float16
devices.dtype_vae = torch.float32 if cmd_opts.no_half or cmd_opts.no_half_vae else torch.float16
B
brkirch 已提交
93 94

device = devices.device
A
AUTOMATIC 已提交
95
weight_load_location = None if cmd_opts.lowram else "cpu"
A
Abdullah Barhoum 已提交
96

97
batch_cond_uncond = cmd_opts.always_batch_cond_uncond or not (cmd_opts.lowvram or cmd_opts.medvram)
98
parallel_processing_allowed = not cmd_opts.lowvram and not cmd_opts.medvram
99
xformers_available = False
100
config_filename = cmd_opts.ui_settings_file
A
AUTOMATIC 已提交
101

A
AUTOMATIC 已提交
102
os.makedirs(cmd_opts.hypernetwork_dir, exist_ok=True)
A
AUTOMATIC 已提交
103
hypernetworks = {}
A
AUTOMATIC 已提交
104
loaded_hypernetworks = []
A
AUTOMATIC 已提交
105

A
AUTOMATIC 已提交
106

A
AUTOMATIC 已提交
107
def reload_hypernetworks():
A
AUTOMATIC 已提交
108
    from modules.hypernetworks import hypernetwork
A
AUTOMATIC 已提交
109 110 111 112
    global hypernetworks

    hypernetworks = hypernetwork.list_hypernetworks(cmd_opts.hypernetwork_dir)

113

114
class State:
115
    skipped = False
116 117
    interrupted = False
    job = ""
A
AUTOMATIC 已提交
118 119
    job_no = 0
    job_count = 0
120
    processing_has_refined_job_count = False
D
DepFA 已提交
121
    job_timestamp = '0'
A
AUTOMATIC 已提交
122 123
    sampling_step = 0
    sampling_steps = 0
A
AUTOMATIC 已提交
124 125
    current_latent = None
    current_image = None
126
    current_image_sampling_step = 0
127
    id_live_preview = 0
128
    textinfo = None
129
    time_start = None
130
    server_start = None
131
    _server_command_signal = threading.Event()
A
anonCantCode 已提交
132
    _server_command: Optional[str] = None
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149

    @property
    def need_restart(self) -> bool:
        # Compatibility getter for need_restart.
        return self.server_command == "restart"

    @need_restart.setter
    def need_restart(self, value: bool) -> None:
        # Compatibility setter for need_restart.
        if value:
            self.server_command = "restart"

    @property
    def server_command(self):
        return self._server_command

    @server_command.setter
150
    def server_command(self, value: Optional[str]) -> None:
151 152 153 154 155 156
        """
        Set the server command to `value` and signal that it's been set.
        """
        self._server_command = value
        self._server_command_signal.set()

A
anonCantCode 已提交
157
    def wait_for_server_command(self, timeout: Optional[float] = None) -> Optional[str]:
158 159 160 161 162 163 164 165 166 167 168 169
        """
        Wait for server command to get set; return and clear the value and signal.
        """
        if self._server_command_signal.wait(timeout):
            self._server_command_signal.clear()
            req = self._server_command
            self._server_command = None
            return req
        return None

    def request_restart(self) -> None:
        self.interrupt()
A
AUTOMATIC 已提交
170
        self.server_command = "restart"
171
        log.info("Received restart request")
172

173 174
    def skip(self):
        self.skipped = True
175
        log.info("Received skip request")
176

177 178
    def interrupt(self):
        self.interrupted = True
179
        log.info("Received interrupt request")
180

A
AUTOMATIC 已提交
181
    def nextjob(self):
182
        if opts.live_previews_enable and opts.show_progress_every_n_steps == -1:
183
            self.do_set_current_image()
E
evshiron 已提交
184

A
AUTOMATIC 已提交
185 186
        self.job_no += 1
        self.sampling_step = 0
187
        self.current_image_sampling_step = 0
D
update  
discus0434 已提交
188

E
evshiron 已提交
189
    def dict(self):
E
evshiron 已提交
190 191
        obj = {
            "skipped": self.skipped,
V
Vladimir Mandic 已提交
192
            "interrupted": self.interrupted,
E
evshiron 已提交
193 194
            "job": self.job,
            "job_count": self.job_count,
195
            "job_timestamp": self.job_timestamp,
E
evshiron 已提交
196 197 198 199 200
            "job_no": self.job_no,
            "sampling_step": self.sampling_step,
            "sampling_steps": self.sampling_steps,
        }

E
evshiron 已提交
201
        return obj
E
evshiron 已提交
202

203
    def begin(self, job: str = "(unknown)"):
204 205
        self.sampling_step = 0
        self.job_count = -1
206
        self.processing_has_refined_job_count = False
207 208 209 210 211
        self.job_no = 0
        self.job_timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
        self.current_latent = None
        self.current_image = None
        self.current_image_sampling_step = 0
212
        self.id_live_preview = 0
213 214 215
        self.skipped = False
        self.interrupted = False
        self.textinfo = None
216
        self.time_start = time.time()
217
        self.job = job
218
        devices.torch_gc()
219
        log.info("Starting job %s", job)
220 221

    def end(self):
222 223
        duration = time.time() - self.time_start
        log.info("Ending job %s (%.2f seconds)", self.job, duration)
224 225 226 227
        self.job = ""
        self.job_count = 0

        devices.torch_gc()
A
AUTOMATIC 已提交
228

A
AUTOMATIC 已提交
229
    def set_current_image(self):
230
        """sets self.current_image from self.current_latent if enough sampling steps have been made after the last call to this"""
231 232 233
        if not parallel_processing_allowed:
            return

234
        if self.sampling_step - self.current_image_sampling_step >= opts.show_progress_every_n_steps and opts.live_previews_enable and opts.show_progress_every_n_steps != -1:
235 236 237 238 239
            self.do_set_current_image()

    def do_set_current_image(self):
        if self.current_latent is None:
            return
E
evshiron 已提交
240

A
AUTOMATIC 已提交
241
        import modules.sd_samplers
A
AUTOMATIC 已提交
242

A
AUTOMATIC1111 已提交
243 244 245 246 247 248 249 250 251 252 253 254
        try:
            if opts.show_progress_grid:
                self.assign_current_image(modules.sd_samplers.samples_to_image_grid(self.current_latent))
            else:
                self.assign_current_image(modules.sd_samplers.sample_to_image(self.current_latent))

            self.current_image_sampling_step = self.sampling_step

        except Exception:
            # when switching models during genration, VAE would be on CPU, so creating an image will fail.
            # we silently ignore this error
            errors.record_exception()
A
AUTOMATIC 已提交
255

256 257 258 259
    def assign_current_image(self, image):
        self.current_image = image
        self.id_live_preview += 1

A
AUTOMATIC 已提交
260

261
state = State()
262
state.server_start = time.time()
263

264
styles_filename = cmd_opts.styles_file
A
AUTOMATIC 已提交
265
prompt_styles = modules.styles.StyleDatabase(styles_filename)
266

A
AUTOMATIC 已提交
267 268
interrogator = modules.interrogate.InterrogateModels("interrogate")

A
AUTOMATIC 已提交
269
face_restorers = []
270

271

272
class OptionInfo:
273
    def __init__(self, default=None, label="", component=None, component_args=None, onchange=None, section=None, refresh=None, comment_before='', comment_after=''):
274 275 276 277 278
        self.default = default
        self.label = label
        self.component = component
        self.component_args = component_args
        self.onchange = onchange
279
        self.section = section
280
        self.refresh = refresh
281
        self.do_not_save = False
282

283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
        self.comment_before = comment_before
        """HTML text that will be added after label in UI"""

        self.comment_after = comment_after
        """HTML text that will be added before label in UI"""

    def link(self, label, url):
        self.comment_before += f"[<a href='{url}' target='_blank'>{label}</a>]"
        return self

    def js(self, label, js_func):
        self.comment_before += f"[<a onclick='{js_func}(); return false'>{label}</a>]"
        return self

    def info(self, info):
        self.comment_after += f"<span class='info'>({info})</span>"
        return self

301 302 303 304
    def html(self, html):
        self.comment_after += html
        return self

305 306 307 308
    def needs_restart(self):
        self.comment_after += " <span class='info'>(requires restart)</span>"
        return self

W
w-e-w 已提交
309 310 311
    def needs_reload_ui(self):
        self.comment_after += " <span class='info'>(requires Reload UI)</span>"
        return self
312

313

314 315 316
class OptionHTML(OptionInfo):
    def __init__(self, text):
        super().__init__(str(text).strip(), label='', component=lambda **kwargs: gr.HTML(elem_classes="settings-info", **kwargs))
317

318
        self.do_not_save = True
319

320

A
Aidan Holland 已提交
321
def options_section(section_identifier, options_dict):
A
AUTOMATIC 已提交
322
    for v in options_dict.values():
A
Aidan Holland 已提交
323
        v.section = section_identifier
324 325 326

    return options_dict

E
Eyrie 已提交
327

A
AUTOMATIC 已提交
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
def list_checkpoint_tiles():
    import modules.sd_models
    return modules.sd_models.checkpoint_tiles()


def refresh_checkpoints():
    import modules.sd_models
    return modules.sd_models.list_models()


def list_samplers():
    import modules.sd_samplers
    return modules.sd_samplers.all_samplers


343
hide_dirs = {"visible": not cmd_opts.hide_ui_dir_config}
344
tab_names = []
345 346 347

options_templates = {}

348 349 350
options_templates.update(options_section(('saving-images', "Saving images/grids"), {
    "samples_save": OptionInfo(True, "Always save all generated images"),
    "samples_format": OptionInfo('png', 'File format for images'),
351
    "samples_filename_pattern": OptionInfo("", "Images filename pattern", component_args=hide_dirs).link("wiki", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Custom-Images-Filename-Name-and-Subdirectory"),
352
    "save_images_add_number": OptionInfo(True, "Add number to filename when saving", component_args=hide_dirs),
353 354 355 356 357

    "grid_save": OptionInfo(True, "Always save all generated image grids"),
    "grid_format": OptionInfo('png', 'File format for grids'),
    "grid_extended_filename": OptionInfo(False, "Add extended info (seed, prompt) to filename when saving grid"),
    "grid_only_if_multiple": OptionInfo(True, "Do not save grids consisting of one picture"),
358
    "grid_prevent_empty_spots": OptionInfo(False, "Prevent empty spots in grid (when set to autodetect)"),
359
    "grid_zip_filename_pattern": OptionInfo("", "Archive filename pattern", component_args=hide_dirs).link("wiki", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Custom-Images-Filename-Name-and-Subdirectory"),
360
    "n_rows": OptionInfo(-1, "Grid row count; use -1 for autodetect and 0 for it to be same as batch size", gr.Slider, {"minimum": -1, "maximum": 16, "step": 1}),
361 362 363 364
    "font": OptionInfo("", "Font for image grids that have text"),
    "grid_text_active_color": OptionInfo("#000000", "Text color for image grids", ui_components.FormColorPicker, {}),
    "grid_text_inactive_color": OptionInfo("#999999", "Inactive text color for image grids", ui_components.FormColorPicker, {}),
    "grid_background_color": OptionInfo("#ffffff", "Background color for image grids", ui_components.FormColorPicker, {}),
365

366 367 368
    "enable_pnginfo": OptionInfo(True, "Save text information about generation parameters as chunks to png files"),
    "save_txt": OptionInfo(False, "Create a text file next to every image with generation parameters."),
    "save_images_before_face_restoration": OptionInfo(False, "Save a copy of image before doing face restoration."),
369
    "save_images_before_highres_fix": OptionInfo(False, "Save a copy of image before applying highres fix."),
370
    "save_images_before_color_correction": OptionInfo(False, "Save a copy of image before applying color correction to img2img results"),
371 372
    "save_mask": OptionInfo(False, "For inpainting, save a copy of the greyscale mask"),
    "save_mask_composite": OptionInfo(False, "For inpainting, save a masked composite"),
373
    "jpeg_quality": OptionInfo(80, "Quality for saved jpeg images", gr.Slider, {"minimum": 1, "maximum": 100, "step": 1}),
M
missionfloyd 已提交
374
    "webp_lossless": OptionInfo(False, "Use lossless compression for webp images"),
375
    "export_for_4chan": OptionInfo(True, "Save copy of large images as JPG").info("if the file size is above the limit, or either width or height are above the limit"),
376 377
    "img_downscale_threshold": OptionInfo(4.0, "File size limit for the above option, MB", gr.Number),
    "target_side_length": OptionInfo(4000, "Width/height limit for the above option, in pixels", gr.Number),
378
    "img_max_size_mp": OptionInfo(200, "Maximum image size", gr.Number).info("in megapixels"),
379

380
    "use_original_name_batch": OptionInfo(True, "Use original name for output filename during batch process in extras tab"),
381
    "use_upscaler_name_as_suffix": OptionInfo(False, "Use upscaler name as filename suffix in the extras tab"),
382
    "save_selected_only": OptionInfo(True, "When using 'Save' button, only save a single selected image"),
F
forsurefr 已提交
383
    "save_init_img": OptionInfo(False, "Save init images when using img2img"),
384 385 386 387

    "temp_dir":  OptionInfo("", "Directory for temporary images; leave empty for default"),
    "clean_temp_dir_at_start": OptionInfo(False, "Cleanup non-default temporary directory when starting webui"),

A
AUTOMATIC1111 已提交
388
    "save_incomplete_images": OptionInfo(False, "Save incomplete images").info("save images that has been interrupted in mid-generation; even if not saved, they will still show up in webui output."),
389 390
}))

391 392
options_templates.update(options_section(('saving-paths', "Paths for saving"), {
    "outdir_samples": OptionInfo("", "Output directory for images; if empty, defaults to three directories below", component_args=hide_dirs),
393 394 395 396 397 398 399
    "outdir_txt2img_samples": OptionInfo("outputs/txt2img-images", 'Output directory for txt2img images', component_args=hide_dirs),
    "outdir_img2img_samples": OptionInfo("outputs/img2img-images", 'Output directory for img2img images', component_args=hide_dirs),
    "outdir_extras_samples": OptionInfo("outputs/extras-images", 'Output directory for images from extras tab', component_args=hide_dirs),
    "outdir_grids": OptionInfo("", "Output directory for grids; if empty, defaults to two directories below", component_args=hide_dirs),
    "outdir_txt2img_grids": OptionInfo("outputs/txt2img-grids", 'Output directory for txt2img grids', component_args=hide_dirs),
    "outdir_img2img_grids": OptionInfo("outputs/img2img-grids", 'Output directory for img2img grids', component_args=hide_dirs),
    "outdir_save": OptionInfo("log/images", "Directory for saving images using the Save button", component_args=hide_dirs),
400
    "outdir_init_images": OptionInfo("outputs/init-images", "Directory for saving init images when using img2img", component_args=hide_dirs),
401 402
}))

403
options_templates.update(options_section(('saving-to-dirs', "Saving to a directory"), {
404 405
    "save_to_dirs": OptionInfo(True, "Save images to a subdirectory"),
    "grid_save_to_dirs": OptionInfo(True, "Save grids to a subdirectory"),
A
AUTOMATIC 已提交
406
    "use_save_to_dirs_for_ui": OptionInfo(False, "When using \"Save\" button, save images to a subdirectory"),
407
    "directories_filename_pattern": OptionInfo("[date]", "Directory name pattern", component_args=hide_dirs).link("wiki", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Custom-Images-Filename-Name-and-Subdirectory"),
408
    "directories_max_prompt_words": OptionInfo(8, "Max prompt words for [prompt_words] pattern", gr.Slider, {"minimum": 1, "maximum": 20, "step": 1, **hide_dirs}),
409 410 411
}))

options_templates.update(options_section(('upscaling', "Upscaling"), {
412 413 414
    "ESRGAN_tile": OptionInfo(192, "Tile size for ESRGAN upscalers.", gr.Slider, {"minimum": 0, "maximum": 512, "step": 16}).info("0 = no tiling"),
    "ESRGAN_tile_overlap": OptionInfo(8, "Tile overlap for ESRGAN upscalers.", gr.Slider, {"minimum": 0, "maximum": 48, "step": 1}).info("Low values = visible seam"),
    "realesrgan_enabled_models": OptionInfo(["R-ESRGAN 4x+", "R-ESRGAN 4x+ Anime6B"], "Select which Real-ESRGAN models to show in the web UI.", gr.CheckboxGroup, lambda: {"choices": shared_items.realesrgan_models_names()}),
415
    "upscaler_for_img2img": OptionInfo(None, "Upscaler for img2img", gr.Dropdown, lambda: {"choices": [x.name for x in sd_upscalers]}),
416 417 418
}))

options_templates.update(options_section(('face-restoration', "Face restoration"), {
M
missionfloyd 已提交
419
    "face_restoration_model": OptionInfo("CodeFormer", "Face restoration model", gr.Radio, lambda: {"choices": [x.name() for x in face_restorers]}),
420
    "code_former_weight": OptionInfo(0.5, "CodeFormer weight", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01}).info("0 = maximum effect; 1 = minimum effect"),
421
    "face_restoration_unload": OptionInfo(False, "Move face restoration model from VRAM into RAM after processing"),
422 423
}))

424
options_templates.update(options_section(('system', "System"), {
425
    "auto_launch_browser": OptionInfo("Local", "Automatically open webui in browser on startup", gr.Radio, lambda: {"choices": ["Disable", "Local", "Remote"]}),
W
w-e-w 已提交
426 427
    "show_warnings": OptionInfo(False, "Show warnings in console.").needs_reload_ui(),
    "show_gradio_deprecation_warnings": OptionInfo(True, "Show gradio deprecation warnings in console.").needs_reload_ui(),
428
    "memmon_poll_rate": OptionInfo(8, "VRAM usage polls per second during generation.", gr.Slider, {"minimum": 0, "maximum": 40, "step": 1}).info("0 = disable"),
429
    "samples_log_stdout": OptionInfo(False, "Always print all generation info to standard output"),
J
Justin Riddiough 已提交
430
    "multiple_tqdm": OptionInfo(True, "Add a second progress bar to the console that shows progress for an entire job."),
431
    "print_hypernet_extra": OptionInfo(False, "Print extra hypernetwork information to console."),
432
    "list_hidden_files": OptionInfo(True, "Load models/files in hidden directories").info("directory is hidden if its name starts with \".\""),
433
    "disable_mmap_load_safetensors": OptionInfo(False, "Disable memmapping for loading .safetensors files.").info("fixes very slow loading speed in some cases"),
434
    "hide_ldm_prints": OptionInfo(True, "Prevent Stability-AI's ldm/sgm modules from printing noise to console."),
435 436
}))

437
options_templates.update(options_section(('training', "Training"), {
F
Fampai 已提交
438
    "unload_models_when_training": OptionInfo(False, "Move VAE and CLIP to RAM when training if possible. Saves VRAM."),
439
    "pin_memory": OptionInfo(False, "Turn on pin_memory for DataLoader. Makes training slightly faster but can increase memory usage."),
S
Shondoit 已提交
440
    "save_optimizer_state": OptionInfo(False, "Saves Optimizer state as separate *.optim file. Training of embedding or HN can be resumed with the matching optim file."),
441
    "save_training_settings_to_txt": OptionInfo(True, "Save textual inversion and hypernet settings to a text file whenever training starts."),
442 443
    "dataset_filename_word_regex": OptionInfo("", "Filename word regex"),
    "dataset_filename_join_string": OptionInfo(" ", "Filename join string"),
444 445
    "training_image_repeats_per_epoch": OptionInfo(1, "Number of repeats for a single input image per epoch; used only for displaying epoch number", gr.Number, {"precision": 0}),
    "training_write_csv_every": OptionInfo(500, "Save an csv containing the loss to log directory every N steps, 0 to disable"),
F
Fampai 已提交
446
    "training_xattention_optimizations": OptionInfo(False, "Use cross attention optimizations while training"),
447 448 449
    "training_enable_tensorboard": OptionInfo(False, "Enable tensorboard logging."),
    "training_tensorboard_save_images": OptionInfo(False, "Save generated images within tensorboard."),
    "training_tensorboard_flush_every": OptionInfo(120, "How often, in seconds, to flush the pending tensorboard events and summaries to disk."),
450 451
}))

452
options_templates.update(options_section(('sd', "Stable Diffusion"), {
453
    "sd_model_checkpoint": OptionInfo(None, "Stable Diffusion checkpoint", gr.Dropdown, lambda: {"choices": list_checkpoint_tiles()}, refresh=refresh_checkpoints),
454 455 456
    "sd_checkpoints_limit": OptionInfo(1, "Maximum number of checkpoints loaded at the same time", gr.Slider, {"minimum": 1, "maximum": 10, "step": 1}),
    "sd_checkpoints_keep_in_cpu": OptionInfo(True, "Only keep one model on device").info("will keep models other than the currently used one in RAM rather than VRAM"),
    "sd_checkpoint_cache": OptionInfo(0, "Checkpoints to cache in RAM", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}).info("obsolete; set to 0 and use the two settings above instead"),
A
AUTOMATIC 已提交
457
    "sd_unet": OptionInfo("Automatic", "SD Unet", gr.Dropdown, lambda: {"choices": shared_items.sd_unet_items()}, refresh=shared_items.refresh_unet_list).info("choose Unet model: Automatic = use one with same filename as checkpoint; None = use Unet from checkpoint"),
W
w-e-w 已提交
458
    "enable_quantization": OptionInfo(False, "Enable quantization in K samplers for sharper and cleaner results. This may change existing seeds").needs_reload_ui(),
459
    "enable_emphasis": OptionInfo(True, "Enable emphasis").info("use (text) to make model pay more attention to text and [text] to make it pay less attention"),
460
    "enable_batch_seeds": OptionInfo(True, "Make K-diffusion samplers produce same images in a batch as when making a single image"),
461
    "comma_padding_backtrack": OptionInfo(20, "Prompt word wrap length limit", gr.Slider, {"minimum": 0, "maximum": 74, "step": 1}).info("in tokens - for texts shorter than specified, if they don't fit into 75 token limit, move them to the next 75 token chunk"),
A
arch-fan 已提交
462
    "CLIP_stop_at_last_layers": OptionInfo(1, "Clip skip", gr.Slider, {"minimum": 1, "maximum": 12, "step": 1}).link("wiki", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#clip-skip").info("ignore last layers of CLIP network; 1 ignores none, 2 ignores one layer"),
463
    "upcast_attn": OptionInfo(False, "Upcast cross attention layer to float32"),
464
    "randn_source": OptionInfo("GPU", "Random number generator source.", gr.Radio, {"choices": ["GPU", "CPU", "NV"]}).info("changes seeds drastically; use CPU to produce the same picture across different videocard vendors; use NV to produce same picture as on NVidia videocards"),
465 466
}))

A
AUTOMATIC1111 已提交
467 468 469 470 471
options_templates.update(options_section(('sdxl', "Stable Diffusion XL"), {
    "sdxl_crop_top": OptionInfo(0, "crop top coordinate"),
    "sdxl_crop_left": OptionInfo(0, "crop left coordinate"),
    "sdxl_refiner_low_aesthetic_score": OptionInfo(2.5, "SDXL low aesthetic score", gr.Number).info("used for refiner model negative prompt"),
    "sdxl_refiner_high_aesthetic_score": OptionInfo(6.0, "SDXL high aesthetic score", gr.Number).info("used for refiner model prompt"),
472 473
}))

474
options_templates.update(options_section(('vae', "VAE"), {
475 476 477 478 479 480
    "sd_vae_explanation": OptionHTML("""
<abbr title='Variational autoencoder'>VAE</abbr> is a neural network that transforms a standard <abbr title='red/green/blue'>RGB</abbr>
image into latent space representation and back. Latent space representation is what stable diffusion is working on during sampling
(i.e. when the progress bar is between empty and full). For txt2img, VAE is used to create a resulting image after the sampling is finished.
For img2img, VAE is used to process user's input image before the sampling, and to create an image after sampling.
"""),
481 482
    "sd_vae_checkpoint_cache": OptionInfo(0, "VAE Checkpoints to cache in RAM", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
    "sd_vae": OptionInfo("Automatic", "SD VAE", gr.Dropdown, lambda: {"choices": shared_items.sd_vae_items()}, refresh=shared_items.refresh_vae_list).info("choose VAE model: Automatic = use one with same filename as checkpoint; None = use VAE from checkpoint"),
A
AUTOMATIC1111 已提交
483
    "sd_vae_overrides_per_model_preferences": OptionInfo(True, "Selected VAE overrides per-model preferences").info("you can set per-model VAE either by editing user metadata for checkpoints, or by making the VAE have same name as checkpoint"),
D
dhwz 已提交
484
    "auto_vae_precision": OptionInfo(True, "Automatically revert VAE to 32-bit floats").info("triggers when a tensor with NaNs is produced in VAE; disabling the option in this case will result in a black square image"),
485 486 487
    "sd_vae_encode_method": OptionInfo("Full", "VAE type for encode", gr.Radio, {"choices": ["Full", "TAESD"]}).info("method to encode image to latent (use in img2img, hires-fix or inpaint mask)"),
    "sd_vae_decode_method": OptionInfo("Full", "VAE type for decode", gr.Radio, {"choices": ["Full", "TAESD"]}).info("method to decode latent to image"),
}))
488 489 490 491 492 493 494

options_templates.update(options_section(('img2img', "img2img"), {
    "inpainting_mask_weight": OptionInfo(1.0, "Inpainting conditioning mask strength", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}),
    "initial_noise_multiplier": OptionInfo(1.0, "Noise multiplier for img2img", gr.Slider, {"minimum": 0.5, "maximum": 1.5, "step": 0.01}),
    "img2img_color_correction": OptionInfo(False, "Apply color correction to img2img results to match original colors."),
    "img2img_fix_steps": OptionInfo(False, "With img2img, do exactly the amount of steps the slider specifies.").info("normally you'd do less with less denoising"),
    "img2img_background_color": OptionInfo("#ffffff", "With img2img, fill transparent parts of the input image with this color.", ui_components.FormColorPicker, {}),
W
w-e-w 已提交
495 496 497 498
    "img2img_editor_height": OptionInfo(720, "Height of the image editor", gr.Slider, {"minimum": 80, "maximum": 1600, "step": 1}).info("in pixels").needs_reload_ui(),
    "img2img_sketch_default_brush_color": OptionInfo("#ffffff", "Sketch initial brush color", ui_components.FormColorPicker, {}).info("default brush color of img2img sketch").needs_reload_ui(),
    "img2img_inpaint_mask_brush_color": OptionInfo("#ffffff", "Inpaint mask brush color", ui_components.FormColorPicker,  {}).info("brush color of inpaint mask").needs_reload_ui(),
    "img2img_inpaint_sketch_default_brush_color": OptionInfo("#ffffff", "Inpaint sketch initial brush color", ui_components.FormColorPicker, {}).info("default brush color of img2img inpaint sketch").needs_reload_ui(),
499 500 501 502
    "return_mask": OptionInfo(False, "For inpainting, include the greyscale mask in results for web"),
    "return_mask_composite": OptionInfo(False, "For inpainting, include masked composite in results for web"),
}))

503
options_templates.update(options_section(('optimizations', "Optimizations"), {
504
    "cross_attention_optimization": OptionInfo("Automatic", "Cross attention optimization", gr.Dropdown, lambda: {"choices": shared_items.cross_attention_optimizations()}),
505
    "s_min_uncond": OptionInfo(0.0, "Negative Guidance minimum sigma", gr.Slider, {"minimum": 0.0, "maximum": 15.0, "step": 0.01}).link("PR", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/9177").info("skip negative prompt for some steps when the image is almost ready; 0=disable, higher=faster"),
506
    "token_merging_ratio": OptionInfo(0.0, "Token merging ratio", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}).link("PR", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/9256").info("0=disable, higher=faster"),
507 508
    "token_merging_ratio_img2img": OptionInfo(0.0, "Token merging ratio for img2img", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}).info("only applies if non-zero and overrides above"),
    "token_merging_ratio_hr": OptionInfo(0.0, "Token merging ratio for high-res pass", gr.Slider, {"minimum": 0.0, "maximum": 0.9, "step": 0.1}).info("only applies if non-zero and overrides above"),
509
    "pad_cond_uncond": OptionInfo(False, "Pad prompt/negative prompt to be same length").info("improves performance when prompt and negative prompt have different lengths; changes seeds"),
A
AUTOMATIC1111 已提交
510
    "persistent_cond_cache": OptionInfo(True, "Persistent cond cache").info("Do not recalculate conds from prompts if prompts have not changed since previous calculation"),
511 512
}))

513 514 515
options_templates.update(options_section(('compatibility', "Compatibility"), {
    "use_old_emphasis_implementation": OptionInfo(False, "Use old emphasis implementation. Can be useful to reproduce old seeds."),
    "use_old_karras_scheduler_sigmas": OptionInfo(False, "Use old karras scheduler sigmas (0.1 to 10)."),
516
    "no_dpmpp_sde_batch_determinism": OptionInfo(False, "Do not make DPM++ SDE deterministic across different batch sizes."),
517
    "use_old_hires_fix_width_height": OptionInfo(False, "For hires fix, use width/height sliders to set final resolution rather than first pass (disables Upscale by, Resize width/height to)."),
C
catboxanon 已提交
518
    "dont_fix_second_order_samplers_schedule": OptionInfo(False, "Do not fix prompt schedule for second order samplers."),
519
    "hires_fix_use_firstpass_conds": OptionInfo(False, "For hires fix, calculate conds of second pass using extra networks of first pass."),
520 521
}))

522
options_templates.update(options_section(('interrogate', "Interrogate"), {
523 524 525 526 527 528
    "interrogate_keep_models_in_memory": OptionInfo(False, "Keep models in VRAM"),
    "interrogate_return_ranks": OptionInfo(False, "Include ranks of model tags matches in results.").info("booru only"),
    "interrogate_clip_num_beams": OptionInfo(1, "BLIP: num_beams", gr.Slider, {"minimum": 1, "maximum": 16, "step": 1}),
    "interrogate_clip_min_length": OptionInfo(24, "BLIP: minimum description length", gr.Slider, {"minimum": 1, "maximum": 128, "step": 1}),
    "interrogate_clip_max_length": OptionInfo(48, "BLIP: maximum description length", gr.Slider, {"minimum": 1, "maximum": 256, "step": 1}),
    "interrogate_clip_dict_limit": OptionInfo(1500, "CLIP: maximum number of lines in text file").info("0 = No limit"),
529
    "interrogate_clip_skip_categories": OptionInfo([], "CLIP: skip inquire categories", gr.CheckboxGroup, lambda: {"choices": modules.interrogate.category_types()}, refresh=modules.interrogate.category_types),
530 531 532 533 534
    "interrogate_deepbooru_score_threshold": OptionInfo(0.5, "deepbooru: score threshold", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01}),
    "deepbooru_sort_alpha": OptionInfo(True, "deepbooru: sort tags alphabetically").info("if not: sort by score"),
    "deepbooru_use_spaces": OptionInfo(True, "deepbooru: use spaces in tags").info("if not: use underscores"),
    "deepbooru_escape": OptionInfo(True, "deepbooru: escape (\\) brackets").info("so they are used as literal brackets and not for emphasis"),
    "deepbooru_filter_tags": OptionInfo("", "deepbooru: filter out those tags").info("separate by comma"),
535
}))
536

537
options_templates.update(options_section(('extra_networks', "Extra Networks"), {
538 539
    "extra_networks_show_hidden_directories": OptionInfo(True, "Show hidden directories").info("directory is hidden if its name starts with \".\"."),
    "extra_networks_hidden_models": OptionInfo("When searched", "Show cards for models in hidden directories", gr.Radio, {"choices": ["Always", "When searched", "Never"]}).info('"When searched" option will only show the item when the search string has 4 characters or more'),
540
    "extra_networks_default_multiplier": OptionInfo(1.0, "Default multiplier for extra networks", gr.Slider, {"minimum": 0.0, "maximum": 2.0, "step": 0.01}),
541 542
    "extra_networks_card_width": OptionInfo(0, "Card width for Extra Networks").info("in pixels"),
    "extra_networks_card_height": OptionInfo(0, "Card height for Extra Networks").info("in pixels"),
543 544
    "extra_networks_card_text_scale": OptionInfo(1.0, "Card text scale", gr.Slider, {"minimum": 0.0, "maximum": 2.0, "step": 0.01}).info("1 = original size"),
    "extra_networks_card_show_desc": OptionInfo(True, "Show description on card"),
545
    "extra_networks_add_text_separator": OptionInfo(" ", "Extra networks separator").info("extra text to add before <...> when adding extra network to prompt"),
W
w-e-w 已提交
546
    "ui_extra_networks_tab_reorder": OptionInfo("", "Extra networks tab order").needs_reload_ui(),
547
    "textual_inversion_print_at_load": OptionInfo(False, "Print a list of Textual Inversion embeddings when loading model"),
548
    "textual_inversion_add_hashes_to_infotext": OptionInfo(True, "Add Textual Inversion hashes to infotext"),
A
AUTOMATIC 已提交
549
    "sd_hypernetwork": OptionInfo("None", "Add hypernetwork to prompt", gr.Dropdown, lambda: {"choices": ["None", *hypernetworks]}, refresh=reload_hypernetworks),
550 551
}))

552
options_templates.update(options_section(('ui', "User interface"), {
W
w-e-w 已提交
553
    "localization": OptionInfo("None", "Localization", gr.Dropdown, lambda: {"choices": ["None"] + list(localization.localizations.keys())}, refresh=lambda: localization.list_localizations(cmd_opts.localizations_dir)).needs_reload_ui(),
554
    "gradio_theme": OptionInfo("Default", "Gradio theme", ui_components.DropdownEditable, lambda: {"choices": ["Default"] + gradio_hf_hub_themes}).info("you can also manually enter any of themes from the <a href='https://huggingface.co/spaces/gradio/theme-gallery'>gallery</a>.").needs_reload_ui(),
W
w-e-w 已提交
555
    "gradio_themes_cache": OptionInfo(True, "Cache gradio themes locally").info("disable to update the selected Gradio theme"),
556
    "return_grid": OptionInfo(True, "Show grid in results for web"),
A
AUTOMATIC 已提交
557
    "do_not_show_images": OptionInfo(False, "Do not show any images in results for web"),
558
    "send_seed": OptionInfo(True, "Send seed when sending prompt or image to other interface"),
559
    "send_size": OptionInfo(True, "Send size when sending prompt or image to another interface"),
560
    "js_modal_lightbox": OptionInfo(True, "Enable full page image viewer"),
A
Aidan Holland 已提交
561
    "js_modal_lightbox_initially_zoomed": OptionInfo(True, "Show images zoomed in by default in full page image viewer"),
562
    "js_modal_lightbox_gamepad": OptionInfo(False, "Navigate image viewer with gamepad"),
M
missionfloyd 已提交
563
    "js_modal_lightbox_gamepad_repeat": OptionInfo(250, "Gamepad repeat period, in milliseconds"),
D
DepFA 已提交
564
    "show_progress_in_title": OptionInfo(True, "Show generation progress in window title."),
W
w-e-w 已提交
565 566
    "samplers_in_dropdown": OptionInfo(True, "Use dropdown for sampler selection instead of radio group").needs_reload_ui(),
    "dimensions_and_batch_together": OptionInfo(True, "Show Width/Height and Batch sliders in same row").needs_reload_ui(),
A
AUTOMATIC 已提交
567 568
    "keyedit_precision_attention": OptionInfo(0.1, "Ctrl+up/down precision when editing (attention:1.1)", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}),
    "keyedit_precision_extra": OptionInfo(0.05, "Ctrl+up/down precision when editing <extra networks:0.9>", gr.Slider, {"minimum": 0.01, "maximum": 0.2, "step": 0.001}),
569
    "keyedit_delimiters": OptionInfo(".,\\/!?%^*;:{}=`~()", "Ctrl+up/down word delimiters"),
570
    "keyedit_move": OptionInfo(True, "Alt+left/right moves prompt elements"),
W
w-e-w 已提交
571 572 573 574 575 576 577
    "quicksettings_list": OptionInfo(["sd_model_checkpoint"], "Quicksettings list", ui_components.DropdownMulti, lambda: {"choices": list(opts.data_labels.keys())}).js("info", "settingsHintsShowQuicksettings").info("setting entries that appear at the top of page rather than in settings tab").needs_reload_ui(),
    "ui_tab_order": OptionInfo([], "UI tab order", ui_components.DropdownMulti, lambda: {"choices": list(tab_names)}).needs_reload_ui(),
    "hidden_tabs": OptionInfo([], "Hidden UI tabs", ui_components.DropdownMulti, lambda: {"choices": list(tab_names)}).needs_reload_ui(),
    "ui_reorder_list": OptionInfo([], "txt2img/img2img UI item order", ui_components.DropdownMulti, lambda: {"choices": list(shared_items.ui_reorder_categories())}).info("selected items appear first").needs_reload_ui(),
    "hires_fix_show_sampler": OptionInfo(False, "Hires fix: show hires checkpoint and sampler selection").needs_reload_ui(),
    "hires_fix_show_prompts": OptionInfo(False, "Hires fix: show hires prompt and negative prompt").needs_reload_ui(),
    "disable_token_counters": OptionInfo(False, "Disable prompt token counters").needs_reload_ui(),
578 579
}))

580

581 582 583
options_templates.update(options_section(('infotext', "Infotext"), {
    "add_model_hash_to_info": OptionInfo(True, "Add model hash to generation information"),
    "add_model_name_to_info": OptionInfo(True, "Add model name to generation information"),
584
    "add_user_name_to_info": OptionInfo(False, "Add user name to generation information when authenticated"),
585
    "add_version_to_infotext": OptionInfo(True, "Add program version to generation information"),
586 587 588 589 590 591 592 593
    "disable_weights_auto_swap": OptionInfo(True, "Disregard checkpoint information from pasted infotext").info("when reading generation parameters from text into UI"),
    "infotext_styles": OptionInfo("Apply if any", "Infer styles from prompts of pasted infotext", gr.Radio, {"choices": ["Ignore", "Apply", "Discard", "Apply if any"]}).info("when reading generation parameters from text into UI)").html("""<ul style='margin-left: 1.5em'>
<li>Ignore: keep prompt and styles dropdown as it is.</li>
<li>Apply: remove style text from prompt, always replace styles dropdown value with found styles (even if none are found).</li>
<li>Discard: remove style text from prompt, keep styles dropdown as it is.</li>
<li>Apply if any: remove style text from prompt; if any styles are found in prompt, put them into styles dropdown, otherwise keep it as it is.</li>
</ul>"""),

594 595
}))

596
options_templates.update(options_section(('ui', "Live previews"), {
A
AUTOMATIC 已提交
597
    "show_progressbar": OptionInfo(True, "Show progressbar"),
598
    "live_previews_enable": OptionInfo(True, "Show live previews of the created image"),
599
    "live_previews_image_format": OptionInfo("png", "Live preview file format", gr.Radio, {"choices": ["jpeg", "png", "webp"]}),
600
    "show_progress_grid": OptionInfo(True, "Show previews of all images generated in a batch as a grid"),
601
    "show_progress_every_n_steps": OptionInfo(10, "Live preview display period", gr.Slider, {"minimum": -1, "maximum": 32, "step": 1}).info("in sampling steps - show new live preview image every N sampling steps; -1 = only show after completion of batch"),
A
AUTOMATIC1111 已提交
602
    "show_progress_type": OptionInfo("Approx NN", "Live preview method", gr.Radio, {"choices": ["Full", "Approx NN", "Approx cheap", "TAESD"]}).info("Full = slow but pretty; Approx NN and TAESD = fast but low quality; Approx cheap = super fast but terrible otherwise"),
603
    "live_preview_content": OptionInfo("Prompt", "Live preview subject", gr.Radio, {"choices": ["Combined", "Prompt", "Negative prompt"]}),
604
    "live_preview_refresh_period": OptionInfo(1000, "Progressbar and preview update period").info("in milliseconds"),
605 606
}))

607
options_templates.update(options_section(('sampler-params', "Sampler parameters"), {
W
w-e-w 已提交
608
    "hide_samplers": OptionInfo([], "Hide samplers in user interface", gr.CheckboxGroup, lambda: {"choices": [x.name for x in list_samplers()]}).needs_reload_ui(),
609 610
    "eta_ddim": OptionInfo(0.0, "Eta for DDIM", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}).info("noise multiplier; higher = more unperdictable results"),
    "eta_ancestral": OptionInfo(1.0, "Eta for ancestral samplers", gr.Slider, {"minimum": 0.0, "maximum": 1.0, "step": 0.01}).info("noise multiplier; applies to Euler a and other samplers that have a in them"),
611
    "ddim_discretize": OptionInfo('uniform', "img2img DDIM discretize", gr.Radio, {"choices": ['uniform', 'quad']}),
612 613 614 615
    's_churn': OptionInfo(0.0, "sigma churn", gr.Slider, {"minimum": 0.0, "maximum": 100.0, "step": 0.01}).info('amount of stochasticity; only applies to Euler, Heun, and DPM2'),
    's_tmin':  OptionInfo(0.0, "sigma tmin",  gr.Slider, {"minimum": 0.0, "maximum": 10.0, "step": 0.01}).info('enable stochasticity; start value of the sigma range; only applies to Euler, Heun, and DPM2'),
    's_tmax':  OptionInfo(0.0, "sigma tmax",  gr.Slider, {"minimum": 0.0, "maximum": 999.0, "step": 0.01}).info("0 = inf; end value of the sigma range; only applies to Euler, Heun, and DPM2"),
    's_noise': OptionInfo(1.0, "sigma noise", gr.Slider, {"minimum": 0.0, "maximum": 1.1, "step": 0.001}).info('amount of additional noise to counteract loss of detail during sampling; only applies to Euler, Heun, and DPM2'),
A
AUTOMATIC1111 已提交
616
    'k_sched_type':  OptionInfo("Automatic", "Scheduler type", gr.Dropdown, {"choices": ["Automatic", "karras", "exponential", "polyexponential"]}).info("lets you override the noise schedule for k-diffusion samplers; choosing Automatic disables the three parameters below"),
617
    'sigma_min': OptionInfo(0.0, "sigma min", gr.Number).info("0 = default (~0.03); minimum noise strength for k-diffusion noise scheduler"),
C
catboxanon 已提交
618 619
    'sigma_max': OptionInfo(0.0, "sigma max", gr.Number).info("0 = default (~14.6); maximum noise strength for k-diffusion noise scheduler"),
    'rho':  OptionInfo(0.0, "rho", gr.Number).info("0 = default (7 for karras, 1 for polyexponential); higher values result in a steeper noise schedule (decreases faster)"),
620 621
    'eta_noise_seed_delta': OptionInfo(0, "Eta noise seed delta", gr.Number, {"precision": 0}).info("ENSD; does not improve anything, just produces different results for ancestral samplers - only useful for reproducing images"),
    'always_discard_next_to_last_sigma': OptionInfo(False, "Always discard next-to-last sigma").link("PR", "https://github.com/AUTOMATIC1111/stable-diffusion-webui/pull/6044"),
S
space-nuko 已提交
622
    'uni_pc_variant': OptionInfo("bh1", "UniPC variant", gr.Radio, {"choices": ["bh1", "bh2", "vary_coeff"]}),
S
space-nuko 已提交
623
    'uni_pc_skip_type': OptionInfo("time_uniform", "UniPC skip type", gr.Radio, {"choices": ["time_uniform", "time_quadratic", "logSNR"]}),
624
    'uni_pc_order': OptionInfo(3, "UniPC order", gr.Slider, {"minimum": 1, "maximum": 50, "step": 1}).info("must be < sampling steps"),
S
space-nuko 已提交
625
    'uni_pc_lower_order_final': OptionInfo(True, "UniPC lower order final"),
626
}))
627

628
options_templates.update(options_section(('postprocessing', "Postprocessing"), {
629 630
    'postprocessing_enable_in_main_ui': OptionInfo([], "Enable postprocessing operations in txt2img and img2img tabs", ui_components.DropdownMulti, lambda: {"choices": [x.name for x in shared_items.postprocessing_scripts()]}),
    'postprocessing_operation_order': OptionInfo([], "Postprocessing operation order", ui_components.DropdownMulti, lambda: {"choices": [x.name for x in shared_items.postprocessing_scripts()]}),
631 632 633
    'upscaling_max_images_in_cache': OptionInfo(5, "Maximum number of images in upscaling cache", gr.Slider, {"minimum": 0, "maximum": 10, "step": 1}),
}))

634
options_templates.update(options_section((None, "Hidden options"), {
635
    "disabled_extensions": OptionInfo([], "Disable these extensions"),
636
    "disable_all_extensions": OptionInfo("none", "Disable all extensions (preserves the list of disabled extensions)", gr.Radio, {"choices": ["none", "extra", "all"]}),
637
    "restore_config_state_file": OptionInfo("", "Config state file to restore from, under 'config-states/' folder"),
638
    "sd_checkpoint_hash": OptionInfo("", "SHA256 hash of the current checkpoint"),
639 640
}))

641

642 643
options_templates.update()

644

645
class Options:
646
    data = None
647
    data_labels = options_templates
648
    typemap = {int: float}
649 650 651 652 653 654

    def __init__(self):
        self.data = {k: v.default for k, v in self.data_labels.items()}

    def __setattr__(self, key, value):
        if self.data is not None:
655
            if key in self.data or key in self.data_labels:
656 657
                assert not cmd_opts.freeze_settings, "changing settings is disabled"

658
                info = opts.data_labels.get(key, None)
659 660 661
                if info.do_not_save:
                    return

662
                comp_args = info.component_args if info else None
663 664 665 666 667 668
                if isinstance(comp_args, dict) and comp_args.get('visible', True) is False:
                    raise RuntimeError(f"not possible to set {key} because it is restricted")

                if cmd_opts.hide_ui_dir_config and key in restricted_opts:
                    raise RuntimeError(f"not possible to set {key} because it is restricted")

669
                self.data[key] = value
670
                return
671 672 673 674 675 676 677 678 679 680 681 682 683

        return super(Options, self).__setattr__(key, value)

    def __getattr__(self, item):
        if self.data is not None:
            if item in self.data:
                return self.data[item]

        if item in self.data_labels:
            return self.data_labels[item].default

        return super(Options, self).__getattribute__(item)

684 685 686 687 688 689 690
    def set(self, key, value):
        """sets an option and calls its onchange callback, returning True if the option changed and False otherwise"""

        oldval = self.data.get(key, None)
        if oldval == value:
            return False

691 692 693
        if self.data_labels[key].do_not_save:
            return False

694 695 696 697 698 699
        try:
            setattr(self, key, value)
        except RuntimeError:
            return False

        if self.data_labels[key].onchange is not None:
700 701 702 703 704 705
            try:
                self.data_labels[key].onchange()
            except Exception as e:
                errors.display(e, f"changing setting {key} to {value}")
                setattr(self, key, oldval)
                return False
706 707 708

        return True

A
AUTOMATIC 已提交
709 710 711 712 713 714 715 716 717
    def get_default(self, key):
        """returns the default value for the key"""

        data_label = self.data_labels.get(key)
        if data_label is None:
            return None

        return data_label.default

718
    def save(self, filename):
719 720
        assert not cmd_opts.freeze_settings, "saving settings is disabled"

721
        with open(filename, "w", encoding="utf8") as file:
W
w-e-w 已提交
722
            json.dump(self.data, file, indent=4)
723

724 725 726
    def same_type(self, x, y):
        if x is None or y is None:
            return True
727

728 729
        type_x = self.typemap.get(type(x), type(x))
        type_y = self.typemap.get(type(y), type(y))
730

731
        return type_x == type_y
732

733 734 735
    def load(self, filename):
        with open(filename, "r", encoding="utf8") as file:
            self.data = json.load(file)
736

A
AUTOMATIC1111 已提交
737 738 739 740
        # 1.6.0 VAE defaults
        if self.data.get('sd_vae_as_default') is not None and self.data.get('sd_vae_overrides_per_model_preferences') is None:
            self.data['sd_vae_overrides_per_model_preferences'] = not self.data.get('sd_vae_as_default')

W
w-e-w 已提交
741 742 743 744
        # 1.1.1 quicksettings list migration
        if self.data.get('quicksettings') is not None and self.data.get('quicksettings_list') is None:
            self.data['quicksettings_list'] = [i.strip() for i in self.data.get('quicksettings').split(',')]

745
        # 1.4.0 ui_reorder
746 747
        if isinstance(self.data.get('ui_reorder'), str) and self.data.get('ui_reorder') and "ui_reorder_list" not in self.data:
            self.data['ui_reorder_list'] = [i.strip() for i in self.data.get('ui_reorder').split(',')]
748

749 750 751
        bad_settings = 0
        for k, v in self.data.items():
            info = self.data_labels.get(k, None)
752
            if info is not None and not self.same_type(info.default, v):
753 754 755 756 757 758
                print(f"Warning: bad setting value: {k}: {v} ({type(v).__name__}; expected {type(info.default).__name__})", file=sys.stderr)
                bad_settings += 1

        if bad_settings > 0:
            print(f"The program is likely to not work with bad settings.\nSettings file: {filename}\nEither fix the file, or delete it and restart.", file=sys.stderr)

M
Muhammad Rizqi Nur 已提交
759
    def onchange(self, key, func, call=True):
760 761 762
        item = self.data_labels.get(key)
        item.onchange = func

M
Muhammad Rizqi Nur 已提交
763 764
        if call:
            func()
A
AUTOMATIC 已提交
765

766
    def dumpjson(self):
767 768 769
        d = {k: self.data.get(k, v.default) for k, v in self.data_labels.items()}
        d["_comments_before"] = {k: v.comment_before for k, v in self.data_labels.items() if v.comment_before is not None}
        d["_comments_after"] = {k: v.comment_after for k, v in self.data_labels.items() if v.comment_after is not None}
770 771
        return json.dumps(d)

772 773 774 775 776 777 778 779
    def add_option(self, key, info):
        self.data_labels[key] = info

    def reorder(self):
        """reorder settings so that all items related to section always go together"""

        section_ids = {}
        settings_items = self.data_labels.items()
A
AUTOMATIC 已提交
780
        for _, item in settings_items:
781 782 783
            if item.section not in section_ids:
                section_ids[item.section] = len(section_ids)

A
AUTOMATIC 已提交
784
        self.data_labels = dict(sorted(settings_items, key=lambda x: section_ids[x[1].section]))
785

786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808
    def cast_value(self, key, value):
        """casts an arbitrary to the same type as this setting's value with key
        Example: cast_value("eta_noise_seed_delta", "12") -> returns 12 (an int rather than str)
        """

        if value is None:
            return None

        default_value = self.data_labels[key].default
        if default_value is None:
            default_value = getattr(self, key, None)
        if default_value is None:
            return None

        expected_type = type(default_value)
        if expected_type == bool and value == "False":
            value = False
        else:
            value = expected_type(value)

        return value


809 810 811 812
opts = Options()
if os.path.exists(config_filename):
    opts.load(config_filename)

813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837

class Shared(sys.modules[__name__].__class__):
    """
    this class is here to provide sd_model field as a property, so that it can be created and loaded on demand rather than
    at program startup.
    """

    sd_model_val = None

    @property
    def sd_model(self):
        import modules.sd_models

        return modules.sd_models.model_data.get_sd_model()

    @sd_model.setter
    def sd_model(self, value):
        import modules.sd_models

        modules.sd_models.model_data.set_sd_model(value)


sd_model: LatentDiffusion = None  # this var is here just for IDE's type checking; it cannot be accessed because the class field above will be accessed instead
sys.modules[__name__].__class__ = Shared

838
settings_components = None
839
"""assinged from ui.py, a mapping on setting names to gradio components repsponsible for those settings"""
840

A
AUTOMATIC 已提交
841 842
latent_upscale_default_mode = "Latent"
latent_upscale_modes = {
M
MMaker 已提交
843 844 845
    "Latent": {"mode": "bilinear", "antialias": False},
    "Latent (antialiased)": {"mode": "bilinear", "antialias": True},
    "Latent (bicubic)": {"mode": "bicubic", "antialias": False},
M
MMaker 已提交
846
    "Latent (bicubic antialiased)": {"mode": "bicubic", "antialias": True},
M
MMaker 已提交
847
    "Latent (nearest)": {"mode": "nearest", "antialias": False},
848
    "Latent (nearest-exact)": {"mode": "nearest-exact", "antialias": False},
A
AUTOMATIC 已提交
849 850
}

A
AUTOMATIC 已提交
851
sd_upscalers = []
852

M
MalumaDev 已提交
853
clip_model = None
A
AUTOMATIC 已提交
854

855
progress_print_out = sys.stdout
A
AUTOMATIC 已提交
856

S
space-nuko 已提交
857 858 859 860 861 862 863 864
gradio_theme = gr.themes.Base()


def reload_gradio_theme(theme_name=None):
    global gradio_theme
    if not theme_name:
        theme_name = opts.gradio_theme

C
catboxanon 已提交
865 866 867 868 869
    default_theme_args = dict(
        font=["Source Sans Pro", 'ui-sans-serif', 'system-ui', 'sans-serif'],
        font_mono=['IBM Plex Mono', 'ui-monospace', 'Consolas', 'monospace'],
    )

S
space-nuko 已提交
870
    if theme_name == "Default":
C
catboxanon 已提交
871
        gradio_theme = gr.themes.Default(**default_theme_args)
S
space-nuko 已提交
872 873
    else:
        try:
874 875
            theme_cache_dir = os.path.join(script_path, 'tmp', 'gradio_themes')
            theme_cache_path = os.path.join(theme_cache_dir, f'{theme_name.replace("/", "_")}.json')
W
w-e-w 已提交
876 877 878
            if opts.gradio_themes_cache and os.path.exists(theme_cache_path):
                gradio_theme = gr.themes.ThemeClass.load(theme_cache_path)
            else:
879
                os.makedirs(theme_cache_dir, exist_ok=True)
W
w-e-w 已提交
880 881
                gradio_theme = gr.themes.ThemeClass.from_hub(theme_name)
                gradio_theme.dump(theme_cache_path)
882 883
        except Exception as e:
            errors.display(e, "changing gradio theme")
C
catboxanon 已提交
884
            gradio_theme = gr.themes.Default(**default_theme_args)
S
space-nuko 已提交
885 886


887 888 889 890 891 892 893 894 895 896 897 898 899
class TotalTQDM:
    def __init__(self):
        self._tqdm = None

    def reset(self):
        self._tqdm = tqdm.tqdm(
            desc="Total progress",
            total=state.job_count * state.sampling_steps,
            position=1,
            file=progress_print_out
        )

    def update(self):
900
        if not opts.multiple_tqdm or cmd_opts.disable_console_progressbars:
901 902 903 904 905
            return
        if self._tqdm is None:
            self.reset()
        self._tqdm.update()

906
    def updateTotal(self, new_total):
907
        if not opts.multiple_tqdm or cmd_opts.disable_console_progressbars:
908 909 910
            return
        if self._tqdm is None:
            self.reset()
911
        self._tqdm.total = new_total
912

913 914
    def clear(self):
        if self._tqdm is not None:
915
            self._tqdm.refresh()
916 917 918 919 920
            self._tqdm.close()
            self._tqdm = None


total_tqdm = TotalTQDM()
E
EyeDeck 已提交
921 922 923

mem_mon = modules.memmon.MemUsageMonitor("MemMon", device, opts)
mem_mon.start()
924 925


926 927 928 929
def natural_sort_key(s, regex=re.compile('([0-9]+)')):
    return [int(text) if text.isdigit() else text.lower() for text in regex.split(s)]


930
def listfiles(dirname):
931
    filenames = [os.path.join(dirname, x) for x in sorted(os.listdir(dirname), key=natural_sort_key) if not x.startswith(".")]
932
    return [file for file in filenames if os.path.isfile(file)]
A
AUTOMATIC 已提交
933 934 935 936 937 938 939 940 941 942 943 944 945 946


def html_path(filename):
    return os.path.join(script_path, "html", filename)


def html(filename):
    path = html_path(filename)

    if os.path.exists(path):
        with open(path, encoding="utf8") as file:
            return file.read()

    return ""
947 948 949 950 951 952 953 954 955


def walk_files(path, allowed_extensions=None):
    if not os.path.exists(path):
        return

    if allowed_extensions is not None:
        allowed_extensions = set(allowed_extensions)

956 957 958 959 960
    items = list(os.walk(path, followlinks=True))
    items = sorted(items, key=lambda x: natural_sort_key(x[0]))

    for root, _, files in items:
        for filename in sorted(files, key=natural_sort_key):
961 962 963 964 965
            if allowed_extensions is not None:
                _, ext = os.path.splitext(filename)
                if ext not in allowed_extensions:
                    continue

966 967 968
            if not opts.list_hidden_files and ("/." in root or "\\." in root):
                continue

969
            yield os.path.join(root, filename)
970 971 972 973 974 975 976


def ldm_print(*args, **kwargs):
    if opts.hide_ldm_prints:
        return

    print(*args, **kwargs)