Spaces:
Runtime error
Runtime error
from os import path | |
from PIL import Image | |
from typing import Any | |
from constants import DEVICE | |
from paths import FastStableDiffusionPaths | |
from backend.upscale.upscaler import upscale_image | |
from backend.controlnet import controlnet_settings_from_dict | |
from backend.upscale.tiled_upscale import generate_upscaled_image | |
from frontend.webui.image_variations_ui import generate_image_variations | |
from backend.lora import ( | |
get_active_lora_weights, | |
update_lora_weights, | |
load_lora_weight, | |
) | |
from backend.models.lcmdiffusion_setting import ( | |
DiffusionTask, | |
LCMDiffusionSetting, | |
ControlNetSetting, | |
) | |
_batch_count = 1 | |
_edit_lora_settings = False | |
def user_value( | |
value_type: type, | |
message: str, | |
default_value: Any, | |
) -> Any: | |
try: | |
value = value_type(input(message)) | |
except: | |
value = default_value | |
return value | |
def interactive_mode( | |
config, | |
context, | |
): | |
print("=============================================") | |
print("Welcome to FastSD CPU Interactive CLI") | |
print("=============================================") | |
while True: | |
print("> 1. Text to Image") | |
print("> 2. Image to Image") | |
print("> 3. Image Variations") | |
print("> 4. EDSR Upscale") | |
print("> 5. SD Upscale") | |
print("> 6. Edit default generation settings") | |
print("> 7. Edit LoRA settings") | |
print("> 8. Edit ControlNet settings") | |
print("> 9. Edit negative prompt") | |
print("> 10. Quit") | |
option = user_value( | |
int, | |
"Enter a Diffusion Task number (1): ", | |
1, | |
) | |
if option not in range(1, 11): | |
print("Wrong Diffusion Task number!") | |
exit() | |
if option == 1: | |
interactive_txt2img( | |
config, | |
context, | |
) | |
elif option == 2: | |
interactive_img2img( | |
config, | |
context, | |
) | |
elif option == 3: | |
interactive_variations( | |
config, | |
context, | |
) | |
elif option == 4: | |
interactive_edsr( | |
config, | |
context, | |
) | |
elif option == 5: | |
interactive_sdupscale( | |
config, | |
context, | |
) | |
elif option == 6: | |
interactive_settings( | |
config, | |
context, | |
) | |
elif option == 7: | |
interactive_lora( | |
config, | |
context, | |
True, | |
) | |
elif option == 8: | |
interactive_controlnet( | |
config, | |
context, | |
True, | |
) | |
elif option == 9: | |
interactive_negative( | |
config, | |
context, | |
) | |
elif option == 10: | |
exit() | |
def interactive_negative( | |
config, | |
context, | |
): | |
settings = config.lcm_diffusion_setting | |
print(f"Current negative prompt: '{settings.negative_prompt}'") | |
user_input = input("Write a negative prompt (set guidance > 1.0): ") | |
if user_input == "": | |
return | |
else: | |
settings.negative_prompt = user_input | |
def interactive_controlnet( | |
config, | |
context, | |
menu_flag=False, | |
): | |
""" | |
@param menu_flag: Indicates whether this function was called from the main | |
interactive CLI menu; _True_ if called from the main menu, _False_ otherwise | |
""" | |
settings = config.lcm_diffusion_setting | |
if not settings.controlnet: | |
settings.controlnet = ControlNetSetting() | |
current_enabled = settings.controlnet.enabled | |
current_adapter_path = settings.controlnet.adapter_path | |
current_conditioning_scale = settings.controlnet.conditioning_scale | |
current_control_image = settings.controlnet._control_image | |
option = input("Enable ControlNet? (y/N): ") | |
settings.controlnet.enabled = True if option.upper() == "Y" else False | |
if settings.controlnet.enabled: | |
option = input( | |
f"Enter ControlNet adapter path ({settings.controlnet.adapter_path}): " | |
) | |
if option != "": | |
settings.controlnet.adapter_path = option | |
settings.controlnet.conditioning_scale = user_value( | |
float, | |
f"Enter ControlNet conditioning scale ({settings.controlnet.conditioning_scale}): ", | |
settings.controlnet.conditioning_scale, | |
) | |
option = input( | |
f"Enter ControlNet control image path (Leave empty to reuse current): " | |
) | |
if option != "": | |
try: | |
new_image = Image.open(option) | |
settings.controlnet._control_image = new_image | |
except (AttributeError, FileNotFoundError) as e: | |
settings.controlnet._control_image = None | |
if ( | |
not settings.controlnet.adapter_path | |
or not path.exists(settings.controlnet.adapter_path) | |
or not settings.controlnet._control_image | |
): | |
print("Invalid ControlNet settings! Disabling ControlNet") | |
settings.controlnet.enabled = False | |
if ( | |
settings.controlnet.enabled != current_enabled | |
or settings.controlnet.adapter_path != current_adapter_path | |
): | |
settings.rebuild_pipeline = True | |
def interactive_lora( | |
config, | |
context, | |
menu_flag=False, | |
): | |
""" | |
@param menu_flag: Indicates whether this function was called from the main | |
interactive CLI menu; _True_ if called from the main menu, _False_ otherwise | |
""" | |
if context == None or context.lcm_text_to_image.pipeline == None: | |
print("Diffusion pipeline not initialized, please run a generation task first!") | |
return | |
print("> 1. Change LoRA weights") | |
print("> 2. Load new LoRA model") | |
option = user_value( | |
int, | |
"Enter a LoRA option (1): ", | |
1, | |
) | |
if option not in range(1, 3): | |
print("Wrong LoRA option!") | |
return | |
if option == 1: | |
update_weights = [] | |
active_weights = get_active_lora_weights() | |
for lora in active_weights: | |
weight = user_value( | |
float, | |
f"Enter a new LoRA weight for {lora[0]} ({lora[1]}): ", | |
lora[1], | |
) | |
update_weights.append( | |
( | |
lora[0], | |
weight, | |
) | |
) | |
if len(update_weights) > 0: | |
update_lora_weights( | |
context.lcm_text_to_image.pipeline, | |
config.lcm_diffusion_setting, | |
update_weights, | |
) | |
elif option == 2: | |
# Load a new LoRA | |
settings = config.lcm_diffusion_setting | |
settings.lora.fuse = False | |
settings.lora.enabled = False | |
settings.lora.path = input("Enter LoRA model path: ") | |
settings.lora.weight = user_value( | |
float, | |
"Enter a LoRA weight (0.5): ", | |
0.5, | |
) | |
if not path.exists(settings.lora.path): | |
print("Invalid LoRA model path!") | |
return | |
settings.lora.enabled = True | |
load_lora_weight(context.lcm_text_to_image.pipeline, settings) | |
if menu_flag: | |
global _edit_lora_settings | |
_edit_lora_settings = False | |
option = input("Edit LoRA settings after every generation? (y/N): ") | |
if option.upper() == "Y": | |
_edit_lora_settings = True | |
def interactive_settings( | |
config, | |
context, | |
): | |
global _batch_count | |
settings = config.lcm_diffusion_setting | |
print("Enter generation settings (leave empty to use current value)") | |
print("> 1. Use LCM") | |
print("> 2. Use LCM-Lora") | |
print("> 3. Use OpenVINO") | |
option = user_value( | |
int, | |
"Select inference model option (1): ", | |
1, | |
) | |
if option not in range(1, 4): | |
print("Wrong inference model option! Falling back to defaults") | |
return | |
settings.use_lcm_lora = False | |
settings.use_openvino = False | |
if option == 1: | |
lcm_model_id = input(f"Enter LCM model ID ({settings.lcm_model_id}): ") | |
if lcm_model_id != "": | |
settings.lcm_model_id = lcm_model_id | |
elif option == 2: | |
settings.use_lcm_lora = True | |
lcm_lora_id = input( | |
f"Enter LCM-Lora model ID ({settings.lcm_lora.lcm_lora_id}): " | |
) | |
if lcm_lora_id != "": | |
settings.lcm_lora.lcm_lora_id = lcm_lora_id | |
base_model_id = input( | |
f"Enter Base model ID ({settings.lcm_lora.base_model_id}): " | |
) | |
if base_model_id != "": | |
settings.lcm_lora.base_model_id = base_model_id | |
elif option == 3: | |
settings.use_openvino = True | |
openvino_lcm_model_id = input( | |
f"Enter OpenVINO model ID ({settings.openvino_lcm_model_id}): " | |
) | |
if openvino_lcm_model_id != "": | |
settings.openvino_lcm_model_id = openvino_lcm_model_id | |
settings.use_offline_model = True | |
settings.use_tiny_auto_encoder = True | |
option = input("Work offline? (Y/n): ") | |
if option.upper() == "N": | |
settings.use_offline_model = False | |
option = input("Use Tiny Auto Encoder? (Y/n): ") | |
if option.upper() == "N": | |
settings.use_tiny_auto_encoder = False | |
settings.image_width = user_value( | |
int, | |
f"Image width ({settings.image_width}): ", | |
settings.image_width, | |
) | |
settings.image_height = user_value( | |
int, | |
f"Image height ({settings.image_height}): ", | |
settings.image_height, | |
) | |
settings.inference_steps = user_value( | |
int, | |
f"Inference steps ({settings.inference_steps}): ", | |
settings.inference_steps, | |
) | |
settings.guidance_scale = user_value( | |
float, | |
f"Guidance scale ({settings.guidance_scale}): ", | |
settings.guidance_scale, | |
) | |
settings.number_of_images = user_value( | |
int, | |
f"Number of images per batch ({settings.number_of_images}): ", | |
settings.number_of_images, | |
) | |
_batch_count = user_value( | |
int, | |
f"Batch count ({_batch_count}): ", | |
_batch_count, | |
) | |
# output_format = user_value(int, f"Output format (PNG)", 1) | |
print(config.lcm_diffusion_setting) | |
def interactive_txt2img( | |
config, | |
context, | |
): | |
global _batch_count | |
config.lcm_diffusion_setting.diffusion_task = DiffusionTask.text_to_image.value | |
user_input = input("Write a prompt (write 'exit' to quit): ") | |
while True: | |
if user_input == "exit": | |
return | |
elif user_input == "": | |
user_input = config.lcm_diffusion_setting.prompt | |
config.lcm_diffusion_setting.prompt = user_input | |
for i in range(0, _batch_count): | |
context.generate_text_to_image( | |
settings=config, | |
device=DEVICE, | |
) | |
if _edit_lora_settings: | |
interactive_lora( | |
config, | |
context, | |
) | |
user_input = input("Write a prompt: ") | |
def interactive_img2img( | |
config, | |
context, | |
): | |
global _batch_count | |
settings = config.lcm_diffusion_setting | |
settings.diffusion_task = DiffusionTask.image_to_image.value | |
steps = settings.inference_steps | |
source_path = input("Image path: ") | |
if source_path == "": | |
print("Error : You need to provide a file in img2img mode") | |
return | |
settings.strength = user_value( | |
float, | |
f"img2img strength ({settings.strength}): ", | |
settings.strength, | |
) | |
settings.inference_steps = int(steps / settings.strength + 1) | |
user_input = input("Write a prompt (write 'exit' to quit): ") | |
while True: | |
if user_input == "exit": | |
settings.inference_steps = steps | |
return | |
settings.init_image = Image.open(source_path) | |
settings.prompt = user_input | |
for i in range(0, _batch_count): | |
context.generate_text_to_image( | |
settings=config, | |
device=DEVICE, | |
) | |
new_path = input(f"Image path ({source_path}): ") | |
if new_path != "": | |
source_path = new_path | |
settings.strength = user_value( | |
float, | |
f"img2img strength ({settings.strength}): ", | |
settings.strength, | |
) | |
if _edit_lora_settings: | |
interactive_lora( | |
config, | |
context, | |
) | |
settings.inference_steps = int(steps / settings.strength + 1) | |
user_input = input("Write a prompt: ") | |
def interactive_variations( | |
config, | |
context, | |
): | |
global _batch_count | |
settings = config.lcm_diffusion_setting | |
settings.diffusion_task = DiffusionTask.image_to_image.value | |
steps = settings.inference_steps | |
source_path = input("Image path: ") | |
if source_path == "": | |
print("Error : You need to provide a file in Image variations mode") | |
return | |
settings.strength = user_value( | |
float, | |
f"Image variations strength ({settings.strength}): ", | |
settings.strength, | |
) | |
settings.inference_steps = int(steps / settings.strength + 1) | |
while True: | |
settings.init_image = Image.open(source_path) | |
settings.prompt = "" | |
for i in range(0, _batch_count): | |
generate_image_variations( | |
settings.init_image, | |
settings.strength, | |
) | |
if _edit_lora_settings: | |
interactive_lora( | |
config, | |
context, | |
) | |
user_input = input("Continue in Image variations mode? (Y/n): ") | |
if user_input.upper() == "N": | |
settings.inference_steps = steps | |
return | |
new_path = input(f"Image path ({source_path}): ") | |
if new_path != "": | |
source_path = new_path | |
settings.strength = user_value( | |
float, | |
f"Image variations strength ({settings.strength}): ", | |
settings.strength, | |
) | |
settings.inference_steps = int(steps / settings.strength + 1) | |
def interactive_edsr( | |
config, | |
context, | |
): | |
source_path = input("Image path: ") | |
if source_path == "": | |
print("Error : You need to provide a file in EDSR mode") | |
return | |
while True: | |
output_path = FastStableDiffusionPaths.get_upscale_filepath( | |
source_path, | |
2, | |
config.generated_images.format, | |
) | |
result = upscale_image( | |
context, | |
source_path, | |
output_path, | |
2, | |
) | |
user_input = input("Continue in EDSR upscale mode? (Y/n): ") | |
if user_input.upper() == "N": | |
return | |
new_path = input(f"Image path ({source_path}): ") | |
if new_path != "": | |
source_path = new_path | |
def interactive_sdupscale_settings(config): | |
steps = config.lcm_diffusion_setting.inference_steps | |
custom_settings = {} | |
print("> 1. Upscale whole image") | |
print("> 2. Define custom tiles (advanced)") | |
option = user_value( | |
int, | |
"Select an SD Upscale option (1): ", | |
1, | |
) | |
if option not in range(1, 3): | |
print("Wrong SD Upscale option!") | |
return | |
# custom_settings["source_file"] = args.file | |
custom_settings["source_file"] = "" | |
new_path = input(f"Input image path ({custom_settings['source_file']}): ") | |
if new_path != "": | |
custom_settings["source_file"] = new_path | |
if custom_settings["source_file"] == "": | |
print("Error : You need to provide a file in SD Upscale mode") | |
return | |
custom_settings["target_file"] = None | |
if option == 2: | |
custom_settings["target_file"] = input("Image to patch: ") | |
if custom_settings["target_file"] == "": | |
print("No target file provided, upscaling whole input image instead!") | |
custom_settings["target_file"] = None | |
option = 1 | |
custom_settings["output_format"] = config.generated_images.format | |
custom_settings["strength"] = user_value( | |
float, | |
f"SD Upscale strength ({config.lcm_diffusion_setting.strength}): ", | |
config.lcm_diffusion_setting.strength, | |
) | |
config.lcm_diffusion_setting.inference_steps = int( | |
steps / custom_settings["strength"] + 1 | |
) | |
if option == 1: | |
custom_settings["scale_factor"] = user_value( | |
float, | |
f"Scale factor (2.0): ", | |
2.0, | |
) | |
custom_settings["tile_size"] = user_value( | |
int, | |
f"Split input image into tiles of the following size, in pixels (256): ", | |
256, | |
) | |
custom_settings["tile_overlap"] = user_value( | |
int, | |
f"Tile overlap, in pixels (16): ", | |
16, | |
) | |
elif option == 2: | |
custom_settings["scale_factor"] = user_value( | |
float, | |
"Input image to Image-to-patch scale_factor (2.0): ", | |
2.0, | |
) | |
custom_settings["tile_size"] = 256 | |
custom_settings["tile_overlap"] = 16 | |
custom_settings["prompt"] = input( | |
"Write a prompt describing the input image (optional): " | |
) | |
custom_settings["tiles"] = [] | |
if option == 2: | |
add_tile = True | |
while add_tile: | |
print("=== Define custom SD Upscale tile ===") | |
tile_x = user_value( | |
int, | |
"Enter tile's X position: ", | |
0, | |
) | |
tile_y = user_value( | |
int, | |
"Enter tile's Y position: ", | |
0, | |
) | |
tile_w = user_value( | |
int, | |
"Enter tile's width (256): ", | |
256, | |
) | |
tile_h = user_value( | |
int, | |
"Enter tile's height (256): ", | |
256, | |
) | |
tile_scale = user_value( | |
float, | |
"Enter tile's scale factor (2.0): ", | |
2.0, | |
) | |
tile_prompt = input("Enter tile's prompt (optional): ") | |
custom_settings["tiles"].append( | |
{ | |
"x": tile_x, | |
"y": tile_y, | |
"w": tile_w, | |
"h": tile_h, | |
"mask_box": None, | |
"prompt": tile_prompt, | |
"scale_factor": tile_scale, | |
} | |
) | |
tile_option = input("Do you want to define another tile? (y/N): ") | |
if tile_option == "" or tile_option.upper() == "N": | |
add_tile = False | |
return custom_settings | |
def interactive_sdupscale( | |
config, | |
context, | |
): | |
settings = config.lcm_diffusion_setting | |
settings.diffusion_task = DiffusionTask.image_to_image.value | |
settings.init_image = "" | |
source_path = "" | |
steps = settings.inference_steps | |
while True: | |
custom_upscale_settings = None | |
option = input("Edit custom SD Upscale settings? (y/N): ") | |
if option.upper() == "Y": | |
config.lcm_diffusion_setting.inference_steps = steps | |
custom_upscale_settings = interactive_sdupscale_settings(config) | |
if not custom_upscale_settings: | |
return | |
source_path = custom_upscale_settings["source_file"] | |
else: | |
new_path = input(f"Image path ({source_path}): ") | |
if new_path != "": | |
source_path = new_path | |
if source_path == "": | |
print("Error : You need to provide a file in SD Upscale mode") | |
return | |
settings.strength = user_value( | |
float, | |
f"SD Upscale strength ({settings.strength}): ", | |
settings.strength, | |
) | |
settings.inference_steps = int(steps / settings.strength + 1) | |
output_path = FastStableDiffusionPaths.get_upscale_filepath( | |
source_path, | |
2, | |
config.generated_images.format, | |
) | |
generate_upscaled_image( | |
config, | |
source_path, | |
settings.strength, | |
upscale_settings=custom_upscale_settings, | |
context=context, | |
tile_overlap=32 if settings.use_openvino else 16, | |
output_path=output_path, | |
image_format=config.generated_images.format, | |
) | |
user_input = input("Continue in SD Upscale mode? (Y/n): ") | |
if user_input.upper() == "N": | |
settings.inference_steps = steps | |
return | |