Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -10,7 +10,6 @@ from PIL import Image
|
|
| 10 |
from pathlib import Path
|
| 11 |
from diffusers import StableDiffusionXLPipeline, EulerAncestralDiscreteScheduler
|
| 12 |
from huggingface_hub import hf_hub_download
|
| 13 |
-
from transformers import CLIPVisionModelWithProjection
|
| 14 |
from compel import Compel, ReturnedEmbeddingsType
|
| 15 |
|
| 16 |
# --- Configuration ---
|
|
@@ -50,6 +49,11 @@ UPSCALER_URL = os.environ.get("UPSCALER_URL", "https://huggingface.co/spaces/har
|
|
| 50 |
_char_cache = {}
|
| 51 |
|
| 52 |
# --- Helper Functions ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
def auto_res(input_image):
|
| 54 |
if input_image is None:
|
| 55 |
return gr.update(value=1024), gr.update(value=1024), gr.update(value="No image โ using default 1024 x 1024"), gr.update(value=None)
|
|
@@ -93,13 +97,6 @@ def encode_prompts(pipe, prompt, neg):
|
|
| 93 |
# --- Preloading Logic ---
|
| 94 |
def download_and_load_all():
|
| 95 |
print("๐ [Startup] Initializing Character Studio pre-load...")
|
| 96 |
-
|
| 97 |
-
print("โ๏ธ [Pipeline] Loading ViT-H Image Encoder for IP-Adapter...")
|
| 98 |
-
image_encoder = CLIPVisionModelWithProjection.from_pretrained(
|
| 99 |
-
"h94/IP-Adapter",
|
| 100 |
-
subfolder="models/image_encoder",
|
| 101 |
-
torch_dtype=torch.float16
|
| 102 |
-
)
|
| 103 |
|
| 104 |
for model_name in CIVITAI_MODELS.keys():
|
| 105 |
try:
|
|
@@ -116,19 +113,19 @@ def download_and_load_all():
|
|
| 116 |
)
|
| 117 |
|
| 118 |
if model_name not in _char_cache:
|
| 119 |
-
print(f"โ๏ธ [Pipeline] Building {model_name} with IP-Adapter...")
|
| 120 |
pipe = StableDiffusionXLPipeline.from_single_file(
|
| 121 |
str(dest),
|
| 122 |
torch_dtype=torch.float16,
|
| 123 |
-
use_safetensors=True
|
| 124 |
-
image_encoder=image_encoder
|
| 125 |
)
|
| 126 |
pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
|
| 127 |
|
|
|
|
| 128 |
pipe.load_ip_adapter(
|
| 129 |
"h94/IP-Adapter",
|
| 130 |
subfolder="sdxl_models",
|
| 131 |
-
weight_name="ip-
|
| 132 |
)
|
| 133 |
_char_cache[model_name] = pipe
|
| 134 |
|
|
@@ -140,6 +137,10 @@ def download_and_load_all():
|
|
| 140 |
# --- Inference ---
|
| 141 |
@spaces.GPU(duration=60)
|
| 142 |
def generate_gpu(model_name, ref_img, prompt, neg, seed, randomize, w, h, ip_scale, cfg, steps, progress=gr.Progress(track_tqdm=False)):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
if randomize: seed = random.randint(0, MAX_SEED)
|
| 144 |
yield None, seed, gr.update(value="Waiting for GPU..."), gr.update(visible=False, value=None)
|
| 145 |
|
|
@@ -148,25 +149,34 @@ def generate_gpu(model_name, ref_img, prompt, neg, seed, randomize, w, h, ip_sca
|
|
| 148 |
yield None, seed, gr.update(value=f"Model {model_name} not loaded."), gr.update(visible=False)
|
| 149 |
return
|
| 150 |
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
final_prompt = prompt
|
| 163 |
-
if "masterpiece" not in final_prompt.lower():
|
| 164 |
-
final_prompt = f"{prompt}, 1girl, solo, masterpiece, best quality, very aesthetic, absurdres"
|
| 165 |
|
| 166 |
-
|
|
|
|
|
|
|
| 167 |
|
| 168 |
-
|
| 169 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 170 |
image = pipe(
|
| 171 |
prompt_embeds=cond,
|
| 172 |
pooled_prompt_embeds=pool,
|
|
@@ -181,12 +191,14 @@ def generate_gpu(model_name, ref_img, prompt, neg, seed, randomize, w, h, ip_sca
|
|
| 181 |
callback_on_step_end=cb
|
| 182 |
).images[0]
|
| 183 |
|
| 184 |
-
|
| 185 |
-
torch.cuda.empty_cache()
|
| 186 |
-
yield None, seed, gr.update(value=f"Error: {str(e)[:120]}"), gr.update(visible=False)
|
| 187 |
-
return
|
| 188 |
|
| 189 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
|
| 191 |
# --- UI Setup ---
|
| 192 |
def build_css(accent="#7c3aed"):
|
|
@@ -229,7 +241,7 @@ with gr.Blocks(title="Character Lab", css=build_css(DEFAULT_ACCENT), theme=gr.th
|
|
| 229 |
<a href="{UPSCALER_URL}" target="_blank">Upscaler</a>
|
| 230 |
</div>
|
| 231 |
''')
|
| 232 |
-
gr.Markdown("# Character Lab\n**Maintain character consistency using IP-Adapter
|
| 233 |
|
| 234 |
gr.HTML("<p class='lbl'>Select Model</p>")
|
| 235 |
with gr.Row():
|
|
@@ -262,8 +274,8 @@ with gr.Blocks(title="Character Lab", css=build_css(DEFAULT_ACCENT), theme=gr.th
|
|
| 262 |
with gr.Accordion("Advanced Settings", open=False):
|
| 263 |
ch_neg = gr.Text(label="Negative Prompt", max_lines=2, value="lowres, bad anatomy, bad hands, text, watermark, worst quality")
|
| 264 |
|
| 265 |
-
#
|
| 266 |
-
ch_ip_scale = gr.Slider(label="Consistency Strength (IP-Scale)", minimum=0.1, maximum=1.2, step=0.05, value=0.
|
| 267 |
|
| 268 |
with gr.Row():
|
| 269 |
ch_seed = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=0)
|
|
|
|
| 10 |
from pathlib import Path
|
| 11 |
from diffusers import StableDiffusionXLPipeline, EulerAncestralDiscreteScheduler
|
| 12 |
from huggingface_hub import hf_hub_download
|
|
|
|
| 13 |
from compel import Compel, ReturnedEmbeddingsType
|
| 14 |
|
| 15 |
# --- Configuration ---
|
|
|
|
| 49 |
_char_cache = {}
|
| 50 |
|
| 51 |
# --- Helper Functions ---
|
| 52 |
+
def fix_vae_dtype(vae, dtype=torch.float16):
|
| 53 |
+
vae = vae.to(dtype=dtype)
|
| 54 |
+
vae.config.force_upcast = False
|
| 55 |
+
return vae
|
| 56 |
+
|
| 57 |
def auto_res(input_image):
|
| 58 |
if input_image is None:
|
| 59 |
return gr.update(value=1024), gr.update(value=1024), gr.update(value="No image โ using default 1024 x 1024"), gr.update(value=None)
|
|
|
|
| 97 |
# --- Preloading Logic ---
|
| 98 |
def download_and_load_all():
|
| 99 |
print("๐ [Startup] Initializing Character Studio pre-load...")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
|
| 101 |
for model_name in CIVITAI_MODELS.keys():
|
| 102 |
try:
|
|
|
|
| 113 |
)
|
| 114 |
|
| 115 |
if model_name not in _char_cache:
|
| 116 |
+
print(f"โ๏ธ [Pipeline] Building {model_name} with Standard IP-Adapter...")
|
| 117 |
pipe = StableDiffusionXLPipeline.from_single_file(
|
| 118 |
str(dest),
|
| 119 |
torch_dtype=torch.float16,
|
| 120 |
+
use_safetensors=True
|
|
|
|
| 121 |
)
|
| 122 |
pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
|
| 123 |
|
| 124 |
+
# SWITCH TO STANDARD IP-ADAPTER: Much better for Anime styles
|
| 125 |
pipe.load_ip_adapter(
|
| 126 |
"h94/IP-Adapter",
|
| 127 |
subfolder="sdxl_models",
|
| 128 |
+
weight_name="ip-adapter_sdxl.safetensors"
|
| 129 |
)
|
| 130 |
_char_cache[model_name] = pipe
|
| 131 |
|
|
|
|
| 137 |
# --- Inference ---
|
| 138 |
@spaces.GPU(duration=60)
|
| 139 |
def generate_gpu(model_name, ref_img, prompt, neg, seed, randomize, w, h, ip_scale, cfg, steps, progress=gr.Progress(track_tqdm=False)):
|
| 140 |
+
if ref_img is None:
|
| 141 |
+
yield None, seed, gr.update(value="โ ๏ธ Error: Please upload a Character Reference image first!"), gr.update(visible=False)
|
| 142 |
+
return
|
| 143 |
+
|
| 144 |
if randomize: seed = random.randint(0, MAX_SEED)
|
| 145 |
yield None, seed, gr.update(value="Waiting for GPU..."), gr.update(visible=False, value=None)
|
| 146 |
|
|
|
|
| 149 |
yield None, seed, gr.update(value=f"Model {model_name} not loaded."), gr.update(visible=False)
|
| 150 |
return
|
| 151 |
|
| 152 |
+
try:
|
| 153 |
+
# 1. GPU Prep & VAE Lock (Fixes distorted colors)
|
| 154 |
+
pipe.to("cuda")
|
| 155 |
+
pipe.vae = fix_vae_dtype(pipe.vae)
|
| 156 |
+
|
| 157 |
+
# 2. Disable the SDXL Watermarker (Prevents anime deep-frying)
|
| 158 |
+
pipe.watermark = None
|
| 159 |
|
| 160 |
+
pipe.set_ip_adapter_scale(float(ip_scale))
|
| 161 |
+
generator = torch.Generator("cuda").manual_seed(seed)
|
| 162 |
+
|
| 163 |
+
# 3. Auto-enhance Prompt
|
| 164 |
+
final_prompt = prompt or ""
|
| 165 |
+
if "masterpiece" not in final_prompt.lower():
|
| 166 |
+
final_prompt = f"{final_prompt}, 1girl, solo, masterpiece, best quality, very aesthetic, absurdres"
|
|
|
|
|
|
|
|
|
|
| 167 |
|
| 168 |
+
# 4. Auto-enhance Negative Prompt
|
| 169 |
+
base_neg = "lowres, (bad), text, error, missing, extra, worst quality, jpeg artifacts, bad anatomy, watermark, unfinished, displeasing, oldest, early, signature, extra digits, fewer digits, bad hands"
|
| 170 |
+
final_neg = f"{neg}, {base_neg}" if neg else base_neg
|
| 171 |
|
| 172 |
+
yield None, seed, gr.update(value=f"Generating {w}x{h}..."), gr.update(visible=False)
|
| 173 |
+
|
| 174 |
+
def cb(p, i, t, kw):
|
| 175 |
+
progress((i+1)/steps, desc=f"Step {i+1}/{steps}")
|
| 176 |
+
return kw
|
| 177 |
+
|
| 178 |
+
cond, pool, ncond, npool = encode_prompts(pipe, final_prompt, final_neg)
|
| 179 |
+
|
| 180 |
image = pipe(
|
| 181 |
prompt_embeds=cond,
|
| 182 |
pooled_prompt_embeds=pool,
|
|
|
|
| 191 |
callback_on_step_end=cb
|
| 192 |
).images[0]
|
| 193 |
|
| 194 |
+
yield image, seed, gr.update(value="โ
Done!"), gr.update(visible=True)
|
|
|
|
|
|
|
|
|
|
| 195 |
|
| 196 |
+
except Exception as e:
|
| 197 |
+
import traceback
|
| 198 |
+
traceback.print_exc()
|
| 199 |
+
torch.cuda.empty_cache()
|
| 200 |
+
yield None, seed, gr.update(value=f"โ Crash: {str(e)[:150]}"), gr.update(visible=False)
|
| 201 |
+
|
| 202 |
|
| 203 |
# --- UI Setup ---
|
| 204 |
def build_css(accent="#7c3aed"):
|
|
|
|
| 241 |
<a href="{UPSCALER_URL}" target="_blank">Upscaler</a>
|
| 242 |
</div>
|
| 243 |
''')
|
| 244 |
+
gr.Markdown("# Character Lab\n**Maintain character consistency using Standard IP-Adapter.**")
|
| 245 |
|
| 246 |
gr.HTML("<p class='lbl'>Select Model</p>")
|
| 247 |
with gr.Row():
|
|
|
|
| 274 |
with gr.Accordion("Advanced Settings", open=False):
|
| 275 |
ch_neg = gr.Text(label="Negative Prompt", max_lines=2, value="lowres, bad anatomy, bad hands, text, watermark, worst quality")
|
| 276 |
|
| 277 |
+
# Reset to a balanced default scale since Standard IP-Adapter is gentler
|
| 278 |
+
ch_ip_scale = gr.Slider(label="Consistency Strength (IP-Scale)", minimum=0.1, maximum=1.2, step=0.05, value=0.65)
|
| 279 |
|
| 280 |
with gr.Row():
|
| 281 |
ch_seed = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=0)
|