mirror of
https://github.com/vladmandic/automatic
synced 2026-09-19 09:14:35 +02:00
Merge branch 'dev' into master
This commit is contained in:
@@ -168,7 +168,7 @@ def atomic_civit_search_metadata(item, results):
|
||||
# log.error(f'CivitAI search metadata: item={item} {e}')
|
||||
return
|
||||
has_meta = os.path.isfile(meta) and os.stat(meta).st_size > 0
|
||||
if ('card-no-preview.png' in item['preview'] or not has_meta) and os.path.isfile(item['filename']):
|
||||
if ('missing.png' in item['preview'] or not has_meta) and os.path.isfile(item['filename']):
|
||||
sha = item.get('hash', None)
|
||||
found = False
|
||||
result = {
|
||||
@@ -260,7 +260,7 @@ def civit_search_metadata(title: str = None):
|
||||
if type(title) == str:
|
||||
if page.title != title:
|
||||
continue
|
||||
if page.name == 'style':
|
||||
if page.name == 'style' or page.name == 'wildcards':
|
||||
continue
|
||||
for item in page.list_items():
|
||||
if item is None:
|
||||
|
||||
@@ -200,7 +200,7 @@ def create_model_cards(all_models: list[Model]) -> str:
|
||||
if image.url and len(image.url) > 0 and not image.url.lower().endswith('.mp4'):
|
||||
previews.append(image.url)
|
||||
if len(previews) == 0:
|
||||
previews = ['/sdapi/v1/network/thumb?filename=html/card-no-preview.png']
|
||||
previews = ['/sdapi/v1/network/thumb?filename=html/missing.png']
|
||||
all_cards += card.format(id=model.id, name=model.name, type=model.type, preview=previews[0])
|
||||
html = details + cards.format(cards=all_cards)
|
||||
return html
|
||||
|
||||
@@ -7,6 +7,7 @@ from modules.processing_class import StableDiffusionProcessingControl
|
||||
from modules import shared, images, masking, sd_models
|
||||
from modules.timer import process as process_timer
|
||||
from modules.control import util
|
||||
from modules.control import processors as control_processors
|
||||
|
||||
|
||||
debug = os.environ.get('SD_CONTROL_DEBUG', None) is not None
|
||||
@@ -108,7 +109,7 @@ def preprocess_image(
|
||||
if processed_image is not None:
|
||||
processed_images.append(processed_image)
|
||||
if shared.opts.control_unload_processor and process.processor_id is not None:
|
||||
processors.config[process.processor_id]['dirty'] = True # to force reload
|
||||
control_processors.config[process.processor_id]['dirty'] = True # to force reload
|
||||
process.model = None
|
||||
|
||||
# blend processed images
|
||||
|
||||
@@ -102,6 +102,15 @@ predefined_sd3 = {
|
||||
"Alimama Inpainting SD35": 'alimama-creative/SD3-Controlnet-Inpainting',
|
||||
"Alimama SoftEdge SD35": 'alimama-creative/SD3-Controlnet-Softedge',
|
||||
}
|
||||
predefined_qwen = {
|
||||
"InstantX Union Qwen": 'InstantX/Qwen-Image-ControlNet-Union',
|
||||
}
|
||||
predefined_hunyuandit = {
|
||||
"HunyuanDiT Canny": 'Tencent-Hunyuan/HunyuanDiT-v1.2-ControlNet-Diffusers-Canny',
|
||||
"HunyuanDiT Pose": 'Tencent-Hunyuan/HunyuanDiT-v1.2-ControlNet-Diffusers-Pose',
|
||||
"HunyuanDiT Depth": 'Tencent-Hunyuan/HunyuanDiT-v1.2-ControlNet-Diffusers-Depth',
|
||||
}
|
||||
|
||||
variants = {
|
||||
'NoobAI Canny XL': 'fp16',
|
||||
'NoobAI Lineart Anime XL': 'fp16',
|
||||
@@ -116,6 +125,8 @@ all_models.update(predefined_sd15)
|
||||
all_models.update(predefined_sdxl)
|
||||
all_models.update(predefined_f1)
|
||||
all_models.update(predefined_sd3)
|
||||
all_models.update(predefined_qwen)
|
||||
all_models.update(predefined_hunyuandit)
|
||||
cache_dir = 'models/control/controlnet'
|
||||
load_lock = threading.Lock()
|
||||
|
||||
@@ -150,6 +161,10 @@ def api_list_models(model_type: str = None):
|
||||
model_list += list(predefined_f1)
|
||||
if model_type == 'sd3' or model_type == 'all':
|
||||
model_list += list(predefined_sd3)
|
||||
if model_type == 'qwen' or model_type == 'all':
|
||||
model_list += list(predefined_qwen)
|
||||
if model_type == 'hunyuandit' or model_type == 'all':
|
||||
model_list += list(predefined_hunyuandit)
|
||||
model_list += sorted(find_models())
|
||||
return model_list
|
||||
|
||||
@@ -170,6 +185,10 @@ def list_models(refresh=False):
|
||||
models = ['None'] + list(predefined_f1) + sorted(find_models())
|
||||
elif modules.shared.sd_model_type == 'sd3':
|
||||
models = ['None'] + list(predefined_sd3) + sorted(find_models())
|
||||
elif modules.shared.sd_model_type == 'qwen':
|
||||
models = ['None'] + list(predefined_qwen) + sorted(find_models())
|
||||
elif modules.shared.sd_model_type == 'hunyuandit':
|
||||
models = ['None'] + list(predefined_hunyuandit) + sorted(find_models())
|
||||
else:
|
||||
log.warning(f'Control {what} model list failed: unknown model type')
|
||||
models = ['None'] + sorted(predefined_sd15) + sorted(predefined_sdxl) + sorted(predefined_f1) + sorted(predefined_sd3) + sorted(find_models())
|
||||
@@ -222,12 +241,18 @@ class ControlNet():
|
||||
elif shared.sd_model_type == 'sd3':
|
||||
from diffusers import SD3ControlNetModel as cls
|
||||
config = 'InstantX/SD3-Controlnet-Canny'
|
||||
elif shared.sd_model_type == 'qwen':
|
||||
from diffusers import QwenImageControlNetModel as cls
|
||||
config = 'InstantX/Qwen-Image-ControlNet-Union'
|
||||
elif shared.sd_model_type == 'hunyuandit':
|
||||
from diffusers import HunyuanDiT2DControlNetModel as cls
|
||||
config = 'Tencent-Hunyuan/HunyuanDiT-v1.2-ControlNet-Diffusers-Canny'
|
||||
else:
|
||||
log.error(f'Control {what}: type={shared.sd_model_type} unsupported model')
|
||||
return None, None
|
||||
return cls, config
|
||||
|
||||
def load_safetensors(self, model_id, model_path, cls, config):
|
||||
def load_safetensors(self, model_id, model_path, cls, config): # pylint: disable=unused-argument
|
||||
name = os.path.splitext(model_path)[0]
|
||||
config_path = None
|
||||
if not os.path.exists(model_path):
|
||||
@@ -302,6 +327,7 @@ class ControlNet():
|
||||
errors.display(e, 'Control')
|
||||
if self.model is None:
|
||||
return
|
||||
self.model.offload_never = True
|
||||
if self.dtype is not None:
|
||||
self.model.to(self.dtype)
|
||||
if "Control" in opts.sdnq_quantize_weights:
|
||||
@@ -422,6 +448,30 @@ class ControlNetPipeline():
|
||||
controlnet=controlnets, # can be a list
|
||||
)
|
||||
sd_models.move_model(self.pipeline, pipeline.device)
|
||||
elif detect.is_qwen(pipeline) and len(controlnets) > 0:
|
||||
from diffusers import QwenImageControlNetPipeline
|
||||
self.pipeline = QwenImageControlNetPipeline(
|
||||
vae=pipeline.vae,
|
||||
text_encoder=pipeline.text_encoder,
|
||||
tokenizer=pipeline.tokenizer,
|
||||
transformer=pipeline.transformer,
|
||||
scheduler=pipeline.scheduler,
|
||||
controlnet=controlnets[0] if isinstance(controlnets, list) else controlnets, # can be a list
|
||||
)
|
||||
elif detect.is_hunyuandit(pipeline) and len(controlnets) > 0:
|
||||
from diffusers import HunyuanDiTControlNetPipeline
|
||||
self.pipeline = HunyuanDiTControlNetPipeline(
|
||||
vae=pipeline.vae,
|
||||
text_encoder=pipeline.text_encoder,
|
||||
tokenizer=pipeline.tokenizer,
|
||||
text_encoder_2=pipeline.text_encoder_2,
|
||||
tokenizer_2=pipeline.tokenizer_2,
|
||||
transformer=pipeline.transformer,
|
||||
scheduler=pipeline.scheduler,
|
||||
safety_checker=None,
|
||||
feature_extractor=None,
|
||||
controlnet=controlnets[0] if isinstance(controlnets, list) else controlnets, # can be a list
|
||||
)
|
||||
elif len(loras) > 0:
|
||||
self.pipeline = pipeline
|
||||
for lora in loras:
|
||||
@@ -442,17 +492,19 @@ class ControlNetPipeline():
|
||||
if dtype is not None:
|
||||
self.pipeline = self.pipeline.to(dtype)
|
||||
|
||||
controlnet = None # free up memory
|
||||
controlnets = None
|
||||
sd_models.copy_diffuser_options(self.pipeline, pipeline)
|
||||
if opts.diffusers_offload_mode == 'none':
|
||||
sd_models.move_model(self.pipeline, devices.device)
|
||||
from modules.sd_models import set_diffuser_offload
|
||||
set_diffuser_offload(self.pipeline, 'model')
|
||||
sd_models.clear_caches()
|
||||
sd_models.set_diffuser_offload(self.pipeline, 'model')
|
||||
|
||||
t1 = time.time()
|
||||
debug_log(f'Control {what} pipeline: class={self.pipeline.__class__.__name__} time={t1-t0:.2f}')
|
||||
|
||||
def restore(self):
|
||||
if self.pipeline is not None:
|
||||
if self.pipeline is not None and hasattr(self.pipeline, 'unload_lora_weights'):
|
||||
self.pipeline.unload_lora_weights()
|
||||
self.pipeline = None
|
||||
return self.orig_pipeline
|
||||
|
||||
@@ -20,3 +20,11 @@ def is_f1(model):
|
||||
|
||||
def is_sd3(model):
|
||||
return is_compatible(model, pattern='StableDiffusion3Pipeline')
|
||||
|
||||
|
||||
def is_qwen(model):
|
||||
return is_compatible(model, pattern='Qwen')
|
||||
|
||||
|
||||
def is_hunyuandit(model):
|
||||
return is_compatible(model, pattern='HunyuanDiT')
|
||||
|
||||
@@ -134,7 +134,7 @@ class Script(scripts_manager.Script):
|
||||
app = get_app('buffalo_l')
|
||||
from modules.face.faceid import face_id
|
||||
processed_images = face_id(p, app=app, source_images=input_images, model=ip_model, override=ip_override, cache=ip_cache, scale=ip_strength, structure=ip_structure) # run faceid pipeline
|
||||
processed = processing.Processed(p, images_list=processed_images, seed=p.seed, subseed=p.subseed, index_of_first_image=0) # manually created processed object
|
||||
processed = processing.get_processed(p, images_list=processed_images, seed=p.seed, subseed=p.subseed, index_of_first_image=0) # manually created processed object
|
||||
elif mode == 'PhotoMaker': # photomaker creates pipeline and triggers original process_images
|
||||
from modules.face.insightface import get_app
|
||||
app = get_app('buffalo_l')
|
||||
|
||||
@@ -40,7 +40,7 @@ def create_ui(prompt, negative, styles, _overrides):
|
||||
with gr.Accordion(label="Video", open=False):
|
||||
with gr.Row():
|
||||
mp4_codec = gr.Dropdown(label="FP codec", choices=['none', 'libx264'], value='libx264', type='value')
|
||||
ui_common.create_refresh_button(mp4_codec, get_codecs)
|
||||
ui_common.create_refresh_button(mp4_codec, get_codecs, elem_id="framepack_mp4_codec_refresh")
|
||||
mp4_ext = gr.Textbox(label="FP format", value='mp4', elem_id="framepack_mp4_ext")
|
||||
mp4_opt = gr.Textbox(label="FP options", value='crf:16', elem_id="framepack_mp4_ext")
|
||||
with gr.Row():
|
||||
|
||||
+1
-1
@@ -317,7 +317,7 @@ def img2img(id_task: str, state: str, mode: int,
|
||||
p.is_batch = mode == 5
|
||||
if p.is_batch:
|
||||
process_batch(p, img2img_batch_files, img2img_batch_input_dir, img2img_batch_output_dir, img2img_batch_inpaint_mask_dir, args)
|
||||
processed = processing.Processed(p, [], p.seed, "")
|
||||
processed = processing.get_processed(p, [], p.seed, "")
|
||||
else:
|
||||
processed = scripts_manager.scripts_img2img.run(p, *args)
|
||||
if processed is None:
|
||||
|
||||
@@ -253,7 +253,7 @@ def create_ui():
|
||||
gr.HTML('<h2> Extract currently loaded LoRA(s)<br></h2>')
|
||||
with gr.Row():
|
||||
loaded = gr.Textbox(placeholder="Press refresh to query loaded LoRA", label="Loaded LoRA", interactive=False)
|
||||
create_refresh_button(loaded, lambda: None, lambda: {'value': loaded_lora_str()}, "testid")
|
||||
create_refresh_button(loaded, lambda: None, lambda: {'value': loaded_lora_str()}, "lora_extract_refresh")
|
||||
with gr.Group():
|
||||
with gr.Row():
|
||||
modules = gr.CheckboxGroup(label="Modules to extract", value=['unet'], choices=['te', 'unet'])
|
||||
|
||||
@@ -20,7 +20,7 @@ def create_ui(prompt, negative, styles, overrides):
|
||||
with gr.Row():
|
||||
frames = gr.Slider(label='LTX frames', minimum=1, maximum=513, step=1, value=17, elem_id="ltx_frames")
|
||||
seed = gr.Number(label='LTX seed', value=-1, elem_id="ltx_seed", container=True)
|
||||
random_seed = ToolButton(ui_symbols.random, elem_id="ltx_random_seed")
|
||||
random_seed = ToolButton(ui_symbols.random, elem_id="ltx_seed_random")
|
||||
with gr.Accordion(open=False, label="Condition", elem_id='ltx_condition_accordion'):
|
||||
condition_strength = gr.Slider(label='LTX condition strength', minimum=0.1, maximum=1.0, step=0.05, value=0.8, elem_id="ltx_condition_image_strength")
|
||||
with gr.Tabs():
|
||||
@@ -47,7 +47,7 @@ def create_ui(prompt, negative, styles, overrides):
|
||||
mp4_interpolate = gr.Slider(label="LTX interpolation", minimum=0, maximum=10, value=0, step=1)
|
||||
with gr.Row():
|
||||
mp4_codec = gr.Dropdown(label="LTX codec", choices=['none', 'libx264'], value='libx264', type='value')
|
||||
ui_common.create_refresh_button(mp4_codec, get_codecs)
|
||||
ui_common.create_refresh_button(mp4_codec, get_codecs, elem_id="framepack_mp4_codec_refresh")
|
||||
mp4_ext = gr.Textbox(label="LTX format", value='mp4', elem_id="framepack_mp4_ext")
|
||||
mp4_opt = gr.Textbox(label="LTX options", value='crf:16', elem_id="framepack_mp4_ext")
|
||||
with gr.Row():
|
||||
|
||||
+1
-1
@@ -534,7 +534,7 @@ def create_segment_ui():
|
||||
with gr.Row():
|
||||
controls.append(gr.Checkbox(label="Live update", value=True, elem_id="control_mask_live_update"))
|
||||
btn_mask = ui_components.ToolButton(value=ui_symbols.refresh, visible=True, elem_id="control_mask_refresh", )
|
||||
btn_lama = ui_components.ToolButton(value=ui_symbols.image, visible=True, elem_id="control_mask_lama")
|
||||
btn_lama = ui_components.ToolButton(value=ui_symbols.image, visible=True, elem_id="control_mask_remove")
|
||||
with gr.Row():
|
||||
controls.append(gr.Checkbox(label="Inpaint masked only", value=False, elem_id="control_mask_only", ))
|
||||
controls.append(gr.Checkbox(label="Invert mask", value=False, elem_id="control_mask_invert"))
|
||||
|
||||
@@ -76,6 +76,8 @@ def get_model_type(pipe):
|
||||
# hybrid models
|
||||
elif 'Wan' in name:
|
||||
model_type = 'wanai'
|
||||
elif 'HDM-xut' in name:
|
||||
model_type = 'hdm'
|
||||
else:
|
||||
model_type = name
|
||||
return model_type
|
||||
|
||||
@@ -210,7 +210,9 @@ def get_reference_opts(name: str, quiet=False):
|
||||
# shared.log.error(f'Reference: model="{name}" not found')
|
||||
return {}
|
||||
if not quiet:
|
||||
shared.log.debug(f'Reference: model="{name}" {model_opts}')
|
||||
desc = model_opts.copy()
|
||||
desc.pop('desc', None)
|
||||
shared.log.debug(f'Reference: model="{name}" {desc}')
|
||||
return model_opts
|
||||
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ def create_ui():
|
||||
cache_state_dirname = gr.Textbox(value=None, visible=False)
|
||||
with gr.Row():
|
||||
model_dropdown = gr.Dropdown(label="Model", value="Please select model", choices=checkpoint_titles())
|
||||
create_refresh_button(model_dropdown, refresh_checkpoints, {}, "onnx_cache_refresh_diffusers_model")
|
||||
create_refresh_button(model_dropdown, refresh_checkpoints, {}, "onnx_cache_diffusers_model_refresh")
|
||||
with gr.Row():
|
||||
def remove_cache_onnx_converted(dirname: str):
|
||||
shutil.rmtree(os.path.join(opts.onnx_cached_models_path, dirname))
|
||||
|
||||
@@ -92,7 +92,7 @@ def rgb_to_ycbcr_tensor(image: torch.ByteTensor) -> torch.FloatTensor:
|
||||
|
||||
@devices.inference_context()
|
||||
def ycbcr_tensor_to_rgb(ycbcr: torch.FloatTensor) -> torch.ByteTensor:
|
||||
ycbcr_img = (ycbcr / 2)
|
||||
ycbcr_img = ycbcr / 2
|
||||
y = ycbcr_img[:,0,:,:].add_(0.5)
|
||||
cb = ycbcr_img[:,1,:,:]
|
||||
cr = ycbcr_img[:,2,:,:]
|
||||
@@ -171,12 +171,12 @@ def process_image_input(images: PipelineImageInput) -> torch.ByteTensor:
|
||||
img = torch.from_numpy(np.asarray(img).copy()).unsqueeze(0)
|
||||
combined_images.append(img)
|
||||
elif isinstance(img, np.ndarray):
|
||||
if len(img.shape) == 3:
|
||||
img = img.unsqueeze(0)
|
||||
img = torch.from_numpy(img)
|
||||
if img.ndim == 3:
|
||||
img = img.unsqueeze(0)
|
||||
combined_images.append(img)
|
||||
elif isinstance(img, torch.Tensor):
|
||||
if len(img.shape) == 3:
|
||||
if img.ndim == 3:
|
||||
img = img.unsqueeze(0)
|
||||
combined_images.append(img)
|
||||
else:
|
||||
@@ -186,11 +186,11 @@ def process_image_input(images: PipelineImageInput) -> torch.ByteTensor:
|
||||
combined_images = torch.from_numpy(np.asarray(images).copy()).unsqueeze(0)
|
||||
elif isinstance(images, np.ndarray):
|
||||
combined_images = torch.from_numpy(images)
|
||||
if len(combined_images.shape) == 3:
|
||||
if combined_images.ndim == 3:
|
||||
combined_images = combined_images.unsqueeze(0)
|
||||
elif isinstance(images, torch.Tensor):
|
||||
combined_images = images
|
||||
if len(combined_images.shape) == 3:
|
||||
if combined_images.ndim == 3:
|
||||
combined_images = combined_images.unsqueeze(0)
|
||||
else:
|
||||
raise RuntimeError(f"Invalid input! Given: {type(images)} should be in ('torch.Tensor', 'np.ndarray', 'PIL.Image.Image')")
|
||||
|
||||
@@ -414,8 +414,8 @@ class YoloRestorer(Detailer):
|
||||
with gr.Row():
|
||||
detailers = gr.Dropdown(label="Detailer models", elem_id=f"{tab}_detailers", choices=list(self.list), value=shared.opts.detailer_models, multiselect=True, visible=True)
|
||||
detailers_text = gr.Textbox(label="Detailer list", elem_id=f"{tab}_detailers_text", placeholder="Comma separated list of detailer models", lines=2, visible=False, interactive=True)
|
||||
refresh_btn = ui_common.create_refresh_button(detailers, self.enumerate, lambda: {"choices": self.enumerate()}, 'yolo_refresh_models')
|
||||
ui_mode = ui_components.ToolButton(value=ui_symbols.view)
|
||||
refresh_btn = ui_common.create_refresh_button(detailers, self.enumerate, lambda: {"choices": self.enumerate()}, 'yolo_models_refresh')
|
||||
ui_mode = ui_components.ToolButton(value=ui_symbols.view, elem_id=f'{tab}_yolo_models_list')
|
||||
ui_mode.click(fn=self.change_mode, inputs=[detailers, detailers_text], outputs=[detailers, detailers_text, refresh_btn])
|
||||
with gr.Row():
|
||||
classes = gr.Textbox(label="Detailer classes", placeholder="Classes", elem_id=f"{tab}_detailer_classes")
|
||||
|
||||
+39
-22
@@ -27,50 +27,59 @@ get_sampler_index = processing_helpers.get_sampler_index
|
||||
validate_sample = processing_helpers.validate_sample
|
||||
decode_first_stage = processing_helpers.decode_first_stage
|
||||
images_tensor_to_samples = processing_helpers.images_tensor_to_samples
|
||||
processed = None # last known processed results
|
||||
|
||||
|
||||
class Processed:
|
||||
def __init__(self, p: StableDiffusionProcessing, images_list, seed=-1, info=None, subseed=None, all_prompts=None, all_negative_prompts=None, all_seeds=None, all_subseeds=None, index_of_first_image=0, infotexts=None, comments=""):
|
||||
self.images = images_list
|
||||
self.sd_model_hash = getattr(shared.sd_model, 'sd_model_hash', '') if model_data.sd_model is not None else ''
|
||||
|
||||
self.prompt = p.prompt or ''
|
||||
self.negative_prompt = p.negative_prompt or ''
|
||||
self.seed = seed if seed != -1 else p.seed
|
||||
self.subseed = subseed
|
||||
self.subseed_strength = p.subseed_strength
|
||||
self.info = info or create_infotext(p)
|
||||
self.comments = comments or ''
|
||||
self.prompt = self.prompt if type(self.prompt) != list else self.prompt[0]
|
||||
self.negative_prompt = self.negative_prompt if type(self.negative_prompt) != list else self.negative_prompt[0]
|
||||
self.styles = p.styles
|
||||
|
||||
self.images = images_list
|
||||
self.width = p.width if hasattr(p, 'width') else (self.images[0].width if len(self.images) > 0 else 0)
|
||||
self.height = p.height if hasattr(p, 'height') else (self.images[0].height if len(self.images) > 0 else 0)
|
||||
|
||||
self.sampler_name = p.sampler_name or ''
|
||||
self.cfg_scale = p.cfg_scale if p.cfg_scale > 1 else None
|
||||
self.cfg_end = p.cfg_end if p.cfg_end < 0 else None
|
||||
self.image_cfg_scale = p.image_cfg_scale or 0
|
||||
self.steps = p.steps or 0
|
||||
self.batch_size = max(1, p.batch_size)
|
||||
self.denoising_strength = p.denoising_strength
|
||||
|
||||
self.restore_faces = p.restore_faces or False
|
||||
self.face_restoration_model = shared.opts.face_restoration_model if p.restore_faces else None
|
||||
self.detailer = p.detailer_enabled or False
|
||||
self.detailer_model = shared.opts.detailer_model if p.detailer_enabled else None
|
||||
self.sd_model_hash = getattr(shared.sd_model, 'sd_model_hash', '') if model_data.sd_model is not None else ''
|
||||
self.seed_resize_from_w = p.seed_resize_from_w
|
||||
self.seed_resize_from_h = p.seed_resize_from_h
|
||||
self.denoising_strength = p.denoising_strength
|
||||
self.extra_generation_params = p.extra_generation_params
|
||||
self.index_of_first_image = index_of_first_image
|
||||
self.styles = p.styles
|
||||
self.job_timestamp = shared.state.job_timestamp
|
||||
self.clip_skip = p.clip_skip
|
||||
self.eta = p.eta
|
||||
self.prompt = self.prompt if type(self.prompt) != list else self.prompt[0]
|
||||
self.negative_prompt = self.negative_prompt if type(self.negative_prompt) != list else self.negative_prompt[0]
|
||||
|
||||
self.seed = seed if seed != -1 else p.seed
|
||||
self.subseed = subseed
|
||||
self.seed = int(self.seed if type(self.seed) != list else self.seed[0]) if self.seed is not None else -1
|
||||
self.subseed = int(self.subseed if type(self.subseed) != list else self.subseed[0]) if self.subseed is not None else -1
|
||||
self.subseed_strength = p.subseed_strength
|
||||
|
||||
self.is_using_inpainting_conditioning = p.is_using_inpainting_conditioning
|
||||
|
||||
self.all_prompts = all_prompts or p.all_prompts or [self.prompt]
|
||||
self.all_negative_prompts = all_negative_prompts or p.all_negative_prompts or [self.negative_prompt]
|
||||
self.all_seeds = all_seeds or p.all_seeds or [self.seed]
|
||||
self.all_subseeds = all_subseeds or p.all_subseeds or [self.subseed]
|
||||
|
||||
self.info = info or create_infotext(p)
|
||||
self.infotexts = infotexts or [self.info]
|
||||
self.comments = comments or ''
|
||||
memstats.reset_stats()
|
||||
|
||||
def js(self):
|
||||
@@ -113,6 +122,12 @@ class Processed:
|
||||
return f'{self.__class__.__name__}: {self.__dict__}'
|
||||
|
||||
|
||||
def get_processed(*args, **kwargs):
|
||||
global processed # pylint: disable=global-statement
|
||||
processed = Processed(*args, **kwargs)
|
||||
return processed
|
||||
|
||||
|
||||
def process_images(p: StableDiffusionProcessing) -> Processed:
|
||||
timer.process.reset()
|
||||
debug(f'Process images: {vars(p)}')
|
||||
@@ -133,7 +148,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
|
||||
p.override_settings.pop(k, None)
|
||||
for k in p.override_settings.keys():
|
||||
stored_opts[k] = shared.opts.data.get(k, None) or shared.opts.data_labels[k].default
|
||||
processed = None
|
||||
results = None
|
||||
try:
|
||||
# if no checkpoint override or the override checkpoint can't be found, remove override entry and load opts checkpoint
|
||||
if p.override_settings.get('sd_model_checkpoint', None) is not None and sd_checkpoint.checkpoint_aliases.get(p.override_settings.get('sd_model_checkpoint')) is None:
|
||||
@@ -196,11 +211,11 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
|
||||
shared.log.debug(f'Torch profile: {profile_args}')
|
||||
shared.profiler = torch.profiler.profile(**profile_args)
|
||||
shared.profiler.start()
|
||||
processed = process_images_inner(p)
|
||||
results = process_images_inner(p)
|
||||
errors.profile_torch(shared.profiler, 'Process')
|
||||
else:
|
||||
with context_hypertile_vae(p), context_hypertile_unet(p):
|
||||
processed = process_images_inner(p)
|
||||
results = process_images_inner(p)
|
||||
|
||||
finally:
|
||||
script_callbacks.after_process_callback(p)
|
||||
@@ -215,7 +230,7 @@ def process_images(p: StableDiffusionProcessing) -> Processed:
|
||||
if k == 'sd_vae':
|
||||
sd_vae.reload_vae_weights()
|
||||
timer.process.record('post')
|
||||
return processed
|
||||
return results
|
||||
|
||||
|
||||
def process_init(p: StableDiffusionProcessing):
|
||||
@@ -370,6 +385,8 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
p.init(p.all_prompts, p.all_seeds, p.all_subseeds)
|
||||
debug(f'Processing inner: args={vars(p)}')
|
||||
for n in range(p.n_iter):
|
||||
if p.n_iter > 1:
|
||||
shared.log.debug(f'Processing: batch={n+1} total={p.n_iter} progress={(n+1)/p.n_iter:.2f}')
|
||||
shared.state.batch_no = n + 1
|
||||
debug(f'Processing inner: iteration={n+1}/{p.n_iter}')
|
||||
p.iteration = n
|
||||
@@ -397,10 +414,10 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
samples = None
|
||||
timer.process.record('init')
|
||||
if p.scripts is not None and isinstance(p.scripts, scripts_manager.ScriptRunner):
|
||||
processed = p.scripts.process_images(p)
|
||||
if processed is not None:
|
||||
samples = processed.images
|
||||
for script_image, script_infotext in zip(processed.images, processed.infotexts):
|
||||
results = p.scripts.process_images(p)
|
||||
if results is not None:
|
||||
samples = results.images
|
||||
for script_image, script_infotext in zip(results.images, results.infotexts):
|
||||
output_images.append(script_image)
|
||||
infotexts.append(script_infotext)
|
||||
if samples is None:
|
||||
@@ -451,7 +468,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
if shared.opts.grid_save:
|
||||
images.save_image(grid, p.outpath_grids, "", p.all_seeds[0], p.all_prompts[0], shared.opts.grid_format, info=grid_info, p=p, grid=True) # main save grid
|
||||
|
||||
processed = Processed(
|
||||
results = get_processed(
|
||||
p,
|
||||
images_list=output_images,
|
||||
seed=p.all_seeds[0],
|
||||
@@ -462,7 +479,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
infotexts=infotexts,
|
||||
)
|
||||
if p.scripts is not None and isinstance(p.scripts, scripts_manager.ScriptRunner) and not (shared.state.interrupted or shared.state.skipped):
|
||||
p.scripts.postprocess(p, processed)
|
||||
p.scripts.postprocess(p, results)
|
||||
timer.process.record('post')
|
||||
p.ops = list(set(p.ops))
|
||||
if not p.disable_extra_networks:
|
||||
@@ -472,4 +489,4 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed:
|
||||
|
||||
if shared.cmd_opts.lowvram or shared.cmd_opts.medvram:
|
||||
devices.torch_gc(force=True, reason='final')
|
||||
return processed
|
||||
return results
|
||||
|
||||
@@ -310,7 +310,7 @@ def set_pipeline_args(p, model, prompts:list, negative_prompts:list, prompts_2:t
|
||||
if 'Flex2' in model.__class__.__name__:
|
||||
if len(getattr(p, 'init_images', [])) > 0:
|
||||
args['inpaint_image'] = p.init_images[0] if isinstance(p.init_images, list) else p.init_images
|
||||
args['inpaint_mask'] = Image.new('L', args['inpaint_image'].size, 1)
|
||||
args['inpaint_mask'] = Image.new('L', args['inpaint_image'].size, int(p.denoising_strength * 255))
|
||||
args['control_image'] = args['inpaint_image'].convert('L').convert('RGB') # will be interpreted as depth
|
||||
args['control_strength'] = p.denoising_strength
|
||||
args['width'] = p.width
|
||||
@@ -358,8 +358,8 @@ def set_pipeline_args(p, model, prompts:list, negative_prompts:list, prompts_2:t
|
||||
task_kwargs = task_specific_kwargs(p, model)
|
||||
pipe_args = getattr(p, 'task_args', {})
|
||||
model_args = getattr(model, 'task_args', {})
|
||||
task_kwargs.update(pipe_args)
|
||||
task_kwargs.update(model_args)
|
||||
task_kwargs.update(pipe_args or {})
|
||||
task_kwargs.update(model_args or {})
|
||||
if debug_enabled:
|
||||
debug_log(f'Process task args: {task_kwargs}')
|
||||
for k, v in task_kwargs.items():
|
||||
@@ -382,8 +382,15 @@ def set_pipeline_args(p, model, prompts:list, negative_prompts:list, prompts_2:t
|
||||
if 'width' in possible and 'height' in possible:
|
||||
vae_scale_factor = sd_vae.get_vae_scale_factor(model)
|
||||
if isinstance(args['image'], torch.Tensor) or isinstance(args['image'], np.ndarray):
|
||||
args['width'] = vae_scale_factor * args['image'].shape[-1]
|
||||
args['height'] = vae_scale_factor * args['image'].shape[-2]
|
||||
if args['image'].shape[-1] == 3: # nhwc
|
||||
args['width'] = args['image'].shape[-2]
|
||||
args['height'] = args['image'].shape[-3]
|
||||
elif args['image'].shape[-3] == 3: # nchw
|
||||
args['width'] = args['image'].shape[-1]
|
||||
args['height'] = args['image'].shape[-2]
|
||||
else: # assume latent
|
||||
args['width'] = vae_scale_factor * args['image'].shape[-1]
|
||||
args['height'] = vae_scale_factor * args['image'].shape[-2]
|
||||
elif isinstance(args['image'], Image.Image):
|
||||
args['width'] = args['image'].width
|
||||
args['height'] = args['image'].height
|
||||
|
||||
@@ -223,6 +223,11 @@ def process_hires(p: processing.StableDiffusionProcessing, output):
|
||||
shared.state.update('Upscale', 0, 1)
|
||||
output.images = resize_hires(p, latents=output.images)
|
||||
sd_hijack_hypertile.hypertile_set(p, hr=True)
|
||||
elif torch.is_tensor(output.images) and output.images.shape[-1] == 3: # nhwc
|
||||
if output.images.dim() == 3:
|
||||
output.images = TF.to_pil_image(output.images.permute(2,0,1))
|
||||
elif output.images.dim() == 4:
|
||||
output.images = [TF.to_pil_image(output.images[i].permute(2,0,1)) for i in range(output.images.shape[0])]
|
||||
|
||||
strength = p.hr_denoising_strength if p.hr_denoising_strength > 0 else p.denoising_strength
|
||||
if (p.hr_upscaler.lower().startswith('latent') or p.hr_force) and strength > 0:
|
||||
|
||||
@@ -191,7 +191,6 @@ else:
|
||||
|
||||
def set_blaslt_enabled(enabled: bool) -> None:
|
||||
if enabled:
|
||||
load_library_global("/opt/rocm/lib/libhipblaslt.so") # Preload hipBLASLt.
|
||||
os.environ["HIPBLASLT_TENSILE_LIBPATH"] = blaslt_tensile_libpath
|
||||
else:
|
||||
os.environ["TORCH_BLAS_PREFER_HIPBLASLT"] = "0"
|
||||
|
||||
@@ -48,6 +48,8 @@ def guess_by_name(fn, current_guess):
|
||||
return 'SegMoE'
|
||||
elif 'hunyuandit' in fn.lower():
|
||||
return 'HunyuanDiT'
|
||||
elif 'hdm-xut' in fn.lower():
|
||||
return 'hdm'
|
||||
elif 'pixart-xl' in fn.lower():
|
||||
return 'PixArt Alpha'
|
||||
elif 'stable-diffusion-3' in fn.lower():
|
||||
|
||||
@@ -6,11 +6,12 @@ from modules import shared, errors, timer, sd_models
|
||||
def hijack_encode_prompt(*args, **kwargs):
|
||||
shared.state.begin('TE')
|
||||
t0 = time.time()
|
||||
if 'max_sequence_length' in kwargs:
|
||||
if 'max_sequence_length' in kwargs and kwargs['max_sequence_length'] is not None:
|
||||
kwargs['max_sequence_length'] = max(kwargs['max_sequence_length'], os.environ.get('HIDREAM_MAX_SEQUENCE_LENGTH', 256))
|
||||
# if hasattr(shared.sd_model, 'text_encoder') and shared.sd_model.text_encoder is not None:
|
||||
# sd_models.move_model(shared.sd_model.text_encoder, devices.device)
|
||||
try:
|
||||
prompt = kwargs.get('prompt', None) or (args[0] if len(args) > 0 else None)
|
||||
if prompt is not None:
|
||||
shared.log.debug(f'Encode: prompt="{prompt}" hijack=True')
|
||||
res = shared.sd_model.orig_encode_prompt(*args, **kwargs)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Encode prompt: {e}')
|
||||
@@ -18,15 +19,14 @@ def hijack_encode_prompt(*args, **kwargs):
|
||||
res = None
|
||||
t1 = time.time()
|
||||
timer.process.add('te', t1-t0)
|
||||
if hasattr(shared.sd_model, "maybe_free_model_hooks"):
|
||||
shared.sd_model.maybe_free_model_hooks()
|
||||
# if hasattr(shared.sd_model, "maybe_free_model_hooks"):
|
||||
# shared.sd_model.maybe_free_model_hooks()
|
||||
shared.sd_model = sd_models.apply_balanced_offload(shared.sd_model)
|
||||
shared.state.end()
|
||||
return res
|
||||
|
||||
|
||||
def init_hijack(pipe):
|
||||
if shared.opts.te_hijack and pipe is not None and not hasattr(pipe, 'orig_encode_prompt') and hasattr(pipe, 'encode_prompt'):
|
||||
# shared.log.debug(f'Model: cls={pipe.__class__.__name__} hijack encode')
|
||||
if pipe is not None and not hasattr(pipe, 'orig_encode_prompt') and hasattr(pipe, 'encode_prompt'):
|
||||
pipe.orig_encode_prompt = pipe.encode_prompt
|
||||
pipe.encode_prompt = hijack_encode_prompt
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import os
|
||||
import time
|
||||
import torch
|
||||
from modules import shared, sd_models, devices, timer, errors
|
||||
|
||||
|
||||
debug = shared.log.trace if os.environ.get('SD_VIDEO_DEBUG', None) is not None else lambda *args, **kwargs: None
|
||||
|
||||
|
||||
def hijack_vae_decode(*args, **kwargs):
|
||||
shared.state.begin('VAE')
|
||||
t0 = time.time()
|
||||
res = None
|
||||
shared.sd_model = sd_models.apply_balanced_offload(shared.sd_model, exclude=['vae'])
|
||||
try:
|
||||
sd_models.move_model(shared.sd_model.vae, devices.device)
|
||||
if torch.is_tensor(args[0]):
|
||||
latents = args[0].to(device=devices.device, dtype=shared.sd_model.vae.dtype) # upcast to vae dtype
|
||||
res = shared.sd_model.vae.orig_decode(latents, *args[1:], **kwargs)
|
||||
t1 = time.time()
|
||||
shared.log.debug(f'Decode: vae={shared.sd_model.vae.__class__.__name__} slicing={getattr(shared.sd_model.vae, "use_slicing", None)} tiling={getattr(shared.sd_model.vae, "use_tiling", None)} latents={list(latents.shape)}:{latents.device}:{latents.dtype} time={t1-t0:.3f}')
|
||||
else:
|
||||
res = shared.sd_model.vae.orig_decode(*args, **kwargs)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Decode: vae={shared.sd_model.vae.__class__.__name__} {e}')
|
||||
errors.display(e, 'vae')
|
||||
res = None
|
||||
t1 = time.time()
|
||||
timer.process.add('vae', t1-t0)
|
||||
shared.state.end()
|
||||
return res
|
||||
|
||||
|
||||
def hijack_vae_encode(*args, **kwargs):
|
||||
shared.state.begin('VAE')
|
||||
t0 = time.time()
|
||||
res = None
|
||||
shared.sd_model = sd_models.apply_balanced_offload(shared.sd_model, exclude=['vae'])
|
||||
try:
|
||||
sd_models.move_model(shared.sd_model.vae, devices.device)
|
||||
if torch.is_tensor(args[0]):
|
||||
latents = args[0].to(device=devices.device, dtype=shared.sd_model.vae.dtype) # upcast to vae dtype
|
||||
res = shared.sd_model.vae.orig_encode(latents, *args[1:], **kwargs)
|
||||
t1 = time.time()
|
||||
shared.log.debug(f'Encode: vae={shared.sd_model.vae.__class__.__name__} slicing={getattr(shared.sd_model.vae, "use_slicing", None)} tiling={getattr(shared.sd_model.vae, "use_tiling", None)} latents={list(latents.shape)}:{latents.device}:{latents.dtype} time={t1-t0:.3f}')
|
||||
else:
|
||||
res = shared.sd_model.vae.orig_encode(*args, **kwargs)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Encode: vae={shared.sd_model.vae.__class__.__name__} {e}')
|
||||
errors.display(e, 'vae')
|
||||
res = None
|
||||
t1 = time.time()
|
||||
timer.process.add('vae', t1-t0)
|
||||
shared.state.end()
|
||||
return res
|
||||
|
||||
|
||||
def init_hijack(pipe):
|
||||
if pipe is not None and hasattr(pipe, 'vae') and hasattr(pipe.vae, 'decode') and not hasattr(pipe.vae, 'orig_decode'):
|
||||
pipe.vae.orig_decode = pipe.vae.decode
|
||||
pipe.vae.decode = hijack_vae_decode
|
||||
if pipe is not None and hasattr(pipe, 'vae') and hasattr(pipe.vae, 'encode') and not hasattr(pipe.vae, 'orig_encode'):
|
||||
pipe.vae.orig_encode = pipe.vae.encode
|
||||
pipe.vae.encode = hijack_vae_encode
|
||||
+61
-28
@@ -57,22 +57,6 @@ i2i_pipes = [
|
||||
]
|
||||
|
||||
|
||||
def copy_diffuser_options(new_pipe, orig_pipe):
|
||||
new_pipe.sd_checkpoint_info = getattr(orig_pipe, 'sd_checkpoint_info', None)
|
||||
new_pipe.sd_model_checkpoint = getattr(orig_pipe, 'sd_model_checkpoint', None)
|
||||
new_pipe.embedding_db = getattr(orig_pipe, 'embedding_db', None)
|
||||
new_pipe.sd_model_hash = getattr(orig_pipe, 'sd_model_hash', None)
|
||||
new_pipe.has_accelerate = getattr(orig_pipe, 'has_accelerate', False)
|
||||
new_pipe.current_attn_name = getattr(orig_pipe, 'current_attn_name', None)
|
||||
new_pipe.default_scheduler = getattr(orig_pipe, 'default_scheduler', None)
|
||||
new_pipe.is_sdxl = getattr(orig_pipe, 'is_sdxl', False) # a1111 compatibility item
|
||||
new_pipe.is_sd2 = getattr(orig_pipe, 'is_sd2', False)
|
||||
new_pipe.is_sd1 = getattr(orig_pipe, 'is_sd1', True)
|
||||
add_noise_pred_to_diffusers_callback(new_pipe)
|
||||
if new_pipe.has_accelerate:
|
||||
set_accelerate(new_pipe)
|
||||
|
||||
|
||||
def set_huggingface_options():
|
||||
if shared.opts.diffusers_to_gpu: # and model_type.startswith('Stable Diffusion'):
|
||||
sd_hijack_accelerate.hijack_accelerate()
|
||||
@@ -321,7 +305,7 @@ def load_diffuser_force(model_type, checkpoint_info, diffusers_load_config, op='
|
||||
elif model_type in ['AuraFlow']: # forced pipeline
|
||||
from pipelines.model_auraflow import load_auraflow
|
||||
sd_model = load_auraflow(checkpoint_info, diffusers_load_config)
|
||||
allow_post_quant = True
|
||||
allow_post_quant = False
|
||||
elif model_type in ['FLUX']:
|
||||
from pipelines.model_flux import load_flux
|
||||
sd_model = load_flux(checkpoint_info, diffusers_load_config)
|
||||
@@ -355,7 +339,7 @@ def load_diffuser_force(model_type, checkpoint_info, diffusers_load_config, op='
|
||||
sd_model = load_meissonic(checkpoint_info, diffusers_load_config)
|
||||
allow_post_quant = True
|
||||
elif model_type in ['OmniGen2']: # forced pipeline
|
||||
from pipelines.model_omnigen2 import load_omnigen2
|
||||
from pipelines.model_omnigen import load_omnigen2
|
||||
sd_model = load_omnigen2(checkpoint_info, diffusers_load_config)
|
||||
allow_post_quant = False
|
||||
elif model_type in ['OmniGen']: # forced pipeline
|
||||
@@ -397,7 +381,7 @@ def load_diffuser_force(model_type, checkpoint_info, diffusers_load_config, op='
|
||||
elif model_type in ['Kandinsky 2.2']:
|
||||
from pipelines.model_kandinsky import load_kandinsky22
|
||||
sd_model = load_kandinsky22(checkpoint_info, diffusers_load_config)
|
||||
allow_post_quant = False
|
||||
allow_post_quant = True
|
||||
elif model_type in ['Kandinsky 3.0']:
|
||||
from pipelines.model_kandinsky import load_kandinsky3
|
||||
sd_model = load_kandinsky3(checkpoint_info, diffusers_load_config)
|
||||
@@ -406,6 +390,10 @@ def load_diffuser_force(model_type, checkpoint_info, diffusers_load_config, op='
|
||||
from pipelines.model_nextstep import load_nextstep
|
||||
sd_model = load_nextstep(checkpoint_info, diffusers_load_config) # pylint: disable=assignment-from-none
|
||||
allow_post_quant = False
|
||||
elif model_type in ['hdm']:
|
||||
from pipelines.model_hdm import load_hdm
|
||||
sd_model = load_hdm(checkpoint_info, diffusers_load_config)
|
||||
allow_post_quant = False
|
||||
except Exception as e:
|
||||
shared.log.error(f'Load {op}: path="{checkpoint_info.path}" {e}')
|
||||
if debug_load:
|
||||
@@ -539,16 +527,34 @@ def load_diffuser_file(model_type, pipeline, checkpoint_info, diffusers_load_con
|
||||
|
||||
|
||||
def set_overrides(sd_model, checkpoint_info):
|
||||
if 'bigaspv25' in checkpoint_info.name.lower():
|
||||
checkpoint_info_name = checkpoint_info.name.lower()
|
||||
if 'bigaspv25' in checkpoint_info_name or 'nyaflow' in checkpoint_info_name:
|
||||
scheduler_config = sd_model.scheduler.config
|
||||
scheduler_config['prediction_type'] = 'flow_prediction'
|
||||
scheduler_config['use_flow_sigmas'] = True
|
||||
scheduler_config['beta_schedule'] = 'linear'
|
||||
sd_model.scheduler = diffusers.UniPCMultistepScheduler.from_config(scheduler_config)
|
||||
shared.log.info(f'Setting override: model="{checkpoint_info.name}" component=scheduler prediction="flow-prediction"')
|
||||
if 'vpred' in checkpoint_info.name.lower() or 'v-pred' in checkpoint_info.name.lower():
|
||||
elif 'vpred' in checkpoint_info_name or 'v-pred' in checkpoint_info_name or 'v_pred' in checkpoint_info_name:
|
||||
scheduler_config = sd_model.scheduler.config
|
||||
scheduler_config['prediction_type'] = 'v_prediction'
|
||||
scheduler_config['rescale_betas_zero_snr'] = True
|
||||
sd_model.scheduler = diffusers.EulerDiscreteScheduler.from_config(scheduler_config)
|
||||
shared.log.info(f'Setting override: model="{checkpoint_info.name}" component=scheduler prediction="v-prediction"')
|
||||
shared.log.info(f'Setting override: model="{checkpoint_info.name}" component=scheduler prediction="v-prediction" rescale=True')
|
||||
elif checkpoint_info.path.lower().endswith('.safetensors'):
|
||||
try:
|
||||
from safetensors import safe_open
|
||||
with safe_open(checkpoint_info.path, framework='pt') as f:
|
||||
keys = f.keys()
|
||||
if 'v_pred' in keys: # NoobAI VPred models added empty v_pred and ztsnr keys
|
||||
scheduler_config = sd_model.scheduler.config
|
||||
scheduler_config['prediction_type'] = 'v_prediction'
|
||||
if 'ztsnr' in keys:
|
||||
scheduler_config['rescale_betas_zero_snr'] = True
|
||||
sd_model.scheduler = diffusers.EulerDiscreteScheduler.from_config(scheduler_config)
|
||||
shared.log.info(f'Setting override: model="{checkpoint_info.name}" component=scheduler prediction="v-prediction" rescale={scheduler_config.get("rescale_betas_zero_snr", False)}')
|
||||
except Exception as e:
|
||||
shared.log.debug(f'Setting override from keys failed: {e}')
|
||||
|
||||
|
||||
def set_defaults(sd_model, checkpoint_info):
|
||||
@@ -854,6 +860,28 @@ def clean_diffuser_pipe(pipe):
|
||||
pipe.register_to_config(**internal_dict)
|
||||
|
||||
|
||||
def copy_diffuser_options(new_pipe, orig_pipe):
|
||||
new_pipe.sd_checkpoint_info = getattr(orig_pipe, 'sd_checkpoint_info', None)
|
||||
new_pipe.sd_model_checkpoint = getattr(orig_pipe, 'sd_model_checkpoint', None)
|
||||
new_pipe.embedding_db = getattr(orig_pipe, 'embedding_db', None)
|
||||
new_pipe.loaded_loras = getattr(orig_pipe, 'loaded_loras', {})
|
||||
new_pipe.sd_model_hash = getattr(orig_pipe, 'sd_model_hash', None)
|
||||
new_pipe.has_accelerate = getattr(orig_pipe, 'has_accelerate', False)
|
||||
new_pipe.current_attn_name = getattr(orig_pipe, 'current_attn_name', None)
|
||||
new_pipe.default_scheduler = getattr(orig_pipe, 'default_scheduler', None)
|
||||
new_pipe.image_encoder = getattr(orig_pipe, 'image_encoder', None)
|
||||
new_pipe.feature_extractor = getattr(orig_pipe, 'feature_extractor', None)
|
||||
new_pipe.mask_processor = getattr(orig_pipe, 'mask_processor', None)
|
||||
new_pipe.restore_pipeline = getattr(orig_pipe, 'restore_pipeline', None)
|
||||
new_pipe.task_args = getattr(orig_pipe, 'task_args', None)
|
||||
new_pipe.is_sdxl = getattr(orig_pipe, 'is_sdxl', False) # a1111 compatibility item
|
||||
new_pipe.is_sd2 = getattr(orig_pipe, 'is_sd2', False)
|
||||
new_pipe.is_sd1 = getattr(orig_pipe, 'is_sd1', True)
|
||||
add_noise_pred_to_diffusers_callback(new_pipe)
|
||||
if new_pipe.has_accelerate:
|
||||
set_accelerate(new_pipe)
|
||||
|
||||
|
||||
def backup_pipe_components(pipe):
|
||||
if pipe is None:
|
||||
return {}
|
||||
@@ -1157,13 +1185,18 @@ def unload_model_weights(op='model'):
|
||||
shared.log.debug(f'Unload {op}: {memory_stats()}')
|
||||
|
||||
|
||||
def hf_auth_check(checkpoint_info):
|
||||
def hf_auth_check(checkpoint_info, force:bool=False):
|
||||
login = None
|
||||
try:
|
||||
if (checkpoint_info.path.endswith('.safetensors') and os.path.isfile(checkpoint_info.path)) or (os.path.exists(checkpoint_info.path) and os.path.isdir(checkpoint_info.path) and os.path.isfile(os.path.join(checkpoint_info.path, 'model_index.json'))): # skip check for already downloaded models
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
if not force:
|
||||
try:
|
||||
# skip check for single-file safetensors models
|
||||
if (checkpoint_info.path.endswith('.safetensors') and os.path.isfile(checkpoint_info.path)):
|
||||
return True
|
||||
# skip check for local diffusers folders
|
||||
if (os.path.exists(checkpoint_info.path) and os.path.isdir(checkpoint_info.path) and os.path.isfile(os.path.join(checkpoint_info.path, 'model_index.json'))):
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
login = modelloader.hf_login()
|
||||
repo_id = path_to_repo(checkpoint_info)
|
||||
|
||||
+22
-10
@@ -203,18 +203,19 @@ class OffloadHook(accelerate.hooks.ModelHook):
|
||||
return module
|
||||
|
||||
def pre_forward(self, module, *args, **kwargs):
|
||||
if self.last_pre != id(module): # offload every other module first time when new module starts pre-forward
|
||||
self.last_pre = id(module)
|
||||
_id = id(module)
|
||||
if self.last_pre != _id and not hasattr(module, "offload_never"): # offload every other module first time when new module starts pre-forward
|
||||
self.last_pre = _id
|
||||
if shared.opts.diffusers_offload_pre:
|
||||
debug_move(f'Offload: type=balanced op=pre module={module.__class__.__name__}')
|
||||
for pipe in get_pipe_variants():
|
||||
for module_name in get_module_names(pipe):
|
||||
module_instance = getattr(pipe, module_name, None)
|
||||
module_cls = module_instance.__class__.__name__
|
||||
if (module_cls != module.__class__.__name__) and (module_cls not in self.offload_never) and (not devices.same_device(module_instance.device, devices.cpu)):
|
||||
if (_id != id(module_instance)) and (module_cls not in self.offload_never) and (not devices.same_device(module_instance.device, devices.cpu)):
|
||||
apply_balanced_offload_to_module(module_instance, op='pre')
|
||||
|
||||
if not devices.same_device(module.device, devices.device):
|
||||
if not devices.same_device(module.device, devices.device): # move-to-device
|
||||
device_index = torch.device(devices.device).index
|
||||
if device_index is None:
|
||||
device_index = 0
|
||||
@@ -233,6 +234,13 @@ class OffloadHook(accelerate.hooks.ModelHook):
|
||||
module._hf_hook.execution_device = torch.device(devices.device) # pylint: disable=protected-access
|
||||
module.balanced_offload_device_map = device_map
|
||||
module.balanced_offload_max_memory = max_memory
|
||||
|
||||
if debug:
|
||||
for pipe in get_pipe_variants():
|
||||
for module_name in get_module_names(pipe):
|
||||
module_instance = getattr(pipe, module_name, None)
|
||||
shared.log.trace(f'Offload: type=balanced op=pre check module={module_instance.__class__.__name__} device={module_instance.device} dtype={module_instance.dtype}')
|
||||
|
||||
return args, kwargs
|
||||
|
||||
def post_forward(self, module, output):
|
||||
@@ -292,7 +300,7 @@ def get_module_sizes(pipe=None, exclude=[]):
|
||||
return modules
|
||||
|
||||
|
||||
def move_module_to_cpu(module, op='unk'):
|
||||
def move_module_to_cpu(module, op='unk', force:bool=False):
|
||||
try:
|
||||
module_name = getattr(module, "module_name", module.__class__.__name__)
|
||||
module_size = offload_hook_instance.offload_map.get(module_name, offload_hook_instance.model_size())
|
||||
@@ -301,7 +309,11 @@ def move_module_to_cpu(module, op='unk'):
|
||||
prev_gpu = used_gpu
|
||||
module_cls = module.__class__.__name__
|
||||
op = f'{op}:skip'
|
||||
if module_cls in offload_hook_instance.offload_never:
|
||||
if force:
|
||||
op = f'{op}:force'
|
||||
module = module.to(devices.cpu)
|
||||
used_gpu -= module_size
|
||||
elif module_cls in offload_hook_instance.offload_never:
|
||||
op = f'{op}:never'
|
||||
elif module_cls in offload_hook_instance.offload_always:
|
||||
op = f'{op}:always'
|
||||
@@ -313,7 +325,7 @@ def move_module_to_cpu(module, op='unk'):
|
||||
used_gpu -= module_size
|
||||
if debug:
|
||||
quant = getattr(module, "quantization_method", None)
|
||||
debug_move(f'Offload: type=balanced op={op} gpu={prev_gpu:.3f}:{used_gpu:.3f} perc={perc_gpu:.2f} ram={used_ram:.3f} current={module.device} dtype={module.dtype} quant={quant} module={module_cls} size={module_size:.3f}')
|
||||
debug_move(f'Offload: type=balanced op={op} gpu={prev_gpu:.3f}:{used_gpu:.3f} perc={perc_gpu:.2f}:{shared.opts.diffusers_offload_min_gpu_memory} ram={used_ram:.3f} current={module.device} dtype={module.dtype} quant={quant} module={module_cls} size={module_size:.3f}')
|
||||
except Exception as e:
|
||||
if 'out of memory' in str(e):
|
||||
devices.torch_gc(fast=True, force=True, reason='oom')
|
||||
@@ -325,7 +337,7 @@ def move_module_to_cpu(module, op='unk'):
|
||||
errors.display(e, f'Offload: type=balanced op=apply module={getattr(module, "__name__", None)}')
|
||||
|
||||
|
||||
def apply_balanced_offload_to_module(module, op="apply"):
|
||||
def apply_balanced_offload_to_module(module, op="apply", force:bool=False):
|
||||
module_name = getattr(module, "module_name", module.__class__.__name__)
|
||||
network_layer_name = getattr(module, "network_layer_name", None)
|
||||
device_map = getattr(module, "balanced_offload_device_map", None)
|
||||
@@ -334,7 +346,7 @@ def apply_balanced_offload_to_module(module, op="apply"):
|
||||
module = accelerate.hooks.remove_hook_from_module(module, recurse=True)
|
||||
except Exception as e:
|
||||
shared.log.warning(f'Offload remove hook: module={module_name} {e}')
|
||||
move_module_to_cpu(module, op=op)
|
||||
move_module_to_cpu(module, op=op, force=force)
|
||||
try:
|
||||
module = accelerate.hooks.add_hook_to_module(module, offload_hook_instance, append=True)
|
||||
except Exception as e:
|
||||
@@ -345,7 +357,7 @@ def apply_balanced_offload_to_module(module, op="apply"):
|
||||
if device_map and max_memory:
|
||||
module.balanced_offload_device_map = device_map
|
||||
module.balanced_offload_max_memory = max_memory
|
||||
module.offload_post = shared.sd_model_type in offload_post and shared.opts.te_hijack and module_name.startswith("text_encoder")
|
||||
module.offload_post = shared.sd_model_type in offload_post and module_name.startswith("text_encoder")
|
||||
if shared.opts.layerwise_quantization or getattr(module, 'quantization_method', None) == 'LayerWise':
|
||||
model_quant.apply_layerwise(module, quiet=True) # need to reapply since hooks were removed/readded
|
||||
devices.torch_gc(fast=True, force=True, reason='offload')
|
||||
|
||||
@@ -48,11 +48,6 @@ def find_sampler_config(name):
|
||||
return config
|
||||
|
||||
|
||||
def visible_sampler_names():
|
||||
visible_samplers = [x for x in all_samplers if x.name in shared.opts.show_samplers] if len(shared.opts.show_samplers) > 0 else all_samplers
|
||||
return visible_samplers
|
||||
|
||||
|
||||
def restore_default(model):
|
||||
if model is None:
|
||||
return None
|
||||
@@ -131,7 +126,7 @@ def create_sampler(name, model):
|
||||
def set_samplers():
|
||||
global samplers # pylint: disable=global-statement
|
||||
global samplers_for_img2img # pylint: disable=global-statement
|
||||
samplers = visible_sampler_names()
|
||||
samplers = all_samplers
|
||||
# samplers_for_img2img = [x for x in samplers if x.name != "PLMS"]
|
||||
samplers_for_img2img = samplers
|
||||
samplers_map.clear()
|
||||
|
||||
@@ -18,6 +18,9 @@ vae_scale_override = {
|
||||
|
||||
|
||||
def get_vae_scale_factor(model=None):
|
||||
if not shared.sd_loaded:
|
||||
vae_scale_factor = 8
|
||||
return vae_scale_factor
|
||||
patch_size = 1
|
||||
if model is None:
|
||||
model = shared.sd_model
|
||||
|
||||
+32
-13
@@ -11,7 +11,7 @@ from diffusers.quantizers.quantization_config import QuantizationConfigMixin
|
||||
from diffusers.utils import get_module_from_name
|
||||
from modules import devices, shared
|
||||
|
||||
from .common import dtype_dict, use_tensorwise_fp8_matmul, quantized_matmul_dtypes, allowed_types, conv_types, conv_transpose_types
|
||||
from .common import dtype_dict, use_tensorwise_fp8_matmul, allowed_types, conv_types, conv_transpose_types
|
||||
from .dequantizer import dequantizer_dict
|
||||
from .forward import get_forward_func
|
||||
|
||||
@@ -50,6 +50,7 @@ def quantize_weight(weight: torch.FloatTensor, reduction_axes: Union[int, List[i
|
||||
def sdnq_quantize_layer(layer, weights_dtype="int8", torch_dtype=None, group_size=0, quant_conv=False, use_quantized_matmul=False, use_quantized_matmul_conv=False, dequantize_fp32=False, non_blocking=False, quantization_device=None, return_device=None, param_name=None): # pylint: disable=unused-argument
|
||||
layer_class_name = layer.__class__.__name__
|
||||
if layer_class_name in allowed_types:
|
||||
num_of_groups = 1
|
||||
is_conv_type = False
|
||||
is_conv_transpose_type = False
|
||||
is_linear_type = False
|
||||
@@ -69,12 +70,9 @@ def sdnq_quantize_layer(layer, weights_dtype="int8", torch_dtype=None, group_siz
|
||||
group_channel_size = channel_size // layer.groups
|
||||
use_quantized_matmul = False
|
||||
if use_quantized_matmul_conv:
|
||||
use_quantized_matmul = weights_dtype in quantized_matmul_dtypes and group_channel_size >= 32 and output_channel_size >= 32
|
||||
use_quantized_matmul = group_channel_size >= 32 and output_channel_size >= 32
|
||||
if use_quantized_matmul and not dtype_dict[weights_dtype]["is_integer"]:
|
||||
use_quantized_matmul = output_channel_size % 16 == 0 and group_channel_size % 16 == 0
|
||||
if use_quantized_matmul:
|
||||
result_shape = layer.weight.shape
|
||||
layer.weight.data = layer.weight.reshape(output_channel_size, -1)
|
||||
elif layer_class_name in conv_transpose_types:
|
||||
if not quant_conv:
|
||||
return layer
|
||||
@@ -90,9 +88,9 @@ def sdnq_quantize_layer(layer, weights_dtype="int8", torch_dtype=None, group_siz
|
||||
try:
|
||||
output_channel_size, channel_size = layer.weight.shape
|
||||
except Exception as e:
|
||||
raise ValueError(f"SDNQ: layer_class_name={layer_class_name} layer_weight_shape={layer.weight.shape} weights_dtype={weights_dtype} unsupported") from e
|
||||
raise ValueError(f"SDNQ: param_name={param_name} layer_class_name={layer_class_name} layer_weight_shape={layer.weight.shape} weights_dtype={weights_dtype} unsupported") from e
|
||||
if use_quantized_matmul:
|
||||
use_quantized_matmul = weights_dtype in quantized_matmul_dtypes and channel_size >= 32 and output_channel_size >= 32
|
||||
use_quantized_matmul = channel_size >= 32 and output_channel_size >= 32
|
||||
if use_quantized_matmul:
|
||||
if dtype_dict[weights_dtype]["is_integer"]:
|
||||
use_quantized_matmul = output_channel_size % 8 == 0 and channel_size % 8 == 0
|
||||
@@ -100,14 +98,18 @@ def sdnq_quantize_layer(layer, weights_dtype="int8", torch_dtype=None, group_siz
|
||||
use_quantized_matmul = output_channel_size % 16 == 0 and channel_size % 16 == 0
|
||||
|
||||
if group_size == 0:
|
||||
if is_linear_type:
|
||||
if use_quantized_matmul and dtype_dict[weights_dtype]["num_bits"] >= 6:
|
||||
group_size = -1
|
||||
elif is_linear_type:
|
||||
group_size = 2 ** (2 + dtype_dict[weights_dtype]["num_bits"])
|
||||
else:
|
||||
group_size = 2 ** (1 + dtype_dict[weights_dtype]["num_bits"])
|
||||
elif use_quantized_matmul and dtype_dict[weights_dtype]["num_bits"] == 8:
|
||||
group_size = -1 # override user value, re-quantizing 8bit into 8bit is pointless
|
||||
elif group_size != -1 and not is_linear_type:
|
||||
group_size = max(group_size // 2, 1)
|
||||
|
||||
if not use_quantized_matmul and group_size > 0:
|
||||
if group_size > 0:
|
||||
if group_size >= channel_size:
|
||||
group_size = channel_size
|
||||
num_of_groups = 1
|
||||
@@ -159,12 +161,16 @@ def sdnq_quantize_layer(layer, weights_dtype="int8", torch_dtype=None, group_siz
|
||||
if zero_point is not None:
|
||||
zero_point = zero_point.to(torch_dtype)
|
||||
|
||||
if use_quantized_matmul:
|
||||
re_quantize_for_matmul = (num_of_groups > 1 or zero_point is not None)
|
||||
if use_quantized_matmul and not re_quantize_for_matmul:
|
||||
if is_conv_type:
|
||||
result_shape = layer.weight.shape
|
||||
layer.weight.data = layer.weight.reshape(output_channel_size, -1)
|
||||
scale.transpose_(0,1)
|
||||
layer.weight.transpose_(0,1)
|
||||
if not dtype_dict[weights_dtype]["is_integer"]:
|
||||
stride = layer.weight.stride()
|
||||
if stride[0] > stride[1] and stride[1] == 1:
|
||||
weight_stride = layer.weight.stride()
|
||||
if not (weight_stride[0] == 1 and weight_stride[1] > 1):
|
||||
layer.weight.data = layer.weight.t().contiguous().t()
|
||||
if not use_tensorwise_fp8_matmul:
|
||||
scale = scale.to(torch.float32)
|
||||
@@ -178,6 +184,7 @@ def sdnq_quantize_layer(layer, weights_dtype="int8", torch_dtype=None, group_siz
|
||||
original_shape=original_shape,
|
||||
weights_dtype=weights_dtype,
|
||||
use_quantized_matmul=use_quantized_matmul,
|
||||
re_quantize_for_matmul=re_quantize_for_matmul,
|
||||
)
|
||||
layer.weight.data = layer.sdnq_dequantizer.pack_weight(layer.weight).to(return_device, non_blocking=non_blocking)
|
||||
layer.sdnq_dequantizer = layer.sdnq_dequantizer.to(return_device, non_blocking=non_blocking)
|
||||
@@ -392,9 +399,15 @@ class SDNQQuantizer(DiffusersQuantizer):
|
||||
devices.torch_gc(force=True, reason='sdnq')
|
||||
return model
|
||||
|
||||
def get_cuda_warm_up_factor(self):
|
||||
def get_accelerator_warm_up_factor(self):
|
||||
return 32 // dtype_dict[self.quantization_config.weights_dtype]["num_bits"]
|
||||
|
||||
def get_cuda_warm_up_factor(self):
|
||||
"""
|
||||
needed for transformers compatibilty, returns self.get_accelerator_warm_up_factor
|
||||
"""
|
||||
return self.get_accelerator_warm_up_factor()
|
||||
|
||||
def update_tp_plan(self, config):
|
||||
"""
|
||||
needed for transformers compatibilty, no-op function
|
||||
@@ -425,6 +438,12 @@ class SDNQQuantizer(DiffusersQuantizer):
|
||||
"""
|
||||
return param_name
|
||||
|
||||
def update_dtype(self, dtype: torch.dtype) -> torch.dtype:
|
||||
"""
|
||||
needed for transformers compatibilty, no-op function
|
||||
"""
|
||||
return dtype
|
||||
|
||||
@property
|
||||
def is_trainable(self):
|
||||
return False
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import os
|
||||
import torch
|
||||
from modules import devices, shared
|
||||
from modules import shared
|
||||
|
||||
torch_version = float(torch.__version__[:3])
|
||||
|
||||
@@ -34,10 +34,6 @@ if hasattr(torch, "float8_e5m2fnuz"):
|
||||
use_torch_compile = shared.opts.sdnq_dequantize_compile # this setting requires a full restart of the webui to apply
|
||||
use_tensorwise_fp8_matmul = os.environ.get('SDNQ_USE_TENSORWISE_FP8_MATMUL', "1").lower() not in {"0", "false", "no"} # row-wise FP8 only exist on H100 hardware, sdnq will use software row-wise with tensorwise hardware with this setting
|
||||
|
||||
quantized_matmul_dtypes = ("int8", "int7", "int6", "int5", "int4", "int3", "int2", "float8_e4m3fn", "float8_e5m2")
|
||||
if devices.backend in {"cpu", "openvino"}:
|
||||
quantized_matmul_dtypes += ("float8_e4m3fnuz", "float8_e5m2fnuz")
|
||||
|
||||
linear_types = ("Linear",)
|
||||
conv_types = ("Conv1d", "Conv2d", "Conv3d")
|
||||
conv_transpose_types = ("ConvTranspose1d", "ConvTranspose2d", "ConvTranspose3d")
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# pylint: disable=redefined-builtin,no-member,protected-access
|
||||
|
||||
from typing import Tuple
|
||||
|
||||
import torch
|
||||
|
||||
from .common import dtype_dict, use_torch_compile
|
||||
@@ -34,6 +36,34 @@ def dequantize_packed_int_symmetric(weight: torch.ByteTensor, scale: torch.Float
|
||||
return dequantize_symmetric(unpack_int_symetric(weight, shape, weights_dtype, dtype=scale.dtype), scale, dtype, result_shape, skip_quantized_matmul=skip_quantized_matmul)
|
||||
|
||||
|
||||
def quantize_int8(input: torch.FloatTensor, dim: int = -1) -> Tuple[torch.CharTensor, torch.FloatTensor]:
|
||||
scale = torch.amax(input.abs(), dim=dim, keepdims=True).div_(127)
|
||||
input = torch.div(input, scale).round_().clamp_(-128, 127).to(dtype=torch.int8)
|
||||
return input, scale
|
||||
|
||||
|
||||
def re_quantize_matmul_asymmetric(weight: torch.ByteTensor, scale: torch.FloatTensor, zero_point: torch.FloatTensor, result_shape: torch.Size) -> Tuple[torch.CharTensor, torch.FloatTensor]:
|
||||
result = dequantize_asymmetric(weight, scale, zero_point, scale.dtype, result_shape)
|
||||
if result.ndim > 2: # convs
|
||||
result = result.flatten(1,-1)
|
||||
return quantize_int8(result.t_(), dim=0)
|
||||
|
||||
|
||||
def re_quantize_matmul_symmetric(weight: torch.CharTensor, scale: torch.FloatTensor, result_shape: torch.Size) -> Tuple[torch.CharTensor, torch.FloatTensor]:
|
||||
result = dequantize_symmetric(weight, scale, scale.dtype, result_shape)
|
||||
if result.ndim > 2: # convs
|
||||
result = result.flatten(1,-1)
|
||||
return quantize_int8(result.t_(), dim=0)
|
||||
|
||||
|
||||
def re_quantize_matmul_packed_int_asymmetric(weight: torch.ByteTensor, scale: torch.FloatTensor, zero_point: torch.FloatTensor, shape: torch.Size, result_shape: torch.Size, weights_dtype: str) -> torch.FloatTensor:
|
||||
return re_quantize_matmul_asymmetric(unpack_int_asymetric(weight, shape, weights_dtype), scale, zero_point, result_shape)
|
||||
|
||||
|
||||
def re_quantize_matmul_packed_int_symmetric(weight: torch.ByteTensor, scale: torch.FloatTensor, shape: torch.Size, result_shape: torch.Size, weights_dtype: str) -> torch.FloatTensor:
|
||||
return re_quantize_matmul_symmetric(unpack_int_symetric(weight, shape, weights_dtype, dtype=scale.dtype), scale, result_shape)
|
||||
|
||||
|
||||
class AsymmetricWeightsDequantizer(torch.nn.Module):
|
||||
def __init__(
|
||||
self,
|
||||
@@ -43,12 +73,14 @@ class AsymmetricWeightsDequantizer(torch.nn.Module):
|
||||
result_shape: torch.Size,
|
||||
original_shape: torch.Size,
|
||||
weights_dtype: str,
|
||||
use_quantized_matmul: bool = False,
|
||||
**kwargs, # pylint: disable=unused-argument
|
||||
):
|
||||
super().__init__()
|
||||
self.weights_dtype = weights_dtype
|
||||
self.original_shape = original_shape
|
||||
self.use_quantized_matmul = False
|
||||
self.use_quantized_matmul = use_quantized_matmul
|
||||
self.re_quantize_for_matmul = True
|
||||
self.result_dtype = result_dtype
|
||||
self.result_shape = result_shape
|
||||
self.register_buffer("scale", scale)
|
||||
@@ -57,6 +89,9 @@ class AsymmetricWeightsDequantizer(torch.nn.Module):
|
||||
def pack_weight(self, weight: torch.Tensor) -> torch.Tensor:
|
||||
return weight.to(dtype=dtype_dict[self.weights_dtype]["torch_dtype"])
|
||||
|
||||
def re_quantize_matmul(self, weight, **kwargs): # pylint: disable=unused-argument
|
||||
return re_quantize_matmul_asymmetric_compiled(weight, self.scale, self.zero_point, self.result_shape)
|
||||
|
||||
def forward(self, weight, **kwargs): # pylint: disable=unused-argument
|
||||
return dequantize_asymmetric_compiled(weight, self.scale, self.zero_point, self.result_dtype, self.result_shape)
|
||||
|
||||
@@ -70,12 +105,14 @@ class SymmetricWeightsDequantizer(torch.nn.Module):
|
||||
original_shape: torch.Size,
|
||||
weights_dtype: str,
|
||||
use_quantized_matmul: bool = False,
|
||||
re_quantize_for_matmul: bool = False,
|
||||
**kwargs, # pylint: disable=unused-argument
|
||||
):
|
||||
super().__init__()
|
||||
self.weights_dtype = weights_dtype
|
||||
self.original_shape = original_shape
|
||||
self.use_quantized_matmul = use_quantized_matmul
|
||||
self.re_quantize_for_matmul = re_quantize_for_matmul
|
||||
self.result_dtype = result_dtype
|
||||
self.result_shape = result_shape
|
||||
self.register_buffer("scale", scale)
|
||||
@@ -83,7 +120,11 @@ class SymmetricWeightsDequantizer(torch.nn.Module):
|
||||
def pack_weight(self, weight: torch.Tensor) -> torch.Tensor:
|
||||
return weight.to(dtype=dtype_dict[self.weights_dtype]["torch_dtype"])
|
||||
|
||||
def re_quantize_matmul(self, weight, **kwargs): # pylint: disable=unused-argument
|
||||
return re_quantize_matmul_symmetric_compiled(weight, self.scale, self.result_shape)
|
||||
|
||||
def forward(self, weight, skip_quantized_matmul=False, **kwargs): # pylint: disable=unused-argument
|
||||
skip_quantized_matmul = skip_quantized_matmul and not self.re_quantize_for_matmul
|
||||
return dequantize_symmetric_compiled(weight, self.scale, self.result_dtype, self.result_shape, skip_quantized_matmul=skip_quantized_matmul)
|
||||
|
||||
|
||||
@@ -97,11 +138,13 @@ class PackedINTAsymmetricWeightsDequantizer(torch.nn.Module):
|
||||
result_shape: torch.Size,
|
||||
original_shape: torch.Size,
|
||||
weights_dtype: str,
|
||||
use_quantized_matmul: bool = False,
|
||||
**kwargs, # pylint: disable=unused-argument
|
||||
):
|
||||
super().__init__()
|
||||
self.weights_dtype = weights_dtype
|
||||
self.use_quantized_matmul = False
|
||||
self.use_quantized_matmul = use_quantized_matmul
|
||||
self.re_quantize_for_matmul = True
|
||||
self.original_shape = original_shape
|
||||
self.quantized_weight_shape = quantized_weight_shape
|
||||
self.result_dtype = result_dtype
|
||||
@@ -112,6 +155,9 @@ class PackedINTAsymmetricWeightsDequantizer(torch.nn.Module):
|
||||
def pack_weight(self, weight: torch.Tensor) -> torch.Tensor:
|
||||
return pack_int_asymetric(weight, self.weights_dtype)
|
||||
|
||||
def re_quantize_matmul(self, weight, **kwargs): # pylint: disable=unused-argument
|
||||
return re_quantize_matmul_packed_int_asymmetric_compiled(weight, self.scale, self.zero_point, self.quantized_weight_shape, self.result_shape, self.weights_dtype)
|
||||
|
||||
def forward(self, weight, **kwargs): # pylint: disable=unused-argument
|
||||
return dequantize_packed_int_asymmetric_compiled(weight, self.scale, self.zero_point, self.quantized_weight_shape, self.result_dtype, self.result_shape, self.weights_dtype)
|
||||
|
||||
@@ -126,12 +172,14 @@ class PackedINTSymmetricWeightsDequantizer(torch.nn.Module):
|
||||
original_shape: torch.Size,
|
||||
weights_dtype: str,
|
||||
use_quantized_matmul: bool = False,
|
||||
re_quantize_for_matmul: bool = False,
|
||||
**kwargs, # pylint: disable=unused-argument
|
||||
):
|
||||
super().__init__()
|
||||
self.weights_dtype = weights_dtype
|
||||
self.original_shape = original_shape
|
||||
self.use_quantized_matmul = use_quantized_matmul
|
||||
self.re_quantize_for_matmul = re_quantize_for_matmul
|
||||
self.quantized_weight_shape = quantized_weight_shape
|
||||
self.result_dtype = result_dtype
|
||||
self.result_shape = result_shape
|
||||
@@ -140,7 +188,11 @@ class PackedINTSymmetricWeightsDequantizer(torch.nn.Module):
|
||||
def pack_weight(self, weight: torch.Tensor) -> torch.Tensor:
|
||||
return pack_int_symetric(weight, self.weights_dtype)
|
||||
|
||||
def re_quantize_matmul(self, weight, **kwargs): # pylint: disable=unused-argument
|
||||
return re_quantize_matmul_packed_int_symmetric_compiled(weight, self.scale, self.quantized_weight_shape, self.result_shape, self.weights_dtype)
|
||||
|
||||
def forward(self, weight, skip_quantized_matmul=False, **kwargs): # pylint: disable=unused-argument
|
||||
skip_quantized_matmul = skip_quantized_matmul and not self.re_quantize_for_matmul
|
||||
return dequantize_packed_int_symmetric_compiled(weight, self.scale, self.quantized_weight_shape, self.result_dtype, self.result_shape, self.weights_dtype, skip_quantized_matmul=skip_quantized_matmul)
|
||||
|
||||
|
||||
@@ -159,8 +211,8 @@ dequantizer_dict = {
|
||||
"uint4": PackedINTAsymmetricWeightsDequantizer,
|
||||
"uint3": PackedINTAsymmetricWeightsDequantizer,
|
||||
"uint2": PackedINTAsymmetricWeightsDequantizer,
|
||||
"uint1": AsymmetricWeightsDequantizer,
|
||||
"bool": AsymmetricWeightsDequantizer,
|
||||
"uint1": PackedINTAsymmetricWeightsDequantizer,
|
||||
"bool": PackedINTAsymmetricWeightsDequantizer,
|
||||
"float8_e4m3fn": SymmetricWeightsDequantizer,
|
||||
"float8_e4m3fnuz": SymmetricWeightsDequantizer,
|
||||
"float8_e5m2": SymmetricWeightsDequantizer,
|
||||
@@ -173,8 +225,16 @@ if use_torch_compile:
|
||||
dequantize_symmetric_compiled = torch.compile(dequantize_symmetric, fullgraph=True, dynamic=False)
|
||||
dequantize_packed_int_asymmetric_compiled = torch.compile(dequantize_packed_int_asymmetric, fullgraph=True, dynamic=False)
|
||||
dequantize_packed_int_symmetric_compiled = torch.compile(dequantize_packed_int_symmetric, fullgraph=True, dynamic=False)
|
||||
re_quantize_matmul_asymmetric_compiled = torch.compile(re_quantize_matmul_asymmetric, fullgraph=True, dynamic=False)
|
||||
re_quantize_matmul_symmetric_compiled = torch.compile(re_quantize_matmul_symmetric, fullgraph=True, dynamic=False)
|
||||
re_quantize_matmul_packed_int_asymmetric_compiled = torch.compile(re_quantize_matmul_packed_int_asymmetric, fullgraph=True, dynamic=False)
|
||||
re_quantize_matmul_packed_int_symmetric_compiled = torch.compile(re_quantize_matmul_packed_int_symmetric, fullgraph=True, dynamic=False)
|
||||
else:
|
||||
dequantize_asymmetric_compiled = dequantize_asymmetric
|
||||
dequantize_symmetric_compiled = dequantize_symmetric
|
||||
dequantize_packed_int_asymmetric_compiled = dequantize_packed_int_asymmetric
|
||||
dequantize_packed_int_symmetric_compiled = dequantize_packed_int_symmetric
|
||||
re_quantize_matmul_asymmetric_compiled = re_quantize_matmul_asymmetric
|
||||
re_quantize_matmul_symmetric_compiled = re_quantize_matmul_symmetric
|
||||
re_quantize_matmul_packed_int_asymmetric_compiled = re_quantize_matmul_packed_int_asymmetric
|
||||
re_quantize_matmul_packed_int_symmetric_compiled = re_quantize_matmul_packed_int_symmetric
|
||||
|
||||
@@ -25,7 +25,9 @@ def conv_fp8_matmul(
|
||||
input, input_scale = quantize_fp8_matmul_input(input)
|
||||
|
||||
if groups == 1:
|
||||
result = torch._scaled_mm(input, weight, scale_a=input_scale, scale_b=scale, bias=bias, out_dtype=return_dtype).view(mm_output_shape)
|
||||
if bias is not None and bias.dtype != torch.bfloat16:
|
||||
bias = bias.to(dtype=torch.bfloat16)
|
||||
result = torch._scaled_mm(input, weight, scale_a=input_scale, scale_b=scale, bias=bias, out_dtype=torch.bfloat16).view(mm_output_shape).to(return_dtype)
|
||||
else:
|
||||
scale = scale.view(groups, 1, scale.shape[1] // groups)
|
||||
input_scale = input_scale.view(groups, input_scale.shape[0] // groups, 1)
|
||||
@@ -34,12 +36,14 @@ def conv_fp8_matmul(
|
||||
result = []
|
||||
if bias is not None:
|
||||
bias = bias.view(groups, bias.shape[0] // groups)
|
||||
if bias.dtype != torch.bfloat16:
|
||||
bias = bias.to(dtype=torch.bfloat16)
|
||||
for i in range(groups):
|
||||
result.append(torch._scaled_mm(input[:, i], weight[:, i], scale_a=input_scale[i], scale_b=scale[i], bias=bias[i], out_dtype=return_dtype))
|
||||
result.append(torch._scaled_mm(input[:, i], weight[:, i], scale_a=input_scale[i], scale_b=scale[i], bias=bias[i], out_dtype=torch.bfloat16))
|
||||
else:
|
||||
for i in range(groups):
|
||||
result.append(torch._scaled_mm(input[:, i], weight[:, i], scale_a=input_scale[i], scale_b=scale[i], bias=None, out_dtype=return_dtype))
|
||||
result = torch.cat(result, dim=-1).view(mm_output_shape)
|
||||
result.append(torch._scaled_mm(input[:, i], weight[:, i], scale_a=input_scale[i], scale_b=scale[i], bias=None, out_dtype=torch.bfloat16))
|
||||
result = torch.cat(result, dim=-1).view(mm_output_shape).to(return_dtype)
|
||||
|
||||
if conv_type == 1:
|
||||
result = result.transpose_(1,2)
|
||||
|
||||
@@ -16,8 +16,8 @@ def conv_int8_matmul(
|
||||
weight: torch.CharTensor,
|
||||
bias: torch.FloatTensor,
|
||||
scale: torch.FloatTensor,
|
||||
result_shape: torch.Size,
|
||||
quantized_weight_shape: torch.Size,
|
||||
result_shape: torch.Size,
|
||||
weights_dtype: str,
|
||||
reversed_padding_repeated_twice: List[int],
|
||||
padding_mode: str, conv_type: int,
|
||||
@@ -57,11 +57,17 @@ def quantized_conv_forward_int8_matmul(self, input) -> torch.FloatTensor:
|
||||
if torch.numel(input) / input.shape[2] < 32:
|
||||
return self._conv_forward(input, self.sdnq_dequantizer(self.weight, skip_quantized_matmul=True), self.bias)
|
||||
conv_type, stride, padding, dilation = get_conv_args(input.ndim, self.stride, self.padding, self.dilation)
|
||||
if self.sdnq_dequantizer.re_quantize_for_matmul:
|
||||
weight, scale = self.sdnq_dequantizer.re_quantize_matmul(self.weight)
|
||||
quantized_weight_shape = None
|
||||
else:
|
||||
weight = self.weight
|
||||
scale = self.sdnq_dequantizer.scale
|
||||
quantized_weight_shape = getattr(self.sdnq_dequantizer, "quantized_weight_shape", None)
|
||||
return conv_int8_matmul(
|
||||
input, self.weight, self.bias,
|
||||
self.sdnq_dequantizer.scale,
|
||||
input, weight, self.bias,
|
||||
scale, quantized_weight_shape,
|
||||
self.sdnq_dequantizer.result_shape,
|
||||
getattr(self.sdnq_dequantizer, "quantized_weight_shape", None),
|
||||
self.sdnq_dequantizer.weights_dtype,
|
||||
self._reversed_padding_repeated_twice,
|
||||
self.padding_mode, conv_type,
|
||||
|
||||
@@ -8,7 +8,7 @@ from ...common import use_torch_compile # noqa: TID252
|
||||
|
||||
|
||||
def quantize_fp8_matmul_input(input: torch.FloatTensor) -> Tuple[torch.Tensor, torch.FloatTensor]:
|
||||
input = input.flatten(0,-2).contiguous().to(dtype=torch.float32)
|
||||
input = input.flatten(0,-2).to(dtype=torch.float32)
|
||||
input_scale = torch.amax(input.abs(), dim=-1, keepdims=True).div_(448)
|
||||
input = torch.div(input, input_scale).clamp_(-448, 448).to(dtype=torch.float8_e4m3fn)
|
||||
return input, input_scale
|
||||
@@ -23,7 +23,9 @@ def fp8_matmul(
|
||||
return_dtype = input.dtype
|
||||
output_shape = (*input.shape[:-1], weight.shape[-1])
|
||||
input, input_scale = quantize_fp8_matmul_input(input)
|
||||
return torch._scaled_mm(input, weight, scale_a=input_scale, scale_b=scale, bias=bias, out_dtype=return_dtype).view(output_shape)
|
||||
if bias is not None and bias.dtype != torch.bfloat16:
|
||||
bias = bias.to(dtype=torch.bfloat16)
|
||||
return torch._scaled_mm(input, weight, scale_a=input_scale, scale_b=scale, bias=bias, out_dtype=torch.bfloat16).view(output_shape).to(return_dtype)
|
||||
|
||||
|
||||
def quantized_linear_forward_fp8_matmul(self, input: torch.FloatTensor) -> torch.FloatTensor:
|
||||
|
||||
@@ -9,7 +9,7 @@ from ...dequantizer import dequantize_symmetric, dequantize_symmetric_with_bias
|
||||
|
||||
|
||||
def quantize_fp8_matmul_input_tensorwise(input: torch.FloatTensor, scale: torch.FloatTensor) -> Tuple[torch.Tensor, torch.FloatTensor]:
|
||||
input = input.flatten(0,-2).contiguous().to(dtype=scale.dtype)
|
||||
input = input.flatten(0,-2).to(dtype=scale.dtype)
|
||||
input_scale = torch.amax(input.abs(), dim=-1, keepdims=True).div_(448)
|
||||
input = torch.div(input, input_scale).clamp_(-448, 448).to(dtype=torch.float8_e4m3fn)
|
||||
scale = torch.mul(input_scale, scale)
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
# pylint: disable=relative-beyond-top-level,redefined-builtin,protected-access
|
||||
|
||||
from typing import Tuple
|
||||
|
||||
import torch
|
||||
|
||||
from ...common import use_torch_compile # noqa: TID252
|
||||
from ...packed_int import unpack_int_symetric # noqa: TID252
|
||||
from ...dequantizer import dequantize_symmetric, dequantize_symmetric_with_bias # noqa: TID252
|
||||
from ...dequantizer import quantize_int8, dequantize_symmetric, dequantize_symmetric_with_bias # noqa: TID252
|
||||
|
||||
|
||||
def quantize_int8_matmul_input(input: torch.FloatTensor, scale: torch.FloatTensor) -> Tuple[torch.CharTensor, torch.FloatTensor]:
|
||||
input = input.flatten(0,-2).contiguous().to(dtype=scale.dtype)
|
||||
input_scale = torch.amax(input.abs(), dim=-1, keepdims=True).div_(127)
|
||||
input = torch.div(input, input_scale).round_().clamp_(-128, 127).to(dtype=torch.int8)
|
||||
input = input.flatten(0,-2).to(dtype=scale.dtype)
|
||||
input, input_scale = quantize_int8(input, dim=-1)
|
||||
scale = torch.mul(input_scale, scale)
|
||||
if scale.dtype == torch.float16: # fp16 will overflow
|
||||
scale = scale.to(dtype=torch.float32)
|
||||
@@ -41,7 +39,14 @@ def int8_matmul(
|
||||
def quantized_linear_forward_int8_matmul(self, input: torch.FloatTensor) -> torch.FloatTensor:
|
||||
if torch.numel(input) / input.shape[-1] < 32:
|
||||
return torch.nn.functional.linear(input, self.sdnq_dequantizer(self.weight, skip_quantized_matmul=True), self.bias)
|
||||
return int8_matmul(input, self.weight, self.bias, self.sdnq_dequantizer.scale, getattr(self.sdnq_dequantizer, "quantized_weight_shape", None), self.sdnq_dequantizer.weights_dtype)
|
||||
if self.sdnq_dequantizer.re_quantize_for_matmul:
|
||||
weight, scale = self.sdnq_dequantizer.re_quantize_matmul(self.weight)
|
||||
quantized_weight_shape = None
|
||||
else:
|
||||
weight = self.weight
|
||||
scale = self.sdnq_dequantizer.scale
|
||||
quantized_weight_shape = getattr(self.sdnq_dequantizer, "quantized_weight_shape", None)
|
||||
return int8_matmul(input, weight, self.bias, scale, quantized_weight_shape, self.sdnq_dequantizer.weights_dtype)
|
||||
|
||||
|
||||
if use_torch_compile:
|
||||
|
||||
@@ -122,6 +122,21 @@ def pack_uint2(tensor: torch.ByteTensor) -> torch.ByteTensor:
|
||||
return packed_tensor
|
||||
|
||||
|
||||
def pack_uint1(tensor: torch.Tensor) -> torch.Tensor:
|
||||
packed_tensor = tensor.contiguous().reshape(-1, 8)
|
||||
packed_tensor = torch.bitwise_or(
|
||||
torch.bitwise_or(
|
||||
torch.bitwise_or(packed_tensor[:, 0], torch.bitwise_left_shift(packed_tensor[:, 1], 1)),
|
||||
torch.bitwise_or(torch.bitwise_left_shift(packed_tensor[:, 2], 2), torch.bitwise_left_shift(packed_tensor[:, 3], 3))
|
||||
),
|
||||
torch.bitwise_or(
|
||||
torch.bitwise_or(torch.bitwise_left_shift(packed_tensor[:, 4], 4), torch.bitwise_left_shift(packed_tensor[:, 5], 5)),
|
||||
torch.bitwise_or(torch.bitwise_left_shift(packed_tensor[:, 6], 6), torch.bitwise_left_shift(packed_tensor[:, 7], 7))
|
||||
),
|
||||
)
|
||||
return packed_tensor
|
||||
|
||||
|
||||
def unpack_uint7(packed_tensor: torch.ByteTensor, shape: torch.Size) -> torch.ByteTensor:
|
||||
result = torch.stack(
|
||||
(
|
||||
@@ -246,6 +261,23 @@ def unpack_uint2(packed_tensor: torch.ByteTensor, shape: torch.Size) -> torch.By
|
||||
return result
|
||||
|
||||
|
||||
def unpack_uint1(packed_tensor: torch.Tensor, shape: torch.Size) -> torch.Tensor:
|
||||
result = torch.stack(
|
||||
(
|
||||
torch.bitwise_and(packed_tensor, 1),
|
||||
torch.bitwise_and(torch.bitwise_right_shift(packed_tensor, 1), 1),
|
||||
torch.bitwise_and(torch.bitwise_right_shift(packed_tensor, 2), 1),
|
||||
torch.bitwise_and(torch.bitwise_right_shift(packed_tensor, 3), 1),
|
||||
torch.bitwise_and(torch.bitwise_right_shift(packed_tensor, 4), 1),
|
||||
torch.bitwise_and(torch.bitwise_right_shift(packed_tensor, 5), 1),
|
||||
torch.bitwise_and(torch.bitwise_right_shift(packed_tensor, 6), 1),
|
||||
torch.bitwise_right_shift(packed_tensor, 7),
|
||||
),
|
||||
dim=-1
|
||||
).reshape(shape)
|
||||
return result
|
||||
|
||||
|
||||
packed_int_function_dict = {
|
||||
"int7": {"pack": pack_uint7, "unpack": unpack_uint7},
|
||||
"int6": {"pack": pack_uint6, "unpack": unpack_uint6},
|
||||
@@ -259,4 +291,6 @@ packed_int_function_dict = {
|
||||
"uint4": {"pack": pack_uint4, "unpack": unpack_uint4},
|
||||
"uint3": {"pack": pack_uint3, "unpack": unpack_uint3},
|
||||
"uint2": {"pack": pack_uint2, "unpack": unpack_uint2},
|
||||
"uint1": {"pack": pack_uint1, "unpack": unpack_uint1},
|
||||
"bool": {"pack": pack_uint1, "unpack": unpack_uint1},
|
||||
}
|
||||
|
||||
+7
-9
@@ -160,14 +160,14 @@ options_templates.update(options_section(('model_options', "Model Options"), {
|
||||
"model_h1_sep": OptionInfo("<h2>HiDream</h2>", "", gr.HTML),
|
||||
"model_h1_llama_repo": OptionInfo("Default", "LLama repo", gr.Textbox),
|
||||
"model_wan_sep": OptionInfo("<h2>WanAI</h2>", "", gr.HTML),
|
||||
"model_wan_stage": OptionInfo("first", "Processing stage", gr.Radio, {"choices": ['high noise', 'low noise', 'combined'] }),
|
||||
"model_wan_stage": OptionInfo("low noise", "Processing stage", gr.Radio, {"choices": ['high noise', 'low noise', 'combined'] }),
|
||||
"model_wan_boundary": OptionInfo(0.85, "Stage boundary ratio", gr.Slider, {"minimum": 0, "maximum": 1.0, "step": 0.05 }),
|
||||
}))
|
||||
|
||||
options_templates.update(options_section(('offload', "Model Offloading"), {
|
||||
"offload_sep": OptionInfo("<h2>Model Offloading</h2>", "", gr.HTML),
|
||||
"diffusers_offload_mode": OptionInfo(startup_offload_mode, "Model offload mode", gr.Radio, {"choices": ['none', 'balanced', 'group', 'model', 'sequential']}),
|
||||
"diffusers_offload_pre": OptionInfo(False, "Offload during pre-forward"),
|
||||
"diffusers_offload_pre": OptionInfo(True, "Offload during pre-forward"),
|
||||
"diffusers_offload_nonblocking": OptionInfo(False, "Non-blocking move operations"),
|
||||
"diffusers_offload_min_gpu_memory": OptionInfo(startup_offload_min_gpu, "Balanced offload GPU low watermark", gr.Slider, {"minimum": 0, "maximum": 1, "step": 0.01 }),
|
||||
"diffusers_offload_max_gpu_memory": OptionInfo(startup_offload_max_gpu, "Balanced offload GPU high watermark", gr.Slider, {"minimum": 0.1, "maximum": 1, "step": 0.01 }),
|
||||
@@ -251,7 +251,6 @@ options_templates.update(options_section(('text_encoder', "Text Encoder"), {
|
||||
"sd_textencoder_cache_size": OptionInfo(4, "Text encoder cache size", gr.Slider, {"minimum": 0, "maximum": 16, "step": 1}),
|
||||
"sd_textencder_linebreak": OptionInfo(True, "Use line break as prompt segment marker", gr.Checkbox),
|
||||
"diffusers_zeros_prompt_pad": OptionInfo(False, "Use zeros for prompt padding", gr.Checkbox),
|
||||
"te_hijack": OptionInfo(True, "Offload after prompt encode", gr.Checkbox),
|
||||
"te_optional_sep": OptionInfo("<h2>Optional</h2>", "", gr.HTML),
|
||||
"te_shared_t5": OptionInfo(True, "T5: Use shared instance of text encoder"),
|
||||
"te_pooled_embeds": OptionInfo(False, "SDXL: Use weighted pooled embeds"),
|
||||
@@ -520,7 +519,7 @@ options_templates.update(options_section(('image-metadata', "Image Metadata"), {
|
||||
|
||||
options_templates.update(options_section(('ui', "User Interface"), {
|
||||
"themes_sep_ui": OptionInfo("<h2>Theme options</h2>", "", gr.HTML),
|
||||
"theme_type": OptionInfo("Standard", "Theme type", gr.Radio, {"choices": ["Modern", "Standard", "None"]}),
|
||||
"theme_type": OptionInfo("Modern", "Theme type", gr.Radio, {"choices": ["Modern", "Standard", "None"]}),
|
||||
"theme_style": OptionInfo("Auto", "Theme mode", gr.Radio, {"choices": ["Auto", "Dark", "Light"]}),
|
||||
"gradio_theme": OptionInfo("black-teal", "UI theme", gr.Dropdown, lambda: {"choices": theme.list_themes()}, refresh=theme.refresh_themes),
|
||||
|
||||
@@ -533,10 +532,10 @@ options_templates.update(options_section(('ui', "User Interface"), {
|
||||
"subpath": OptionInfo("", "Mount URL subpath"),
|
||||
"ui_request_timeout": OptionInfo(120000, "UI request timeout", gr.Slider, {"minimum": 1000, "maximum": 300000, "step": 10}),
|
||||
|
||||
"cards_sep_ui": OptionInfo("<h2>Card options</h2>", "", gr.HTML),
|
||||
"extra_networks_card_size": OptionInfo(140, "UI card size (px)", gr.Slider, {"minimum": 20, "maximum": 2000, "step": 1}),
|
||||
"extra_networks_card_cover": OptionInfo("sidebar", "UI position", gr.Radio, {"choices": ["cover", "inline", "sidebar"]}),
|
||||
"extra_networks_card_square": OptionInfo(True, "UI disable variable aspect ratio"),
|
||||
"cards_sep_ui": OptionInfo("<h2>Networks panel</h2>", "", gr.HTML),
|
||||
"extra_networks_card_size": OptionInfo(140, "Network card size (px)", gr.Slider, {"minimum": 20, "maximum": 2000, "step": 1}),
|
||||
"extra_networks_card_cover": OptionInfo("sidebar", "Network panel position", gr.Radio, {"choices": ["cover", "inline", "sidebar"]}),
|
||||
"extra_networks_card_square": OptionInfo(True, "Disable variable aspect ratio"),
|
||||
|
||||
"other_sep_ui": OptionInfo("<h2>Other...</h2>", "", gr.HTML),
|
||||
"ui_locale": OptionInfo("Auto", "UI locale", gr.Dropdown, lambda: {"choices": theme.list_locales()}),
|
||||
@@ -556,7 +555,6 @@ options_templates.update(options_section(('ui', "User Interface"), {
|
||||
"return_mask_composite": OptionInfo(False, "Inpainting include masked composite in results"),
|
||||
"send_seed": OptionInfo(True, "Send seed when sending prompt or image to other interface", gr.Checkbox, {"visible": False}),
|
||||
"send_size": OptionInfo(False, "Send size when sending prompt or image to another interface", gr.Checkbox, {"visible": False}),
|
||||
|
||||
}))
|
||||
|
||||
options_templates.update(options_section(('live-preview', "Live Previews"), {
|
||||
|
||||
@@ -20,10 +20,12 @@ def get_default_modes(cmd_opts, mem_stat):
|
||||
cmd_opts.medvram = True # VAE Tiling and other stuff
|
||||
default_offload_mode = "balanced"
|
||||
default_diffusers_offload_min_gpu_memory = 0
|
||||
default_diffusers_offload_always = ', '.join(['T5EncoderModel', 'UMT5EncoderModel'])
|
||||
log.info(f"Device detect: memory={gpu_memory:.1f} default=balanced optimization=medvram")
|
||||
elif gpu_memory >= 24:
|
||||
default_offload_mode = "balanced"
|
||||
default_diffusers_offload_max_gpu_memory = 0.8
|
||||
default_diffusers_offload_always = ', '.join(['T5EncoderModel', 'UMT5EncoderModel'])
|
||||
default_diffusers_offload_never = ', '.join(['CLIPTextModel', 'CLIPTextModelWithProjection', 'AutoencoderKL'])
|
||||
log.info(f"Device detect: memory={gpu_memory:.1f} default=balanced optimization=highvram")
|
||||
else:
|
||||
|
||||
@@ -54,6 +54,7 @@ pipelines = {
|
||||
'SegMoE': getattr(diffusers, 'DiffusionPipeline', None),
|
||||
'FLite': getattr(diffusers, 'DiffusionPipeline', None),
|
||||
'Bria': getattr(diffusers, 'DiffusionPipeline', None),
|
||||
'hdm': getattr(diffusers, 'DiffusionPipeline', None),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ def create_ui():
|
||||
with gr.Tab("CLiP Interrogate", elem_id='tab_clip_interrogate'):
|
||||
with gr.Row():
|
||||
clip_model = gr.Dropdown([], value=shared.opts.interrogate_clip_model, label='CLiP model', elem_id='clip_clip_model')
|
||||
ui_common.create_refresh_button(clip_model, openclip.refresh_clip_models, lambda: {"choices": openclip.refresh_clip_models()}, 'clip_refresh_models')
|
||||
ui_common.create_refresh_button(clip_model, openclip.refresh_clip_models, lambda: {"choices": openclip.refresh_clip_models()}, 'clip_models_refresh')
|
||||
blip_model = gr.Dropdown(list(openclip.caption_models), value=shared.opts.interrogate_blip_model, label='Caption model', elem_id='btN_clip_blip_model')
|
||||
clip_mode = gr.Dropdown(openclip.caption_types, label='Mode', value='fast', elem_id='clip_clip_mode')
|
||||
with gr.Accordion(label='Advanced options', open=False, visible=True):
|
||||
|
||||
+28
-6
@@ -68,15 +68,18 @@ def delete_files(js_data, files, all_files, index):
|
||||
start_index = index
|
||||
deleted = []
|
||||
all_files = [f.split('/file=')[1] if 'file=' in f else f for f in all_files] if isinstance(all_files, list) else []
|
||||
all_files = [os.path.normpath(f) for f in all_files]
|
||||
for _image_index, filedata in enumerate(files, start_index):
|
||||
try:
|
||||
fn = filedata['name']
|
||||
fn = os.path.normpath(filedata['name'])
|
||||
if os.path.exists(fn) and os.path.isfile(fn):
|
||||
deleted.append(fn)
|
||||
os.remove(fn)
|
||||
if fn in all_files:
|
||||
all_files.remove(fn)
|
||||
shared.log.info(f'Delete: image="{fn}"')
|
||||
shared.log.info(f'Delete: image="{fn}"')
|
||||
else:
|
||||
shared.log.warning(f'Delete: image="{fn}" ui mismatch')
|
||||
base, _ext = os.path.splitext(fn)
|
||||
desc = f'{base}.txt'
|
||||
if os.path.exists(desc) and os.path.isfile(desc):
|
||||
@@ -333,7 +336,7 @@ def create_refresh_button(refresh_component, refresh_method, refreshed_args = No
|
||||
return gr.update(**args)
|
||||
|
||||
refresh_button = ui_components.ToolButton(value=ui_symbols.refresh, elem_id=elem_id, visible=visible)
|
||||
refresh_button.click(fn=refresh, inputs=[], outputs=[refresh_component])
|
||||
refresh_button.click(fn=refresh, inputs=[], outputs=[refresh_component], show_progress=False)
|
||||
return refresh_button
|
||||
|
||||
|
||||
@@ -344,7 +347,26 @@ def create_override_inputs(tab): # pylint: disable=unused-argument
|
||||
return override_settings
|
||||
|
||||
|
||||
def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info: gr.Textbox, is_subseed, subseed_strength=None):
|
||||
def reuse_seed(seed_component: gr.Number, reuse_button: gr.Button, subseed:bool=False):
|
||||
def reuse_click(selected_gallery_index):
|
||||
selected_gallery_index = int(selected_gallery_index)
|
||||
from modules import processing
|
||||
if processing.processed is None:
|
||||
seed = -1
|
||||
elif selected_gallery_index >= len(processing.processed.all_seeds):
|
||||
selected_gallery_index -= len(processing.processed.images) - len(processing.processed.all_seeds) # if we have more images than seeds it is likely the grid image
|
||||
seed = processing.processed.all_seeds[selected_gallery_index] if not subseed else processing.processed.all_subseeds[selected_gallery_index]
|
||||
elif len(processing.processed.all_seeds) > 0:
|
||||
seed = processing.processed.all_seeds[0] if not subseed else processing.processed.all_subseeds[0]
|
||||
else:
|
||||
seed = -1
|
||||
shared.log.debug(f'Reuse seed: index={selected_gallery_index} seed={seed} subseed={subseed}')
|
||||
return seed
|
||||
|
||||
reuse_button.click(fn=reuse_click, _js="selected_gallery_index", inputs=[seed_component], outputs=[seed_component], show_progress=False)
|
||||
|
||||
|
||||
def connect_reuse_seed(seed: gr.Number, reuse_seed_btn: gr.Button, generation_info: gr.Textbox, is_subseed, subseed_strength=None):
|
||||
""" Connects a 'reuse (sub)seed' button's click event so that it copies last used
|
||||
(sub)seed value from generation info the to the seed field. If copying subseed and subseed strength
|
||||
was 0, i.e. no variation seed was used, it copies the normal seed value instead."""
|
||||
@@ -372,9 +394,9 @@ def connect_reuse_seed(seed: gr.Number, reuse_seed: gr.Button, generation_info:
|
||||
return [restore_seed, gr_show(False)]
|
||||
dummy_component = gr.Number(visible=False, value=0)
|
||||
if subseed_strength is None:
|
||||
reuse_seed.click(fn=copy_seed, _js="(x, y) => [x, selected_gallery_index()]", show_progress=False, inputs=[generation_info, dummy_component], outputs=[seed, dummy_component])
|
||||
reuse_seed_btn.click(fn=copy_seed, _js="(x, y) => [x, selected_gallery_index()]", show_progress=False, inputs=[generation_info, dummy_component], outputs=[seed, dummy_component])
|
||||
else:
|
||||
reuse_seed.click(fn=copy_seed, _js="(x, y) => [x, selected_gallery_index()]", show_progress=False, inputs=[generation_info, dummy_component], outputs=[seed, dummy_component, subseed_strength])
|
||||
reuse_seed_btn.click(fn=copy_seed, _js="(x, y) => [x, selected_gallery_index()]", show_progress=False, inputs=[generation_info, dummy_component], outputs=[seed, dummy_component, subseed_strength])
|
||||
|
||||
|
||||
def update_token_counter(text):
|
||||
|
||||
@@ -9,9 +9,7 @@ class FormComponent:
|
||||
gr.Dropdown.get_expected_parent = FormComponent.get_expected_parent
|
||||
|
||||
|
||||
class ToolButton(FormComponent, gr.Button):
|
||||
"""Small button with single emoji as text, fits inside gradio forms"""
|
||||
|
||||
class ToolButton(FormComponent, gr.Button): # small button with single emoji as text
|
||||
def __init__(self, *args, **kwargs):
|
||||
classes = kwargs.pop("elem_classes", [])
|
||||
super().__init__(*args, elem_classes=["tool", *classes], **kwargs)
|
||||
@@ -19,66 +17,49 @@ class ToolButton(FormComponent, gr.Button):
|
||||
def get_block_name(self):
|
||||
return "button"
|
||||
|
||||
### unused components below for compatibility with extensions ###
|
||||
|
||||
class FormRow(FormComponent, gr.Row): # unused
|
||||
"""Same as gr.Row but fits inside gradio forms"""
|
||||
|
||||
def get_block_name(self):
|
||||
return "row"
|
||||
|
||||
|
||||
class FormColumn(FormComponent, gr.Column): # unused
|
||||
"""Same as gr.Column but fits inside gradio forms"""
|
||||
|
||||
def get_block_name(self):
|
||||
return "column"
|
||||
|
||||
|
||||
class FormGroup(FormComponent, gr.Group): # unused
|
||||
"""Same as gr.Row but fits inside gradio forms"""
|
||||
|
||||
def get_block_name(self):
|
||||
return "group"
|
||||
|
||||
|
||||
class FormHTML(FormComponent, gr.HTML): # unused
|
||||
"""Same as gr.HTML but fits inside gradio forms"""
|
||||
|
||||
def get_block_name(self):
|
||||
return "html"
|
||||
|
||||
|
||||
class FormColorPicker(FormComponent, gr.ColorPicker): # unused
|
||||
"""Same as gr.ColorPicker but fits inside gradio forms"""
|
||||
|
||||
def get_block_name(self):
|
||||
return "colorpicker"
|
||||
|
||||
|
||||
class DropdownMulti(FormComponent, gr.Dropdown): # unused
|
||||
"""Same as gr.Dropdown but always multiselect"""
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(multiselect=True, **kwargs)
|
||||
|
||||
def get_block_name(self):
|
||||
return "dropdown"
|
||||
|
||||
|
||||
class DropdownEditable(FormComponent, gr.Dropdown): # unused
|
||||
"""Same as gr.Dropdown but allows editing value"""
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(allow_custom_value=True, **kwargs)
|
||||
|
||||
def get_block_name(self):
|
||||
return "dropdown"
|
||||
|
||||
|
||||
class InputAccordion(gr.Checkbox): # unused
|
||||
"""A gr.Accordion that can be used as an input - returns True if open, False if closed.
|
||||
Actaully just a hidden checkbox, but creates an accordion that follows and is followed by the state of the checkbox.
|
||||
"""
|
||||
global_index = 0
|
||||
|
||||
def __init__(self, value, **kwargs):
|
||||
self.accordion_id = kwargs.get('elem_id')
|
||||
if self.accordion_id is None:
|
||||
@@ -97,15 +78,6 @@ class InputAccordion(gr.Checkbox): # unused
|
||||
self.accordion = gr.Accordion(**kwargs_accordion)
|
||||
|
||||
def extra(self):
|
||||
"""Allows you to put something into the label of the accordion.
|
||||
Use it like this:
|
||||
```
|
||||
with InputAccordion(False, label="Accordion") as acc:
|
||||
with acc.extra():
|
||||
FormHTML(value="hello", min_width=0)
|
||||
...
|
||||
```
|
||||
"""
|
||||
return gr.Column(elem_id=self.accordion_id + '-extra', elem_classes='input-accordion-extra', min_width=0)
|
||||
|
||||
def __enter__(self):
|
||||
@@ -120,11 +92,8 @@ class InputAccordion(gr.Checkbox): # unused
|
||||
|
||||
|
||||
class ResizeHandleRow(gr.Row): # unusued
|
||||
"""Same as gr.Row but fits inside gradio forms"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.elem_classes.append("resize-handle-row")
|
||||
|
||||
def get_block_name(self):
|
||||
return "row"
|
||||
|
||||
+32
-30
@@ -157,7 +157,9 @@ def create_ui(_blocks: gr.Blocks=None):
|
||||
|
||||
batch_count, batch_size = ui_sections.create_batch_inputs('control', accordion=True)
|
||||
|
||||
seed, _reuse_seed, subseed, _reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = ui_sections.create_seed_inputs('control', reuse_visible=False)
|
||||
seed, reuse_seed, subseed, reuse_subseed, subseed_strength, seed_resize_from_h, seed_resize_from_w = ui_sections.create_seed_inputs('control')
|
||||
ui_common.reuse_seed(seed, reuse_seed, subseed=False)
|
||||
ui_common.reuse_seed(subseed, reuse_subseed, subseed=True)
|
||||
|
||||
mask_controls = masking.create_segment_ui()
|
||||
|
||||
@@ -244,17 +246,17 @@ def create_ui(_blocks: gr.Blocks=None):
|
||||
enabled_cb = gr.Checkbox(enabled, label='Active', container=False, show_label=True, elem_id=f'control_unit-{i}-enabled')
|
||||
process_id = gr.Dropdown(label="Processor", choices=processors.list_models(), value='None', elem_id=f'control_unit-{i}-process_name')
|
||||
model_id = gr.Dropdown(label="ControlNet", choices=controlnet.list_models(), value='None', elem_id=f'control_unit-{i}-model_name')
|
||||
ui_common.create_refresh_button(model_id, controlnet.list_models, lambda: {"choices": controlnet.list_models(refresh=True)}, f'refresh_controlnet_models_{i}')
|
||||
ui_common.create_refresh_button(model_id, controlnet.list_models, lambda: {"choices": controlnet.list_models(refresh=True)}, f'controlnet_models_{i}_refresh')
|
||||
control_mode = gr.Dropdown(label="CN Mode", choices=['default'], value='default', visible=False, elem_id=f'control_unit-{i}-mode')
|
||||
model_strength = gr.Slider(label="CN Strength", minimum=0.01, maximum=2.0, step=0.01, value=1.0, elem_id=f'control_unit-{i}-strength')
|
||||
control_start = gr.Slider(label="CN Start", minimum=0.0, maximum=1.0, step=0.05, value=0, elem_id=f'control_unit-{i}-start')
|
||||
control_end = gr.Slider(label="CN End", minimum=0.0, maximum=1.0, step=0.05, value=1.0, elem_id=f'control_unit-{i}-end')
|
||||
control_tile = gr.Dropdown(label="CN Tiles", choices=[x.strip() for x in shared.opts.control_tiles.split(',') if 'x' in x], value='1x1', visible=False, elem_id=f'control_unit-{i}-tile')
|
||||
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
|
||||
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
|
||||
image_reuse= ui_components.ToolButton(value=ui_symbols.reuse)
|
||||
btn_preview= ui_components.ToolButton(value=ui_symbols.preview)
|
||||
image_preview = gr.Image(label="Input", type="pil", height=128, width=128, visible=False, interactive=True, show_label=False, show_download_button=False, container=False, elem_id=f'control_unit-{i}-override')
|
||||
reset_btn = ui_components.ToolButton(value=ui_symbols.reset, elem_id=f'controlnet_unit-{i}-reset')
|
||||
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'], elem_id=f'controlnet_unit-{i}-upload')
|
||||
image_reuse= ui_components.ToolButton(value=ui_symbols.reuse, elem_id=f'controlnet_unit-{i}-reuse')
|
||||
btn_preview= ui_components.ToolButton(value=ui_symbols.preview, elem_id=f'controlnet_unit-{i}-preview')
|
||||
image_preview = gr.Image(label="Input", type="pil", height=128, width=128, visible=False, interactive=True, show_label=False, show_download_button=False, container=False, elem_id=f'controlnet_unit-{i}-override')
|
||||
controlnet_ui_units.append(unit_ui)
|
||||
units.append(unit.Unit(
|
||||
unit_type = 'controlnet',
|
||||
@@ -297,13 +299,13 @@ def create_ui(_blocks: gr.Blocks=None):
|
||||
enabled_cb = gr.Checkbox(enabled, label='Active', container=False, show_label=True, elem_id=f'control_unit-{i}-enabled')
|
||||
process_id = gr.Dropdown(label="Processor", choices=processors.list_models(), value='None', elem_id=f'control_unit-{i}-process_name')
|
||||
model_id = gr.Dropdown(label="Adapter", choices=t2iadapter.list_models(), value='None', elem_id=f'control_unit-{i}-model_name')
|
||||
ui_common.create_refresh_button(model_id, t2iadapter.list_models, lambda: {"choices": t2iadapter.list_models(refresh=True)}, f'refresh_adapter_models_{i}')
|
||||
ui_common.create_refresh_button(model_id, t2iadapter.list_models, lambda: {"choices": t2iadapter.list_models(refresh=True)}, f'adapter_models_{i}_refresh')
|
||||
model_strength = gr.Slider(label="T2I Strength", minimum=0.01, maximum=1.0, step=0.01, value=1.0, elem_id=f'control_unit-{i}-strength')
|
||||
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
|
||||
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
|
||||
image_reuse= ui_components.ToolButton(value=ui_symbols.reuse)
|
||||
btn_preview= ui_components.ToolButton(value=ui_symbols.preview)
|
||||
image_preview = gr.Image(label="Input", show_label=False, type="pil", interactive=False, height=128, width=128, visible=False, elem_id=f'control_unit-{i}-override')
|
||||
reset_btn = ui_components.ToolButton(value=ui_symbols.reset, elem_id=f'adapter_unit-{i}-reset')
|
||||
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'], elem_id=f'adapter_unit-{i}-upload')
|
||||
image_reuse= ui_components.ToolButton(value=ui_symbols.reuse, elem_id=f'adapter_unit-{i}-reuse')
|
||||
btn_preview= ui_components.ToolButton(value=ui_symbols.preview, elem_id=f'adapter_unit-{i}-preview')
|
||||
image_preview = gr.Image(label="Input", show_label=False, type="pil", interactive=False, height=128, width=128, visible=False, elem_id=f'adapter_unit-{i}-override')
|
||||
adapter_ui_units.append(unit_ui)
|
||||
units.append(unit.Unit(
|
||||
unit_type = 't2i adapter',
|
||||
@@ -342,15 +344,15 @@ def create_ui(_blocks: gr.Blocks=None):
|
||||
enabled_cb = gr.Checkbox(enabled, label='Active', container=False, show_label=True, elem_id=f'control_unit-{i}-enabled')
|
||||
process_id = gr.Dropdown(label="Processor", choices=processors.list_models(), value='None', elem_id=f'control_unit-{i}-process_name')
|
||||
model_id = gr.Dropdown(label="ControlNet-XS", choices=xs.list_models(), value='None', elem_id=f'control_unit-{i}-model_name')
|
||||
ui_common.create_refresh_button(model_id, xs.list_models, lambda: {"choices": xs.list_models(refresh=True)}, f'refresh_xs_models_{i}')
|
||||
ui_common.create_refresh_button(model_id, xs.list_models, lambda: {"choices": xs.list_models(refresh=True)}, f'xs_models_{i}_refresh')
|
||||
model_strength = gr.Slider(label="CN Strength", minimum=0.01, maximum=1.0, step=0.01, value=1.0, elem_id=f'control_unit-{i}-strength')
|
||||
control_start = gr.Slider(label="Start", minimum=0.0, maximum=1.0, step=0.05, value=0, elem_id=f'control_unit-{i}-start')
|
||||
control_end = gr.Slider(label="End", minimum=0.0, maximum=1.0, step=0.05, value=1.0, elem_id=f'control_unit-{i}-end')
|
||||
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
|
||||
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
|
||||
image_reuse= ui_components.ToolButton(value=ui_symbols.reuse)
|
||||
btn_preview= ui_components.ToolButton(value=ui_symbols.preview)
|
||||
image_preview = gr.Image(label="Input", show_label=False, type="pil", interactive=False, height=128, width=128, visible=False, elem_id=f'control_unit-{i}-override')
|
||||
reset_btn = ui_components.ToolButton(value=ui_symbols.reset, elem_id=f'controlnetxs_unit-{i}-reset')
|
||||
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'], elem_id=f'controlnetxs_unit-{i}-upload')
|
||||
image_reuse= ui_components.ToolButton(value=ui_symbols.reuse, elem_id=f'controlnetxs_unit-{i}-reuse')
|
||||
btn_preview= ui_components.ToolButton(value=ui_symbols.preview, elem_id=f'controlnetxs_unit-{i}-preview')
|
||||
image_preview = gr.Image(label="Input", show_label=False, type="pil", interactive=False, height=128, width=128, visible=False, elem_id=f'controlnetxs_unit-{i}-override')
|
||||
controlnetxs_ui_units.append(unit_ui)
|
||||
units.append(unit.Unit(
|
||||
unit_type = 'xs',
|
||||
@@ -390,13 +392,13 @@ def create_ui(_blocks: gr.Blocks=None):
|
||||
enabled_cb = gr.Checkbox(enabled, label='Active', container=False, show_label=True, elem_id=f'control_unit-{i}-enabled')
|
||||
process_id = gr.Dropdown(label="Processor", choices=processors.list_models(), value='None', elem_id=f'control_unit-{i}-process_name')
|
||||
model_id = gr.Dropdown(label="Model", choices=lite.list_models(), value='None', elem_id=f'control_unit-{i}-model_name')
|
||||
ui_common.create_refresh_button(model_id, lite.list_models, lambda: {"choices": lite.list_models(refresh=True)}, f'refresh_lite_models_{i}')
|
||||
ui_common.create_refresh_button(model_id, lite.list_models, lambda: {"choices": lite.list_models(refresh=True)}, f'lite_models_{i}_refresh')
|
||||
model_strength = gr.Slider(label="CN Strength", minimum=0.01, maximum=1.0, step=0.01, value=1.0, elem_id=f'control_unit-{i}-strength')
|
||||
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
|
||||
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
|
||||
image_reuse= ui_components.ToolButton(value=ui_symbols.reuse)
|
||||
image_preview = gr.Image(label="Input", show_label=False, type="pil", interactive=False, height=128, width=128, visible=False, elem_id=f'control_unit-{i}-override')
|
||||
btn_preview= ui_components.ToolButton(value=ui_symbols.preview)
|
||||
reset_btn = ui_components.ToolButton(value=ui_symbols.reset, elem_id=f'lite_unit-{i}-reset')
|
||||
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'], elem_id=f'lite_unit-{i}-upload')
|
||||
image_reuse= ui_components.ToolButton(value=ui_symbols.reuse, elem_id=f'lite_unit-{i}-reuse')
|
||||
image_preview = gr.Image(label="Input", show_label=False, type="pil", interactive=False, height=128, width=128, visible=False, elem_id=f'lite_unit-{i}-override')
|
||||
btn_preview= ui_components.ToolButton(value=ui_symbols.preview, elem_id=f'lite_unit-{i}-preview')
|
||||
lite_ui_units.append(unit_ui)
|
||||
units.append(unit.Unit(
|
||||
unit_type = 'lite',
|
||||
@@ -436,11 +438,11 @@ def create_ui(_blocks: gr.Blocks=None):
|
||||
enabled_cb = gr.Checkbox(enabled, label='Active', container=False, show_label=True, elem_id=f'control_unit-{i}-enabled')
|
||||
model_id = gr.Dropdown(label="Reference", choices=reference.list_models(), value='Reference', visible=False, elem_id=f'control_unit-{i}-model_name')
|
||||
model_strength = gr.Slider(label="CN Strength", minimum=0.01, maximum=1.0, step=0.01, value=1.0, visible=False, elem_id=f'control_unit-{i}-strength')
|
||||
reset_btn = ui_components.ToolButton(value=ui_symbols.reset)
|
||||
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'])
|
||||
image_reuse= ui_components.ToolButton(value=ui_symbols.reuse)
|
||||
image_preview = gr.Image(label="Input", show_label=False, type="pil", interactive=False, height=128, width=128, visible=False, elem_id=f'control_unit-{i}-override')
|
||||
btn_preview= ui_components.ToolButton(value=ui_symbols.preview)
|
||||
reset_btn = ui_components.ToolButton(value=ui_symbols.reset, elem_id=f'reference_unit-{i}-reset')
|
||||
image_upload = gr.UploadButton(label=ui_symbols.upload, file_types=['image'], elem_classes=['form', 'gradio-button', 'tool'], elem_id=f'reference_unit-{i}-upload')
|
||||
image_reuse= ui_components.ToolButton(value=ui_symbols.reuse, elem_id=f'reference_unit-{i}-reuse')
|
||||
image_preview = gr.Image(label="Input", show_label=False, type="pil", interactive=False, height=128, width=128, visible=False, elem_id=f'reference_unit-{i}-override')
|
||||
btn_preview= ui_components.ToolButton(value=ui_symbols.preview, elem_id=f'reference_unit-{i}-preview')
|
||||
units.append(unit.Unit(
|
||||
unit_type = 'reference',
|
||||
index = i,
|
||||
|
||||
+2
-2
@@ -248,7 +248,7 @@ def create_ui_logs():
|
||||
def create_ui_github():
|
||||
with gr.Row():
|
||||
github_search = gr.Textbox(label="Search GitHub Wiki Pages", elem_id="github_search", elem_classes="docs-search")
|
||||
github_search_btn = ui_components.ToolButton(value=ui_symbols.search, elem_id="github_search_btn")
|
||||
github_search_btn = ui_components.ToolButton(value=ui_symbols.search, elem_id="github_btn_search")
|
||||
with gr.Row():
|
||||
github_result = gr.HTML(elem_id="github_result", value='', elem_classes="github-result")
|
||||
with gr.Row():
|
||||
@@ -262,7 +262,7 @@ def create_ui_github():
|
||||
def create_ui_docs():
|
||||
with gr.Row():
|
||||
docs_search = gr.Textbox(label="Search Docs", elem_id="github_search", elem_classes="docs-search")
|
||||
docs_search_btn = ui_components.ToolButton(value=ui_symbols.search, elem_id="github_search_btn")
|
||||
docs_search_btn = ui_components.ToolButton(value=ui_symbols.search, elem_id="docs_btn_search")
|
||||
with gr.Row():
|
||||
docs_result = gr.HTML(elem_id="docs_result", value='', elem_classes="docs-result")
|
||||
with gr.Row():
|
||||
|
||||
@@ -24,7 +24,7 @@ extra_pages = shared.extra_networks
|
||||
debug = shared.log.trace if os.environ.get('SD_EN_DEBUG', None) is not None else lambda *args, **kwargs: None
|
||||
debug('Trace: EN')
|
||||
card_full = '''
|
||||
<div class='card' onclick={card_click} title='{name}' data-page='{page}' data-name='{name}' data-filename='{filename}' data-short='{short}' data-tags='{tags}' data-mtime='{mtime}' data-size='{size}' data-search='{search}' style='--data-color: {color}'>
|
||||
<div class='card' onclick={card_click} title='{name}' data-page='{page}' data-name='{name}' data-filename='{filename}' data-short='{short}' data-tags='{tags}' data-mtime='{mtime}' data-size='{size}' data-search='{search}' data-version='{version}' style='--data-color: {color}'>
|
||||
<div class='overlay'>
|
||||
<div class='name {reference}'>{title}</div>
|
||||
</div>
|
||||
@@ -56,7 +56,9 @@ def init_api():
|
||||
global allowed_dirs # pylint: disable=global-statement
|
||||
if len(allowed_dirs) == 0:
|
||||
allowed_dirs = shared.demo.allowed_paths
|
||||
if not os.path.exists(filename):
|
||||
if filename is None or len(filename) == 0:
|
||||
return JSONResponse({ "error": "no filename" }, status_code=400)
|
||||
if not os.path.exists(filename) or not os.path.isfile(filename):
|
||||
return JSONResponse({ "error": f"file {filename}: not found" }, status_code=404)
|
||||
if filename.startswith('html/') or filename.startswith('models/'):
|
||||
return FileResponse(filename, headers={"Accept-Ranges": "bytes"})
|
||||
@@ -149,23 +151,30 @@ class ExtraNetworksPage:
|
||||
return text.replace('~tabname', tabname)
|
||||
|
||||
def create_xyz_grid(self):
|
||||
"""
|
||||
xyz_grid = [x for x in scripts.scripts_data if x.script_class.__module__ == "xyz_grid.py"][0].module
|
||||
pass
|
||||
|
||||
def add_prompt(p, opt, x):
|
||||
for item in [x for x in self.items if x["name"] == opt]:
|
||||
try:
|
||||
p.prompt = f'{p.prompt} {eval(item["prompt"])}' # pylint: disable=eval-used
|
||||
except Exception as e:
|
||||
shared.log.error(f'Cannot evaluate extra network prompt: {item["prompt"]} {e}')
|
||||
|
||||
if not any(self.title in x.label for x in xyz_grid.axis_options):
|
||||
if self.title == 'Model':
|
||||
return
|
||||
opt = xyz_grid.AxisOption(f"[Network] {self.title}", str, add_prompt, choices=lambda: [x["name"] for x in self.items])
|
||||
if opt not in xyz_grid.axis_options:
|
||||
xyz_grid.axis_options.append(opt)
|
||||
"""
|
||||
def find_version(self, item, info):
|
||||
all_versions = info.get('modelVersions', [])
|
||||
if len(all_versions) == 0:
|
||||
return {}
|
||||
try:
|
||||
if item is None:
|
||||
return all_versions[0]
|
||||
elif hasattr(item, 'hash') and item.hash is not None:
|
||||
current_hash = item.hash[:8].upper()
|
||||
elif hasattr(item, 'shorthash') and item.shorthash is not None:
|
||||
current_hash = item.shorthash[:8].upper()
|
||||
elif hasattr(item, 'sha256') and item.sha256 is not None:
|
||||
current_hash = item.sha256[:8].upper()
|
||||
else:
|
||||
return all_versions[0]
|
||||
for v in info.get('modelVersions', []):
|
||||
for f in v.get('files', []):
|
||||
if any(h.startswith(current_hash) for h in f.get('hashes', {}).values()):
|
||||
return v
|
||||
except Exception as e:
|
||||
errors.display(e, 'Network version')
|
||||
return all_versions[0]
|
||||
|
||||
def link_preview(self, filename):
|
||||
quoted_filename = urllib.parse.quote(filename.replace('\\', '/'))
|
||||
@@ -225,7 +234,6 @@ class ExtraNetworksPage:
|
||||
debug(f'EN create-items: page={self.name} items={len(self.items)} time={t1-t0:.2f}')
|
||||
self.list_time += t1-t0
|
||||
|
||||
|
||||
def create_page(self, tabname, skip = False):
|
||||
debug(f'EN create-page: {self.name}')
|
||||
if self.page_time > refresh_time and len(self.html) > 0: # cached page
|
||||
@@ -276,9 +284,19 @@ class ExtraNetworksPage:
|
||||
if len(subdir) == 0:
|
||||
continue
|
||||
style = 'color: var(--color-accent)' if subdir in ['All', 'Local', 'Diffusers', 'Reference'] else ''
|
||||
subdirs_html += f'<button class="lg secondary gradio-button custom-button" onclick="extraNetworksSearchButton(event)" style="{style}">{html.escape(subdir)}</button><br>'
|
||||
if subdir in ['All', 'Local', 'Diffusers', 'Reference']:
|
||||
style = 'network-reference'
|
||||
else:
|
||||
style = 'network-folder'
|
||||
subdirs_html += f'<button class="lg secondary gradio-button custom-button {style}" onclick="extraNetworksSearchButton(event)">{html.escape(subdir)}</button><br>'
|
||||
self.html = ''
|
||||
self.create_items(tabname)
|
||||
versions = sorted({item.get("version", "") for item in self.items if item.get("version")})
|
||||
if 'ref' in versions:
|
||||
versions.remove('ref')
|
||||
versions_html = ''
|
||||
for ver in versions:
|
||||
versions_html += f'<button class="lg secondary gradio-button custom-button network-model" onclick="extraNetworksFilterVersion(event)">{html.escape(ver)}</button><br>'
|
||||
self.create_xyz_grid()
|
||||
htmls = []
|
||||
|
||||
@@ -302,7 +320,7 @@ class ExtraNetworksPage:
|
||||
htmls.append(self.create_html(item, tabname))
|
||||
self.html += ''.join(htmls)
|
||||
self.page_time = time.time()
|
||||
self.html = f"<div id='{tabname}_{self_name_id}_subdirs' class='extra-network-subdirs'>{subdirs_html}</div><div id='~tabname_{self_name_id}_cards' class='extra-network-cards'>{self.html}</div>"
|
||||
self.html = f"""<div id='{tabname}_{self_name_id}_subdirs' class='extra-network-subdirs'>{subdirs_html}{versions_html}</div><div id='~tabname_{self_name_id}_cards' class='extra-network-cards'>{self.html}</div>"""
|
||||
shared.log.debug(f'Networks: type="{self.name}" items={len(self.items)} subfolders={len(subdirs)} tab={tabname} folders={self.allowed_directories_for_previews()} list={self.list_time:.2f} thumb={self.preview_time:.2f} desc={self.desc_time:.2f} info={self.info_time:.2f} workers={shared.max_workers}')
|
||||
if len(self.missing_thumbs) > 0:
|
||||
threading.Thread(target=self.create_thumb).start()
|
||||
@@ -331,7 +349,7 @@ class ExtraNetworksPage:
|
||||
"filename": item.get('filename', ''),
|
||||
"short": os.path.splitext(os.path.basename(item.get('filename', '')))[0],
|
||||
"tags": '|'.join([item.get('tags')] if isinstance(item.get('tags', {}), str) else list(item.get('tags', {}).keys())),
|
||||
"preview": html.escape(item.get('preview', None) or self.link_preview('html/card-no-preview.png')),
|
||||
"preview": html.escape(item.get('preview', None) or self.link_preview('html/missing.png')),
|
||||
"width": 'var(--card-size)',
|
||||
"height": 'var(--card-size)' if shared.opts.extra_networks_card_square else 'auto',
|
||||
"fit": shared.opts.extra_networks_card_fit,
|
||||
@@ -357,7 +375,7 @@ class ExtraNetworksPage:
|
||||
|
||||
def find_preview_file(self, path):
|
||||
if path is None:
|
||||
return 'html/card-no-preview.png'
|
||||
return 'html/missing.png'
|
||||
if os.path.join('models', 'Reference') in path:
|
||||
return path
|
||||
exts = ["jpg", "jpeg", "png", "webp", "tiff", "jp2", "jxl"]
|
||||
@@ -374,7 +392,7 @@ class ExtraNetworksPage:
|
||||
if '.thumb.' not in file:
|
||||
self.missing_thumbs.append(file)
|
||||
return file
|
||||
return 'html/card-no-preview.png'
|
||||
return 'html/missing.png'
|
||||
|
||||
def find_preview(self, filename):
|
||||
t0 = time.time()
|
||||
@@ -424,7 +442,7 @@ class ExtraNetworksPage:
|
||||
item['preview'] = self.link_preview(found)
|
||||
debug(f'EN mapped-preview: {item["name"]}={found}')
|
||||
if item.get('preview', None) is None:
|
||||
item['preview'] = self.link_preview('html/card-no-preview.png')
|
||||
item['preview'] = self.link_preview('html/missing.png')
|
||||
debug(f'EN missing-preview: {item["name"]}')
|
||||
self.preview_time += time.time() - t0
|
||||
|
||||
@@ -692,19 +710,19 @@ def create_ui(container, button_parent, tabname, skip_indexing = False):
|
||||
|
||||
def fn_save_img(image):
|
||||
if ui.last_item is None or ui.last_item.local_preview is None:
|
||||
return 'html/card-no-preview.png'
|
||||
return 'html/missing.png'
|
||||
images = []
|
||||
if ui.gallery is not None:
|
||||
images = list(ui.gallery.temp_files) # gallery cannot be used as input component so looking at most recently registered temp files
|
||||
if len(images) < 1:
|
||||
shared.log.warning(f'Network no image: item="{ui.last_item.name}"')
|
||||
return 'html/card-no-preview.png'
|
||||
return 'html/missing.png'
|
||||
try:
|
||||
images.sort(key=lambda f: os.path.getmtime(f), reverse=True)
|
||||
image = Image.open(images[0])
|
||||
except Exception as e:
|
||||
shared.log.error(f'Network error opening image: item="{ui.last_item.name}" {e}')
|
||||
return 'html/card-no-preview.png'
|
||||
return 'html/missing.png'
|
||||
fn_delete_img(image)
|
||||
if image.width > 512 or image.height > 512:
|
||||
image = image.convert('RGB')
|
||||
@@ -723,7 +741,7 @@ def create_ui(container, button_parent, tabname, skip_indexing = False):
|
||||
if os.path.exists(file):
|
||||
os.remove(file)
|
||||
shared.log.debug(f'Network delete image: item="{ui.last_item.name}" filename="{file}"')
|
||||
return 'html/card-no-preview.png'
|
||||
return 'html/missing.png'
|
||||
|
||||
def fn_save_desc(desc):
|
||||
if hasattr(ui.last_item, 'type') and ui.last_item.type == 'Style':
|
||||
|
||||
@@ -28,10 +28,11 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage):
|
||||
preview = v.get('preview', v['path'])
|
||||
preview_file = self.find_preview_file(os.path.join(reference_dir, preview))
|
||||
_size, mtime = modelstats.stat(preview_file)
|
||||
name = os.path.normpath(os.path.join(reference_dir, k)).replace('\\', '/')
|
||||
yield {
|
||||
"type": 'Model',
|
||||
"name": os.path.join(reference_dir, k),
|
||||
"title": os.path.join(reference_dir, k),
|
||||
"name": name,
|
||||
"title": name,
|
||||
"filename": url,
|
||||
"preview": self.find_preview(os.path.join(reference_dir, preview)),
|
||||
"local_preview": preview_file,
|
||||
@@ -42,6 +43,7 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage):
|
||||
"info": {},
|
||||
"metadata": {},
|
||||
"description": v.get('desc', ''),
|
||||
"version": "ref",
|
||||
}
|
||||
|
||||
def create_item(self, name):
|
||||
@@ -62,6 +64,9 @@ class ExtraNetworksPageCheckpoints(ui_extra_networks.ExtraNetworksPage):
|
||||
}
|
||||
record["info"] = self.find_info(checkpoint.filename)
|
||||
record["description"] = self.find_description(checkpoint.filename, record["info"])
|
||||
version = self.find_version(checkpoint, record["info"])
|
||||
record["version"] = version.get("baseModel", "") if record["info"] else ""
|
||||
|
||||
except Exception as e:
|
||||
shared.log.debug(f'Networks error: type=model file="{name}" {e}')
|
||||
return record
|
||||
|
||||
@@ -17,7 +17,7 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage):
|
||||
lora_load.list_available_networks()
|
||||
|
||||
@staticmethod
|
||||
def get_tags(l, info):
|
||||
def get_tags(l, info, version):
|
||||
tags = {}
|
||||
try:
|
||||
if l.metadata is not None:
|
||||
@@ -37,26 +37,13 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage):
|
||||
tag = ' '.join(words[1:]).lower()
|
||||
tags[tag] = words[0]
|
||||
|
||||
def find_version():
|
||||
found_versions = []
|
||||
current_hash = l.hash[:8].upper()
|
||||
all_versions = info.get('modelVersions', [])
|
||||
for v in info.get('modelVersions', []):
|
||||
for f in v.get('files', []):
|
||||
if any(h.startswith(current_hash) for h in f.get('hashes', {}).values()):
|
||||
found_versions.append(v)
|
||||
if len(found_versions) == 0:
|
||||
found_versions = all_versions
|
||||
return found_versions
|
||||
|
||||
for v in find_version(): # trigger words from info json
|
||||
possible_tags = v.get('trainedWords', [])
|
||||
if isinstance(possible_tags, list):
|
||||
for tag_str in possible_tags:
|
||||
for tag in tag_str.split(','):
|
||||
tag = tag.strip().lower()
|
||||
if tag not in tags:
|
||||
tags[tag] = 0
|
||||
possible_tags = version.get('trainedWords', [])
|
||||
if isinstance(possible_tags, list):
|
||||
for tag_str in possible_tags:
|
||||
for tag in tag_str.split(','):
|
||||
tag = tag.strip().lower()
|
||||
if tag not in tags:
|
||||
tags[tag] = 0
|
||||
|
||||
possible_tags = info.get('tags', []) # tags from info json
|
||||
if not isinstance(possible_tags, list):
|
||||
@@ -87,6 +74,7 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage):
|
||||
name = os.path.splitext(os.path.relpath(l.filename, shared.cmd_opts.lora_dir))[0]
|
||||
size, mtime = modelstats.stat(l.filename)
|
||||
info = self.find_info(l.filename)
|
||||
version = self.find_version(l, info)
|
||||
item = {
|
||||
"type": 'Lora',
|
||||
"name": name,
|
||||
@@ -97,10 +85,10 @@ class ExtraNetworksPageLora(ui_extra_networks.ExtraNetworksPage):
|
||||
"metadata": json.dumps(l.metadata, indent=4) if l.metadata else None,
|
||||
"mtime": mtime,
|
||||
"size": size,
|
||||
"version": l.sd_version,
|
||||
"version": version.get("baseModel", l.sd_version) if info else l.sd_version,
|
||||
"info": info,
|
||||
"description": self.find_description(l.filename, info),
|
||||
"tags": self.get_tags(l, info),
|
||||
"tags": self.get_tags(l, info, version),
|
||||
}
|
||||
return item
|
||||
except Exception as e:
|
||||
|
||||
@@ -16,6 +16,7 @@ class ExtraNetworksPageVAEs(ui_extra_networks.ExtraNetworksPage):
|
||||
try:
|
||||
size, mtime = modelstats.stat(filename)
|
||||
info = self.find_info(filename)
|
||||
version = self.find_version(None, info)
|
||||
record = {
|
||||
"type": 'VAE',
|
||||
"name": name,
|
||||
@@ -31,6 +32,7 @@ class ExtraNetworksPageVAEs(ui_extra_networks.ExtraNetworksPage):
|
||||
"size": size,
|
||||
"info": info,
|
||||
"description": self.find_description(filename, info),
|
||||
"version": version.get("baseModel", "N/A") if info else "N/A",
|
||||
}
|
||||
yield record
|
||||
except Exception as e:
|
||||
|
||||
+27
-13
@@ -12,20 +12,34 @@ def read_media(fn):
|
||||
shared.log.error(f'Gallery not found: file="{fn}"')
|
||||
return [[], None, '', '', f'Media not found: {fn}']
|
||||
stat_size, stat_mtime = modelstats.stat(fn)
|
||||
if fn.lower().endswith('.mp4'):
|
||||
frames, fps, duration, w, h, codec, _frame = video.get_video_params(fn)
|
||||
# Treat common containers as video for preview; Gradio/HTML5 will handle codec support.
|
||||
video_exts = ('.mp4', '.webm', '.mkv', '.avi', '.mov', '.mpg', '.mpeg', '.mjpeg')
|
||||
if fn.lower().endswith(video_exts):
|
||||
geninfo = ''
|
||||
log = f'''
|
||||
<p>Video <b>{w} x {h}</b>
|
||||
| Codec <b>{codec}</b>
|
||||
| Frames <b>{frames:,}</b>
|
||||
| FPS <b>{fps:.2f}</b>
|
||||
| Duration <b>{duration:.2f}</b>
|
||||
| Size <b>{stat_size:,}</b>
|
||||
| Modified <b>{stat_mtime}</b></p><br>
|
||||
'''
|
||||
return [gr.update(visible=False, value=[]), gr.update(visible=True, value=fn), geninfo, geninfo, log]
|
||||
else:
|
||||
try:
|
||||
frames, fps, duration, w, h, codec, _frame = video.get_video_params(fn)
|
||||
log = f'''
|
||||
<p>Video <b>{w} x {h}</b>
|
||||
| Codec <b>{codec}</b>
|
||||
| Frames <b>{frames:,}</b>
|
||||
| FPS <b>{fps:.2f}</b>
|
||||
| Duration <b>{duration:.2f}</b>
|
||||
| Size <b>{stat_size:,}</b>
|
||||
| Modified <b>{stat_mtime}</b></p><br>
|
||||
'''
|
||||
except Exception as e: # keep preview even if probing fails
|
||||
shared.log.warning(f'Video probe failed: file="{fn}" {e}')
|
||||
log = f'''
|
||||
<p>Video
|
||||
| Size <b>{stat_size:,}</b>
|
||||
| Modified <b>{stat_mtime}</b></p><br>
|
||||
'''
|
||||
return [
|
||||
gr.update(visible=False, value=[]), # hide image gallery preview
|
||||
gr.update(visible=True, value=fn), # show video player
|
||||
geninfo, geninfo, log
|
||||
]
|
||||
else: # image
|
||||
image = Image.open(fn)
|
||||
image.already_saved_as = fn
|
||||
geninfo, _items = images.read_info_from_image(image)
|
||||
|
||||
@@ -159,8 +159,8 @@ def create_ui():
|
||||
|
||||
img2img_gallery, img2img_generation_info, img2img_html_info, _img2img_html_info_formatted, img2img_html_log = ui_common.create_output_panel("img2img", prompt=img2img_prompt)
|
||||
|
||||
ui_common.connect_reuse_seed(seed, reuse_seed, img2img_generation_info, is_subseed=False)
|
||||
ui_common.connect_reuse_seed(subseed, reuse_subseed, img2img_generation_info, is_subseed=True, subseed_strength=subseed_strength)
|
||||
ui_common.reuse_seed(seed, reuse_seed, subseed=False)
|
||||
ui_common.reuse_seed(subseed, reuse_subseed, subseed=True)
|
||||
|
||||
img2img_prompt_img.change(fn=modules.images.image_data, inputs=[img2img_prompt_img], outputs=[img2img_prompt, img2img_prompt_img])
|
||||
dummy_component1 = gr.Textbox(visible=False, value='dummy')
|
||||
|
||||
@@ -169,11 +169,11 @@ def create_ui():
|
||||
merge_mode_docs = gr.HTML(value=getattr(merge_methods, "weighted_sum", "").__doc__.replace("\n", "<br>"))
|
||||
with gr.Row():
|
||||
primary_model_name = gr.Dropdown(sd_model_choices(), label="Primary model", value="None")
|
||||
create_refresh_button(primary_model_name, sd_models.list_models, lambda: {"choices": sd_model_choices()}, "refresh_checkpoint_A")
|
||||
create_refresh_button(primary_model_name, sd_models.list_models, lambda: {"choices": sd_model_choices()}, "checkpoint_A_refresh")
|
||||
secondary_model_name = gr.Dropdown(sd_model_choices(), label="Secondary model", value="None")
|
||||
create_refresh_button(secondary_model_name, sd_models.list_models, lambda: {"choices": sd_model_choices()}, "refresh_checkpoint_B")
|
||||
create_refresh_button(secondary_model_name, sd_models.list_models, lambda: {"choices": sd_model_choices()}, "checkpoint_B_refresh")
|
||||
tertiary_model_name = gr.Dropdown(sd_model_choices(), label="Tertiary model", value="None", visible=False)
|
||||
tertiary_refresh = create_refresh_button(tertiary_model_name, sd_models.list_models, lambda: {"choices": sd_model_choices()}, "refresh_checkpoint_C", visible=False)
|
||||
tertiary_refresh = create_refresh_button(tertiary_model_name, sd_models.list_models, lambda: {"choices": sd_model_choices()}, "checkpoint_C_refresh", visible=False)
|
||||
with gr.Row():
|
||||
with gr.Tabs() as tabs:
|
||||
with gr.TabItem(label="Simple Merge", id=0):
|
||||
@@ -229,7 +229,7 @@ def create_ui():
|
||||
bake_in_vae = gr.Dropdown(choices=["None"] + list(sd_vae.vae_dict), value="None", interactive=True, label="Replace VAE")
|
||||
create_refresh_button(bake_in_vae, sd_vae.refresh_vae_list,
|
||||
lambda: {"choices": ["None"] + list(sd_vae.vae_dict)},
|
||||
"modelmerger_refresh_bake_in_vae")
|
||||
"modelmerger_bake_in_vae_refresh")
|
||||
with gr.Row():
|
||||
modelmerger_merge = gr.Button(value="Merge", variant='primary')
|
||||
|
||||
@@ -403,7 +403,7 @@ def create_ui():
|
||||
with gr.Column(scale=5):
|
||||
with gr.Row():
|
||||
model_name = gr.Dropdown(sd_models.checkpoint_titles(), label="Input model")
|
||||
create_refresh_button(model_name, sd_models.list_models, lambda: {"choices": sd_models.checkpoint_titles()}, "refresh_checkpoint_Z")
|
||||
create_refresh_button(model_name, sd_models.list_models, lambda: {"choices": sd_models.checkpoint_titles()}, "checkpoint_Z_refresh")
|
||||
with gr.Column(scale=5):
|
||||
custom_name = gr.Textbox(label="Output model", placeholder="Output model path")
|
||||
with gr.Row():
|
||||
@@ -492,7 +492,7 @@ def create_ui():
|
||||
with gr.Row(elem_id='civitai_search_row'):
|
||||
civit_search_text = gr.Textbox(label='', placeholder='keyword', elem_id="civit_search_text")
|
||||
civit_search_tag = gr.Textbox(label='', placeholder='tag', elem_id="civit_search_text")
|
||||
civit_search_text_btn = ToolButton(value=ui_symbols.search, interactive=True)
|
||||
civit_search_text_btn = ToolButton(value=ui_symbols.search, interactive=True, elem_id="civit_text_search")
|
||||
with gr.Accordion(label='Advanced', open=False, elem_id="civitai_search_options"):
|
||||
civit_download_btn = gr.Button(value="Download model", variant='primary', elem_id="civitai_download_btn", visible=False)
|
||||
with gr.Row():
|
||||
@@ -530,7 +530,7 @@ def create_ui():
|
||||
gr.HTML('<h2> Download model from huggingface<br></h2>')
|
||||
with gr.Row():
|
||||
hf_search_text = gr.Textbox('', label='Search models', placeholder='search huggingface models')
|
||||
hf_search_btn = ToolButton(value=ui_symbols.search)
|
||||
hf_search_btn = ToolButton(value=ui_symbols.search, interactive=True, elem_id="hf_text_search")
|
||||
with gr.Row():
|
||||
hf_selected = gr.Textbox('', label='Select model', placeholder='select model from search results or enter model name manually')
|
||||
with gr.Accordion(label='Advanced', open=False, elem_id="hf_search_options"):
|
||||
|
||||
@@ -87,7 +87,7 @@ def create_resolution_inputs(tab, default_width=1024, default_height=1024):
|
||||
ar_dropdown = gr.Dropdown(show_label=False, interactive=True, choices=ar_list, value=ar_list[0], elem_id=f"{tab}_ar", elem_classes=["ar-dropdown"])
|
||||
for c in [ar_dropdown, width, height]:
|
||||
c.change(fn=ar_change, inputs=[ar_dropdown, width, height], outputs=[width, height], show_progress=False)
|
||||
res_switch_btn = ToolButton(value=ui_symbols.switch, elem_id=f"{tab}_res_switch_btn")
|
||||
res_switch_btn = ToolButton(value=ui_symbols.switch, elem_id=f"{tab}_res_btn_swap")
|
||||
res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress=False)
|
||||
return width, height
|
||||
|
||||
@@ -111,12 +111,12 @@ def create_seed_inputs(tab, reuse_visible=True, accordion=True, subseed_visible=
|
||||
with gr.Accordion(open=False, label="Seed", elem_id=f"{tab}_seed_group", elem_classes=["small-accordion"]) if accordion else gr.Group():
|
||||
with gr.Row(elem_id=f"{tab}_seed_row", variant="compact"):
|
||||
seed = gr.Number(label='Initial seed', value=-1, elem_id=f"{tab}_seed", container=True)
|
||||
random_seed = ToolButton(ui_symbols.random, elem_id=f"{tab}_random_seed")
|
||||
reuse_seed = ToolButton(ui_symbols.reuse, elem_id=f"{tab}_reuse_seed", visible=reuse_visible)
|
||||
random_seed = ToolButton(ui_symbols.random, elem_id=f"{tab}_seed_random")
|
||||
reuse_seed = ToolButton(ui_symbols.reuse, elem_id=f"{tab}_seed_reuse", visible=reuse_visible)
|
||||
with gr.Row(elem_id=f"{tab}_subseed_row", variant="compact", visible=subseed_visible):
|
||||
subseed = gr.Number(label='Variation', value=-1, elem_id=f"{tab}_subseed", container=True)
|
||||
random_subseed = ToolButton(ui_symbols.random, elem_id=f"{tab}_random_subseed")
|
||||
reuse_subseed = ToolButton(ui_symbols.reuse, elem_id=f"{tab}_reuse_subseed", visible=reuse_visible)
|
||||
random_subseed = ToolButton(ui_symbols.random, elem_id=f"{tab}_subseed_random")
|
||||
reuse_subseed = ToolButton(ui_symbols.reuse, elem_id=f"{tab}_subseed_reuse", visible=reuse_visible)
|
||||
subseed_strength = gr.Slider(label='Variation strength', value=0.0, minimum=0, maximum=1, step=0.01, elem_id=f"{tab}_subseed_strength", elem_classes=["subseed-strength"])
|
||||
with gr.Row(visible=seed_resize_visible):
|
||||
seed_resize_from_w = gr.Slider(minimum=0, maximum=4096, step=8, label="Resize seed from width", value=0, elem_id=f"{tab}_seed_resize_from_w")
|
||||
@@ -342,7 +342,7 @@ def create_resize_inputs(tab, images, accordion=True, latent=False, non_zero=Tru
|
||||
resize_name = gr.Dropdown(label=f"Method{prefix}" if non_zero else "Resize method", elem_id=f"{tab}_resize_name", choices=available_upscalers, value=available_upscalers[0], visible=True)
|
||||
resize_context_choices = ["Add with forward", "Remove with forward", "Add with backward", "Remove with backward"]
|
||||
resize_context = gr.Dropdown(label=f"Context{prefix}", elem_id=f"{tab}_resize_context", choices=resize_context_choices, value=resize_context_choices[0], visible=False)
|
||||
resize_refresh_btn = ui_common.create_refresh_button(resize_name, modelloader.load_upscalers, lambda: {"choices": modelloader.load_upscalers()}, 'refresh_upscalers')
|
||||
resize_refresh_btn = ui_common.create_refresh_button(resize_name, modelloader.load_upscalers, lambda: {"choices": modelloader.load_upscalers()}, f'{tab}_upscalers_refresh')
|
||||
|
||||
def resize_mode_change(mode):
|
||||
if mode is None or mode == 0:
|
||||
@@ -365,9 +365,9 @@ def create_resize_inputs(tab, images, accordion=True, latent=False, non_zero=Tru
|
||||
ar_dropdown = gr.Dropdown(show_label=False, interactive=True, choices=ar_list, value=ar_list[0], elem_id=f"{tab}_resize_ar", elem_classes=["ar-dropdown"])
|
||||
for c in [ar_dropdown, width, height]:
|
||||
c.change(fn=ar_change, inputs=[ar_dropdown, width, height], outputs=[width, height], show_progress=False)
|
||||
res_switch_btn = ToolButton(value=ui_symbols.switch, elem_id=f"{tab}_resize_switch_size_btn")
|
||||
res_switch_btn = ToolButton(value=ui_symbols.switch, elem_id=f"{tab}_resize_size_swap")
|
||||
res_switch_btn.click(lambda w, h: (h, w), inputs=[width, height], outputs=[width, height], show_progress=False)
|
||||
detect_image_size_btn = ToolButton(value=ui_symbols.detect, elem_id=f"{tab}_resize_detect_size_btn")
|
||||
detect_image_size_btn = ToolButton(value=ui_symbols.detect, elem_id=f"{tab}_resize_detect_size")
|
||||
el = tab.split('_')[0]
|
||||
detect_image_size_btn.click(fn=lambda w, h, _: (w or gr.update(), h or gr.update()), _js=f'currentImageResolution{el}', inputs=[dummy_component, dummy_component, dummy_component], outputs=[width, height], show_progress=False)
|
||||
with gr.Tab(label="Scale", id=1, elem_id=f"{tab}_scale_tab_scale") as tab_scale_by:
|
||||
|
||||
@@ -77,11 +77,11 @@ def create_setting_component(key, is_quicksettings=False):
|
||||
if info.refresh is not None:
|
||||
if is_quicksettings:
|
||||
res = comp(label=info.label, value=fun(), elem_id=elem_id, **args)
|
||||
ui_common.create_refresh_button(res, info.refresh, info.component_args, f"refresh_{key}")
|
||||
ui_common.create_refresh_button(res, info.refresh, info.component_args, f"settings_{key}_refresh")
|
||||
else:
|
||||
with gr.Row():
|
||||
res = comp(label=info.label, value=fun(), elem_id=elem_id, **args)
|
||||
ui_common.create_refresh_button(res, info.refresh, info.component_args, f"refresh_{key}")
|
||||
ui_common.create_refresh_button(res, info.refresh, info.component_args, f"settings_{key}_refresh")
|
||||
elif info.folder is not None:
|
||||
with gr.Row():
|
||||
res = comp(label=info.label, value=fun(), elem_id=elem_id, elem_classes="folder-selector", **args)
|
||||
@@ -175,10 +175,10 @@ def create_ui():
|
||||
global text_settings # pylint: disable=global-statement
|
||||
text_settings = gr.Textbox(elem_id="settings_json", elem_classes=["settings_json"], value=lambda: shared.opts.dumpjson(), visible=False)
|
||||
with gr.Row(elem_id="system_row"):
|
||||
restart_submit = gr.Button(value="Restart server", variant='primary', elem_id="restart_submit")
|
||||
shutdown_submit = gr.Button(value="Shutdown server", variant='primary', elem_id="shutdown_submit")
|
||||
unload_sd_model = gr.Button(value='Unload model', variant='primary', elem_id="sett_unload_sd_model")
|
||||
reload_sd_model = gr.Button(value='Reload model', variant='primary', elem_id="sett_reload_sd_model")
|
||||
restart_submit = gr.Button(value="Restart server", variant='primary', elem_id="restart_submit")
|
||||
shutdown_submit = gr.Button(value="Shutdown server", variant='primary', elem_id="shutdown_submit")
|
||||
enable_profiling = gr.Button(value='Start profiling', variant='primary', elem_id="enable_profiling")
|
||||
|
||||
with gr.Tabs(elem_id="system") as system_tabs:
|
||||
@@ -187,7 +187,6 @@ def create_ui():
|
||||
with gr.TabItem("Settings", id="system_settings", elem_id="tab_settings"):
|
||||
with gr.Row(elem_id="settings_row"):
|
||||
settings_submit = gr.Button(value="Apply settings", variant='primary', elem_id="settings_submit")
|
||||
preview_theme = gr.Button(value="Preview theme", variant='primary', elem_id="settings_preview_theme")
|
||||
defaults_submit = gr.Button(value="Restore defaults", variant='primary', elem_id="defaults_submit")
|
||||
with gr.Row():
|
||||
_settings_search = gr.Textbox(label="Search", elem_id="settings_search")
|
||||
@@ -288,7 +287,6 @@ def create_ui():
|
||||
reload_sd_model.click(fn=reload_sd_weights, inputs=[], outputs=[])
|
||||
enable_profiling.click(fn=switch_profiling, inputs=[], outputs=[enable_profiling])
|
||||
request_notifications.click(fn=lambda: None, inputs=[], outputs=[], _js='function(){}')
|
||||
preview_theme.click(fn=None, _js='previewTheme', inputs=[], outputs=[])
|
||||
settings_submit.click(
|
||||
fn=call_queue.wrap_gradio_call(run_settings, extra_outputs=[gr.update()]),
|
||||
inputs=components,
|
||||
@@ -323,7 +321,7 @@ def create_quicksettings(interfaces):
|
||||
quicksetting_keys.append(k)
|
||||
shared.settings_components[k] = component
|
||||
quicksetting_keys = gr.State(value=','.join(quicksetting_keys), elem_id="quicksettings_keys")
|
||||
btn_reset = ui_components.ToolButton(value=ui_symbols.clear, visible=True, elem_id="quicksettings_reset")
|
||||
btn_reset = ui_components.ToolButton(value=ui_symbols.clear, visible=True, elem_id="quicksettings_clear")
|
||||
btn_reset.click(fn=reset_quicksettings, inputs=[quicksetting_keys], outputs=quicksetting_components)
|
||||
|
||||
generation_parameters_copypaste.connect_paste_params_buttons()
|
||||
|
||||
@@ -27,8 +27,6 @@ preview = '🖼️'
|
||||
image = '🖌️'
|
||||
resize = '⁜'
|
||||
interrogate = '♻'
|
||||
int_clip = '✎'
|
||||
int_blip = '✐'
|
||||
bullet = '⃝'
|
||||
sort_alpha_asc = '\uf15d'
|
||||
sort_alpha_dsc = '\uf15e'
|
||||
|
||||
@@ -45,8 +45,8 @@ def create_ui():
|
||||
txt2img_script_inputs = modules.scripts_manager.scripts_txt2img.setup_ui(parent='txt2img', accordion=True)
|
||||
|
||||
txt2img_gallery, txt2img_generation_info, txt2img_html_info, _txt2img_html_info_formatted, txt2img_html_log = ui_common.create_output_panel("txt2img", preview=True, prompt=txt2img_prompt)
|
||||
ui_common.connect_reuse_seed(seed, reuse_seed, txt2img_generation_info, is_subseed=False)
|
||||
ui_common.connect_reuse_seed(subseed, reuse_subseed, txt2img_generation_info, is_subseed=True, subseed_strength=subseed_strength)
|
||||
ui_common.reuse_seed(seed, reuse_seed, subseed=False)
|
||||
ui_common.reuse_seed(subseed, reuse_subseed, subseed=True)
|
||||
|
||||
dummy_component = gr.Textbox(visible=False, value='dummy')
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ models = {
|
||||
Model(name='WAN 2.2 5B I2V',
|
||||
url='https://huggingface.co/Wan-AI/Wan2.2-TI2V-5B-Diffusers',
|
||||
repo='Wan-AI/Wan2.2-TI2V-5B-Diffusers',
|
||||
repo_cls=diffusers.WanPipeline,
|
||||
repo_cls=diffusers.WanImageToVideoPipeline,
|
||||
te_cls=transformers.T5EncoderModel,
|
||||
dit_cls=diffusers.WanTransformer3DModel),
|
||||
Model(name='WAN 2.2 A14B T2V',
|
||||
@@ -164,7 +164,7 @@ models = {
|
||||
Model(name='WAN 2.2 A14B I2V',
|
||||
url='https://huggingface.co/Wan-AI/Wan2.2-I2V-A14B-Diffusers',
|
||||
repo='Wan-AI/Wan2.2-T2V-A14B-Diffusers',
|
||||
repo_cls=diffusers.WanPipeline,
|
||||
repo_cls=diffusers.WanImageToVideoPipeline,
|
||||
te_cls=transformers.T5EncoderModel,
|
||||
dit_cls=diffusers.WanTransformer3DModel),
|
||||
Model(name='WAN 2.1 1.3B T2V',
|
||||
@@ -212,35 +212,35 @@ models = {
|
||||
],
|
||||
'SkyReels V2': [
|
||||
Model(name='None'),
|
||||
Model(name='SkyReels-V2 T2I-DF 1.3B-540P',
|
||||
Model(name='SkyReels-V2 T2V-DF 1.3B-540P',
|
||||
url='https://huggingface.co/Skywork/SkyReels-V2-DF-1.3B-540P-Diffusers',
|
||||
repo='Skywork/SkyReels-V2-DF-1.3B-540P-Diffusers',
|
||||
repo_cls=diffusers.SkyReelsV2DiffusionForcingPipeline,
|
||||
repo_revision='refs/pr/1',
|
||||
te_cls=transformers.UMT5EncoderModel,
|
||||
dit_cls=diffusers.SkyReelsV2Transformer3DModel),
|
||||
Model(name='SkyReels-V2 T2I-DF 14B-720P',
|
||||
Model(name='SkyReels-V2 T2V-DF 14B-720P',
|
||||
url='https://huggingface.co/Skywork/SkyReels-V2-DF-14B-720P-Diffusers',
|
||||
repo='Skywork/SkyReels-V2-DF-14B-720P-Diffusers',
|
||||
repo_cls=diffusers.SkyReelsV2DiffusionForcingPipeline,
|
||||
repo_revision='refs/pr/1',
|
||||
te_cls=transformers.UMT5EncoderModel,
|
||||
dit_cls=diffusers.SkyReelsV2Transformer3DModel),
|
||||
Model(name='SkyReels-V2 I2I-DF 14B-720P',
|
||||
Model(name='SkyReels-V2 I2V-DF 14B-720P',
|
||||
url='https://huggingface.co/Skywork/SkyReels-V2-DF-14B-720P-Diffusers',
|
||||
repo='Skywork/SkyReels-V2-DF-14B-720P-Diffusers',
|
||||
repo_cls=diffusers.SkyReelsV2DiffusionForcingImageToVideoPipeline,
|
||||
repo_revision='refs/pr/1',
|
||||
te_cls=transformers.UMT5EncoderModel,
|
||||
dit_cls=diffusers.SkyReelsV2Transformer3DModel),
|
||||
Model(name='SkyReels-V2 T2I 14B-720P',
|
||||
Model(name='SkyReels-V2 T2V 14B-720P',
|
||||
url='https://huggingface.co/Skywork/SkyReels-V2-T2V-14B-720P-Diffusers',
|
||||
repo='Skywork/SkyReels-V2-T2V-14B-720P-Diffusers',
|
||||
repo_cls=diffusers.SkyReelsV2Pipeline,
|
||||
repo_revision='refs/pr/1',
|
||||
te_cls=transformers.UMT5EncoderModel,
|
||||
dit_cls=diffusers.SkyReelsV2Transformer3DModel),
|
||||
Model(name='SkyReels-V2 I2I 14B-720P',
|
||||
Model(name='SkyReels-V2 I2V 14B-720P',
|
||||
url='https://huggingface.co/Skywork/SkyReels-V2-I2V-14B-720P-Diffusers',
|
||||
repo='Skywork/SkyReels-V2-I2V-14B-720P-Diffusers',
|
||||
repo_cls=diffusers.SkyReelsV2ImageToVideoPipeline,
|
||||
@@ -320,4 +320,18 @@ models = {
|
||||
te_cls=transformers.T5EncoderModel,
|
||||
dit_cls=diffusers.CogVideoXTransformer3DModel),
|
||||
],
|
||||
'nVidia Cosmos': [
|
||||
Model(name='nvidia Cosmos Predict2 2B I2V',
|
||||
url='https://huggingface.co/nvidia/Cosmos-Predict2-2B-Text2Image',
|
||||
repo='nvidia/Cosmos-Predict2-2B-Video2World',
|
||||
repo_cls=diffusers.Cosmos2VideoToWorldPipeline,
|
||||
te_cls=transformers.T5EncoderModel,
|
||||
dit_cls=diffusers.CosmosTransformer3DModel),
|
||||
Model(name='nvidia Cosmos Predict2 2B I2V',
|
||||
url='https://huggingface.co/nvidia/Cosmos-Predict2-2B-Text2Image',
|
||||
repo='nvidia/Cosmos-Predict2-2B-Video2World',
|
||||
repo_cls=diffusers.Cosmos2VideoToWorldPipeline,
|
||||
te_cls=transformers.T5EncoderModel,
|
||||
dit_cls=diffusers.CosmosTransformer3DModel),
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import os
|
||||
import copy
|
||||
import time
|
||||
from modules import shared, errors, sd_models, sd_checkpoint, model_quant, devices, sd_hijack_te
|
||||
from modules.video_models import models_def, video_utils, video_vae, video_overrides, video_cache
|
||||
from modules import shared, errors, sd_models, sd_checkpoint, model_quant, devices, sd_hijack_te, sd_hijack_vae
|
||||
from modules.video_models import models_def, video_utils, video_overrides, video_cache
|
||||
|
||||
|
||||
loaded_model = None
|
||||
debug = shared.log.trace if os.environ.get('SD_VIDEO_DEBUG', None) is not None else lambda *args, **kwargs: None
|
||||
|
||||
|
||||
def load_model(selected: models_def.Model):
|
||||
@@ -24,7 +22,7 @@ def load_model(selected: models_def.Model):
|
||||
# text encoder
|
||||
try:
|
||||
quant_args = model_quant.create_config(module='TE')
|
||||
debug(f'Video load: module=te repo="{selected.te or selected.repo}" folder="{selected.te_folder}" cls={selected.te_cls.__name__} quant={model_quant.get_quant_type(quant_args)}')
|
||||
shared.log.debug(f'Video load: module=te repo="{selected.te or selected.repo}" folder="{selected.te_folder}" cls={selected.te_cls.__name__} quant={model_quant.get_quant_type(quant_args)}')
|
||||
text_encoder = selected.te_cls.from_pretrained(
|
||||
pretrained_model_name_or_path=selected.te or selected.repo,
|
||||
subfolder=selected.te_folder,
|
||||
@@ -41,7 +39,7 @@ def load_model(selected: models_def.Model):
|
||||
# transformer
|
||||
try:
|
||||
quant_args = model_quant.create_config(module='Model')
|
||||
debug(f'Video load: module=transformer repo="{selected.dit or selected.repo}" folder="{selected.dit_folder}" cls={selected.dit_cls.__name__} quant={model_quant.get_quant_type(quant_args)}')
|
||||
shared.log.debug(f'Video load: module=transformer repo="{selected.dit or selected.repo}" folder="{selected.dit_folder}" cls={selected.dit_cls.__name__} quant={model_quant.get_quant_type(quant_args)}')
|
||||
transformer = selected.dit_cls.from_pretrained(
|
||||
pretrained_model_name_or_path=selected.dit or selected.repo,
|
||||
subfolder=selected.dit_folder,
|
||||
@@ -60,7 +58,7 @@ def load_model(selected: models_def.Model):
|
||||
|
||||
# model
|
||||
try:
|
||||
debug(f'Video load: module=pipe repo="{selected.repo}" cls={selected.repo_cls.__name__}')
|
||||
shared.log.debug(f'Video load: module=pipe repo="{selected.repo}" cls={selected.repo_cls.__name__}')
|
||||
shared.sd_model = selected.repo_cls.from_pretrained(
|
||||
pretrained_model_name_or_path=selected.repo,
|
||||
transformer=transformer,
|
||||
@@ -81,13 +79,10 @@ def load_model(selected: models_def.Model):
|
||||
shared.sd_model.sd_checkpoint_info = sd_checkpoint.CheckpointInfo(selected.repo)
|
||||
shared.sd_model.sd_model_hash = None
|
||||
sd_models.set_diffuser_options(shared.sd_model, offload=False)
|
||||
|
||||
if selected.vae_hijack and hasattr(shared.sd_model.vae, 'decode'):
|
||||
shared.sd_model.vae.orig_decode = shared.sd_model.vae.decode
|
||||
shared.sd_model.vae.decode = video_vae.hijack_vae_decode
|
||||
shared.sd_model.vae.orig_encode = shared.sd_model.vae.encode
|
||||
shared.sd_model.vae.encode = video_vae.hijack_vae_encode
|
||||
sd_hijack_vae.init_hijack(shared.sd_model)
|
||||
if selected.te_hijack and hasattr(shared.sd_model, 'encode_prompt'):
|
||||
# shared.sd_model.orig_encode_prompt = shared.sd_model.encode_prompt
|
||||
sd_hijack_te.init_hijack(shared.sd_model)
|
||||
if selected.image_hijack and hasattr(shared.sd_model, 'encode_image'):
|
||||
shared.sd_model.orig_encode_image = shared.sd_model.encode_image
|
||||
|
||||
@@ -46,3 +46,5 @@ def set_overrides(p: processing.StableDiffusionProcessingVideo, selected: Model)
|
||||
if 'LTX' in cls:
|
||||
p.task_args['width'] = 32 * (p.width // 32)
|
||||
p.task_args['height'] = 32 * (p.height // 32)
|
||||
if 'SkyReelsV2DiffusionForcing' in cls:
|
||||
p.task_args['overlap_history'] = 17
|
||||
|
||||
@@ -69,6 +69,8 @@ def generate(*args, **kwargs):
|
||||
elif 'T2V' in model:
|
||||
if init_image is not None:
|
||||
shared.log.warning('Video: op=T2V init image not supported')
|
||||
else:
|
||||
shared.log.warning(f'Video: unknown model type "{model}"')
|
||||
|
||||
# cleanup memory
|
||||
shared.sd_model = sd_models.apply_balanced_offload(shared.sd_model)
|
||||
|
||||
@@ -92,8 +92,8 @@ def create_ui(prompt, negative, styles, overrides):
|
||||
with gr.Row():
|
||||
frames = gr.Slider(label='Frames', minimum=1, maximum=1024, step=1, value=17, elem_id="video_frames")
|
||||
seed = gr.Number(label='Initial seed', value=-1, elem_id="video_seed", container=True)
|
||||
random_seed = ToolButton(ui_symbols.random, elem_id="video_random_seed")
|
||||
reuse_seed = ToolButton(ui_symbols.reuse, elem_id="video_reuse_seed")
|
||||
random_seed = ToolButton(ui_symbols.random, elem_id="video_seed_random")
|
||||
reuse_seed = ToolButton(ui_symbols.reuse, elem_id="video_seed_reuse")
|
||||
with gr.Accordion(open=False, label="Parameters", elem_id='video_parameters_accordion'):
|
||||
steps, sampler_index = ui_sections.create_sampler_and_steps_selection(None, "video", default_steps=50)
|
||||
with gr.Row():
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import os
|
||||
import time
|
||||
import torch
|
||||
from modules import shared, sd_models, devices, timer, errors
|
||||
from modules import shared, devices
|
||||
|
||||
|
||||
debug = shared.log.trace if os.environ.get('SD_VIDEO_DEBUG', None) is not None else lambda *args, **kwargs: None
|
||||
@@ -45,57 +43,3 @@ def vae_decode_tiny(latents):
|
||||
images = vae.decode_video(latents, parallel=False).transpose(1, 2).mul_(2).sub_(1)
|
||||
images = images.transpose(1, 2).mul_(2).sub_(1)
|
||||
return (images, None)
|
||||
|
||||
|
||||
def hijack_vae_decode(*args, **kwargs):
|
||||
shared.state.begin('VAE')
|
||||
t0 = time.time()
|
||||
res = None
|
||||
if vae_type == 'Tiny':
|
||||
res = vae_decode_tiny(args[0])
|
||||
if vae_type == 'Remote':
|
||||
pass
|
||||
if res is None:
|
||||
shared.sd_model = sd_models.apply_balanced_offload(shared.sd_model, exclude=['vae'])
|
||||
try:
|
||||
sd_models.move_model(shared.sd_model.vae, devices.device)
|
||||
if torch.is_tensor(args[0]):
|
||||
latents = args[0].to(device=devices.device, dtype=shared.sd_model.vae.dtype) # upcast to vae dtype
|
||||
res = shared.sd_model.vae.orig_decode(latents, *args[1:], **kwargs)
|
||||
t1 = time.time()
|
||||
shared.log.debug(f'Decode: vae={shared.sd_model.vae.__class__.__name__} slicing={getattr(shared.sd_model.vae, "use_slicing", None)} tiling={getattr(shared.sd_model.vae, "use_tiling", None)} latents={list(latents.shape)}:{latents.device}:{latents.dtype} time={t1-t0:.3f}')
|
||||
else:
|
||||
res = shared.sd_model.vae.orig_decode(*args, **kwargs)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Decode: type={vae_type} {e}')
|
||||
errors.display(e, 'vae')
|
||||
res = None
|
||||
t1 = time.time()
|
||||
timer.process.add('vae', t1-t0)
|
||||
shared.state.end()
|
||||
return res
|
||||
|
||||
|
||||
def hijack_vae_encode(*args, **kwargs):
|
||||
shared.state.begin('VAE')
|
||||
t0 = time.time()
|
||||
res = None
|
||||
if res is None:
|
||||
shared.sd_model = sd_models.apply_balanced_offload(shared.sd_model, exclude=['vae'])
|
||||
try:
|
||||
sd_models.move_model(shared.sd_model.vae, devices.device)
|
||||
if torch.is_tensor(args[0]):
|
||||
latents = args[0].to(device=devices.device, dtype=shared.sd_model.vae.dtype) # upcast to vae dtype
|
||||
res = shared.sd_model.vae.orig_encode(latents, *args[1:], **kwargs)
|
||||
t1 = time.time()
|
||||
shared.log.debug(f'Encode: vae={shared.sd_model.vae.__class__.__name__} slicing={getattr(shared.sd_model.vae, "use_slicing", None)} tiling={getattr(shared.sd_model.vae, "use_tiling", None)} latents={list(latents.shape)}:{latents.device}:{latents.dtype} time={t1-t0:.3f}')
|
||||
else:
|
||||
res = shared.sd_model.vae.orig_encode(*args, **kwargs)
|
||||
except Exception as e:
|
||||
shared.log.error(f'Encode: type={vae_type} {e}')
|
||||
errors.display(e, 'vae')
|
||||
res = None
|
||||
t1 = time.time()
|
||||
timer.process.add('vae', t1-t0)
|
||||
shared.state.end()
|
||||
return res
|
||||
|
||||
Reference in New Issue
Block a user