Update app.py
Browse files
app.py
CHANGED
|
@@ -4,18 +4,26 @@ from diffusers import StableDiffusionInpaintPipeline, AutoencoderKL
|
|
| 4 |
from diffusers import DPMSolverMultistepScheduler, PNDMScheduler
|
| 5 |
from controlnet_module import controlnet_processor
|
| 6 |
import torch
|
| 7 |
-
from PIL import Image, ImageDraw
|
| 8 |
import time
|
| 9 |
import os
|
| 10 |
import tempfile
|
| 11 |
import random
|
| 12 |
import re
|
| 13 |
|
| 14 |
-
# === FACE-FIX IMPORT (
|
| 15 |
try:
|
| 16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
FACEFIX_AVAILABLE = True
|
| 18 |
-
print("Face-Fix
|
| 19 |
except Exception as e:
|
| 20 |
print(f"Face-Fix nicht verfügbar: {e}")
|
| 21 |
FACEFIX_AVAILABLE = False
|
|
@@ -212,6 +220,61 @@ class ImageToImageProgressCallback:
|
|
| 212 |
self.progress(progress_val, desc="Generierung läuft...")
|
| 213 |
return callback_kwargs
|
| 214 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 215 |
# === HAUPTFUNKTION: TEXT ZU BILD MIT AUTOMATISCHEM FACE-FIX ===
|
| 216 |
def text_to_image(prompt, model_id, steps, guidance_scale, progress=gr.Progress()):
|
| 217 |
try:
|
|
@@ -248,9 +311,13 @@ def text_to_image(prompt, model_id, steps, guidance_scale, progress=gr.Progress(
|
|
| 248 |
|
| 249 |
# AUTOMATISCHER FACE-FIX NUR BEI PERSONEN
|
| 250 |
if FACEFIX_AVAILABLE and is_person_prompt(enhanced_prompt):
|
| 251 |
-
print("Person erkannt → Starte
|
| 252 |
progress(0.92, desc="Perfektioniere Gesicht & Hände...")
|
| 253 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 254 |
image = apply_facefix(
|
| 255 |
image=image,
|
| 256 |
prompt=enhanced_prompt,
|
|
@@ -258,13 +325,47 @@ def text_to_image(prompt, model_id, steps, guidance_scale, progress=gr.Progress(
|
|
| 258 |
seed=seed,
|
| 259 |
model_id=model_id
|
| 260 |
)
|
|
|
|
| 261 |
print("Face-Fix abgeschlossen!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 262 |
except Exception as e:
|
| 263 |
-
print(f"Face-Fix fehlgeschlagen (
|
| 264 |
|
| 265 |
duration = time.time() - start_time
|
| 266 |
config = MODEL_CONFIGS.get(model_id, {"name": model_id})
|
| 267 |
status_msg = f"Generiert mit {config.get('name', model_id)} in {duration:.1f}s"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 268 |
return image, status_msg
|
| 269 |
|
| 270 |
except Exception as e:
|
|
@@ -273,7 +374,6 @@ def text_to_image(prompt, model_id, steps, guidance_scale, progress=gr.Progress(
|
|
| 273 |
traceback.print_exc()
|
| 274 |
return None, f"Fehler: {str(e)}"
|
| 275 |
|
| 276 |
-
|
| 277 |
def img_to_image(image, prompt, neg_prompt, strength, steps, guidance_scale,
|
| 278 |
face_preserve, bbox_x1, bbox_y1, bbox_x2, bbox_y2,
|
| 279 |
progress=gr.Progress()):
|
|
@@ -289,12 +389,11 @@ def img_to_image(image, prompt, neg_prompt, strength, steps, guidance_scale,
|
|
| 289 |
print(f"Negativ-Prompt: {neg_prompt}")
|
| 290 |
print(f"Gesicht beibehalten: {face_preserve}")
|
| 291 |
|
| 292 |
-
|
| 293 |
-
# ===== NEU: AUTOMATISCHEN NEGATIV-PROMPT GENERIEREN =====
|
| 294 |
auto_negatives = auto_negative_prompt(prompt)
|
| 295 |
print(f"🤖 Automatisch generierter Negativ-Prompt: {auto_negatives}")
|
| 296 |
|
| 297 |
-
#
|
| 298 |
combined_negative_prompt = ""
|
| 299 |
|
| 300 |
if neg_prompt and neg_prompt.strip():
|
|
@@ -303,7 +402,6 @@ def img_to_image(image, prompt, neg_prompt, strength, steps, guidance_scale,
|
|
| 303 |
print(f"👤 Benutzer Negativ-Prompt: {user_neg}")
|
| 304 |
|
| 305 |
# Entferne Duplikate zwischen automatischen und manuellen Prompts
|
| 306 |
-
# Konvertiere beide in Sets für einfachen Duplikatvergleich
|
| 307 |
user_words = [word.strip().lower() for word in user_neg.split(",")]
|
| 308 |
auto_words = [word.strip().lower() for word in auto_negatives.split(",")]
|
| 309 |
|
|
@@ -315,7 +413,7 @@ def img_to_image(image, prompt, neg_prompt, strength, steps, guidance_scale,
|
|
| 315 |
if auto_word and auto_word not in user_words:
|
| 316 |
combined_words.append(auto_word)
|
| 317 |
|
| 318 |
-
# Zusammenfügen und Duplikate entfernen
|
| 319 |
unique_words = []
|
| 320 |
seen_words = set()
|
| 321 |
for word in combined_words:
|
|
@@ -330,8 +428,6 @@ def img_to_image(image, prompt, neg_prompt, strength, steps, guidance_scale,
|
|
| 330 |
print(f"ℹ️ Kein manueller Negativ-Prompt, verwende nur automatischen: {combined_negative_prompt}")
|
| 331 |
|
| 332 |
print(f"✅ Finaler kombinierter Negativ-Prompt: {combined_negative_prompt}")
|
| 333 |
-
# ===== ENDE DER NEUEN LOGIK =====
|
| 334 |
-
|
| 335 |
|
| 336 |
progress(0, desc="Starte Generierung mit ControlNet...")
|
| 337 |
|
|
@@ -415,6 +511,22 @@ def img_to_image(image, prompt, neg_prompt, strength, steps, guidance_scale,
|
|
| 415 |
print(f"🕒 Dauer: {end_time - start_time:.2f} Sekunden")
|
| 416 |
|
| 417 |
generated_image = result.images[0]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 418 |
return generated_image
|
| 419 |
|
| 420 |
except Exception as e:
|
|
@@ -423,24 +535,6 @@ def img_to_image(image, prompt, neg_prompt, strength, steps, guidance_scale,
|
|
| 423 |
traceback.print_exc()
|
| 424 |
return None
|
| 425 |
|
| 426 |
-
def update_bbox_from_image(image):
|
| 427 |
-
"""Aktualisiert die Bounding-Box-Koordinaten wenn ein Bild hochgeladen wird"""
|
| 428 |
-
if image is None:
|
| 429 |
-
return None, None, None, None
|
| 430 |
-
|
| 431 |
-
bbox = auto_detect_face_area(image)
|
| 432 |
-
return bbox[0], bbox[1], bbox[2], bbox[3]
|
| 433 |
-
|
| 434 |
-
def update_model_settings(model_id):
|
| 435 |
-
"""Aktualisiert die empfohlenen Einstellungen basierend auf Modellauswahl"""
|
| 436 |
-
config = MODEL_CONFIGS.get(model_id, MODEL_CONFIGS["runwayml/stable-diffusion-v1-5"])
|
| 437 |
-
|
| 438 |
-
return (
|
| 439 |
-
config["recommended_steps"], # steps
|
| 440 |
-
config["recommended_cfg"], # guidance_scale
|
| 441 |
-
f"📊 Empfohlene Einstellungen: {config['steps']} Steps, CFG {config['cfg']}"
|
| 442 |
-
)
|
| 443 |
-
|
| 444 |
def main_ui():
|
| 445 |
with gr.Blocks(
|
| 446 |
title="AI Image Generator",
|
|
@@ -535,6 +629,15 @@ def main_ui():
|
|
| 535 |
color: #721c24;
|
| 536 |
border: 1px solid #f5c6cb;
|
| 537 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 538 |
"""
|
| 539 |
) as demo:
|
| 540 |
|
|
@@ -542,6 +645,16 @@ def main_ui():
|
|
| 542 |
with gr.Tab("Text zu Bild"):
|
| 543 |
gr.Markdown("## 🎨 Text zu Bild Generator")
|
| 544 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 545 |
with gr.Row():
|
| 546 |
with gr.Column(scale=2):
|
| 547 |
# Modellauswahl Dropdown (NUR 2 MODELLE)
|
|
@@ -567,7 +680,7 @@ def main_ui():
|
|
| 567 |
|
| 568 |
with gr.Column(scale=3):
|
| 569 |
txt_input = gr.Textbox(
|
| 570 |
-
placeholder="z.B. ultra realistic
|
| 571 |
lines=3,
|
| 572 |
label="🎯 Prompt (Englisch)",
|
| 573 |
info="Beschreibe detailliert, was du sehen möchtest. Negative Prompts werden automatisch generiert."
|
|
|
|
| 4 |
from diffusers import DPMSolverMultistepScheduler, PNDMScheduler
|
| 5 |
from controlnet_module import controlnet_processor
|
| 6 |
import torch
|
| 7 |
+
from PIL import Image, ImageDraw, ImageFont
|
| 8 |
import time
|
| 9 |
import os
|
| 10 |
import tempfile
|
| 11 |
import random
|
| 12 |
import re
|
| 13 |
|
| 14 |
+
# === FACE-FIX IMPORT (korrigierte Version) ===
|
| 15 |
try:
|
| 16 |
+
# Versuche verschiedene Import-Möglichkeiten
|
| 17 |
+
try:
|
| 18 |
+
from controlnet_facefix import apply_facefix
|
| 19 |
+
except ImportError:
|
| 20 |
+
# Alternative Import-Methode
|
| 21 |
+
import sys
|
| 22 |
+
sys.path.append(".")
|
| 23 |
+
from controlnet_facefix import apply_facefix
|
| 24 |
+
|
| 25 |
FACEFIX_AVAILABLE = True
|
| 26 |
+
print("Face-Fix erfolgreich geladen")
|
| 27 |
except Exception as e:
|
| 28 |
print(f"Face-Fix nicht verfügbar: {e}")
|
| 29 |
FACEFIX_AVAILABLE = False
|
|
|
|
| 220 |
self.progress(progress_val, desc="Generierung läuft...")
|
| 221 |
return callback_kwargs
|
| 222 |
|
| 223 |
+
# === BILDVERARBEITUNGS-FUNKTIONEN FÜR BILD-TO-BILD TAB ===
|
| 224 |
+
def process_image_upload(image):
|
| 225 |
+
"""Verarbeitet den Bild-Upload und aktualisiert die Koordinaten und Vorschau"""
|
| 226 |
+
if image is None:
|
| 227 |
+
return None, 100, 100, 300, 300
|
| 228 |
+
|
| 229 |
+
# Automatische Gesichtserkennung
|
| 230 |
+
bbox = auto_detect_face_area(image)
|
| 231 |
+
|
| 232 |
+
# Live-Vorschau erstellen
|
| 233 |
+
preview = update_live_preview(image, bbox[0], bbox[1], bbox[2], bbox[3], True)
|
| 234 |
+
|
| 235 |
+
return preview, bbox[0], bbox[1], bbox[2], bbox[3]
|
| 236 |
+
|
| 237 |
+
def update_live_preview(image, x1, y1, x2, y2, face_preserve):
|
| 238 |
+
"""Erstellt eine Live-Vorschau mit farbigem Rahmen basierend auf dem Modus"""
|
| 239 |
+
if image is None:
|
| 240 |
+
return None
|
| 241 |
+
|
| 242 |
+
# Kopie des Bildes für Vorschau
|
| 243 |
+
preview = image.copy()
|
| 244 |
+
|
| 245 |
+
# Zeichne Rahmen basierend auf Modus
|
| 246 |
+
draw = ImageDraw.Draw(preview)
|
| 247 |
+
|
| 248 |
+
# Rahmenfarbe basierend auf Modus
|
| 249 |
+
if face_preserve:
|
| 250 |
+
# 🟢 GRÜN: Alles außerhalb des Rahmens wird verändert
|
| 251 |
+
outline_color = "green"
|
| 252 |
+
else:
|
| 253 |
+
# 🔴 ROT: Nur innerhalb des Rahmens wird verändert
|
| 254 |
+
outline_color = "red"
|
| 255 |
+
|
| 256 |
+
# Rahmen zeichnen
|
| 257 |
+
draw.rectangle([x1, y1, x2, y2], outline=outline_color, width=3)
|
| 258 |
+
|
| 259 |
+
# Hinweis-Text hinzufügen
|
| 260 |
+
try:
|
| 261 |
+
# Versuche, eine Schriftart zu laden (falls verfügbar)
|
| 262 |
+
try:
|
| 263 |
+
font = ImageFont.truetype("arial.ttf", 16)
|
| 264 |
+
except:
|
| 265 |
+
font = ImageFont.load_default()
|
| 266 |
+
|
| 267 |
+
text = f"{'🟢 Schutzmodus AN' if face_preserve else '🔴 Schutzmodus AUS'}"
|
| 268 |
+
# Text-Hintergrund
|
| 269 |
+
text_bbox = draw.textbbox((x1, y1 - 25), text, font=font)
|
| 270 |
+
draw.rectangle(text_bbox, fill="white")
|
| 271 |
+
# Text
|
| 272 |
+
draw.text((x1, y1 - 25), text, fill=outline_color, font=font)
|
| 273 |
+
except:
|
| 274 |
+
pass # Falls Schrift nicht geladen werden kann
|
| 275 |
+
|
| 276 |
+
return preview
|
| 277 |
+
|
| 278 |
# === HAUPTFUNKTION: TEXT ZU BILD MIT AUTOMATISCHEM FACE-FIX ===
|
| 279 |
def text_to_image(prompt, model_id, steps, guidance_scale, progress=gr.Progress()):
|
| 280 |
try:
|
|
|
|
| 311 |
|
| 312 |
# AUTOMATISCHER FACE-FIX NUR BEI PERSONEN
|
| 313 |
if FACEFIX_AVAILABLE and is_person_prompt(enhanced_prompt):
|
| 314 |
+
print("Person erkannt → Starte Face-Fix für perfekte Gesichter...")
|
| 315 |
progress(0.92, desc="Perfektioniere Gesicht & Hände...")
|
| 316 |
try:
|
| 317 |
+
# Originalbild speichern für Vergleich
|
| 318 |
+
original_image = image.copy()
|
| 319 |
+
|
| 320 |
+
# Face-Fix anwenden
|
| 321 |
image = apply_facefix(
|
| 322 |
image=image,
|
| 323 |
prompt=enhanced_prompt,
|
|
|
|
| 325 |
seed=seed,
|
| 326 |
model_id=model_id
|
| 327 |
)
|
| 328 |
+
|
| 329 |
print("Face-Fix abgeschlossen!")
|
| 330 |
+
|
| 331 |
+
# Optional: Vergleichsbild erstellen
|
| 332 |
+
try:
|
| 333 |
+
width, height = image.size
|
| 334 |
+
comparison = Image.new('RGB', (width * 2, height))
|
| 335 |
+
comparison.paste(original_image, (0, 0))
|
| 336 |
+
comparison.paste(image, (width, 0))
|
| 337 |
+
|
| 338 |
+
# Trennlinie
|
| 339 |
+
draw = ImageDraw.Draw(comparison)
|
| 340 |
+
draw.line([(width, 0), (width, height)], fill="white", width=2)
|
| 341 |
+
|
| 342 |
+
# Beschriftung hinzufügen
|
| 343 |
+
try:
|
| 344 |
+
font = ImageFont.truetype("arial.ttf", 20)
|
| 345 |
+
except:
|
| 346 |
+
font = ImageFont.load_default()
|
| 347 |
+
|
| 348 |
+
draw.text((10, 10), "Vor Face-Fix", fill="white", font=font)
|
| 349 |
+
draw.text((width + 10, 10), "Nach Face-Fix", fill="white", font=font)
|
| 350 |
+
|
| 351 |
+
# Vergleichsbild als Option zurückgeben
|
| 352 |
+
image = comparison
|
| 353 |
+
|
| 354 |
+
except Exception as e:
|
| 355 |
+
print(f"Vergleichsbild konnte nicht erstellt werden: {e}")
|
| 356 |
+
# Bei Fehler einfach das verbesserte Bild verwenden
|
| 357 |
+
|
| 358 |
except Exception as e:
|
| 359 |
+
print(f"Face-Fix fehlgeschlagen (Original bleibt): {e}")
|
| 360 |
|
| 361 |
duration = time.time() - start_time
|
| 362 |
config = MODEL_CONFIGS.get(model_id, {"name": model_id})
|
| 363 |
status_msg = f"Generiert mit {config.get('name', model_id)} in {duration:.1f}s"
|
| 364 |
+
|
| 365 |
+
# Face-Fix Info hinzufügen
|
| 366 |
+
if FACEFIX_AVAILABLE and is_person_prompt(enhanced_prompt):
|
| 367 |
+
status_msg += " + Face-Fix angewendet"
|
| 368 |
+
|
| 369 |
return image, status_msg
|
| 370 |
|
| 371 |
except Exception as e:
|
|
|
|
| 374 |
traceback.print_exc()
|
| 375 |
return None, f"Fehler: {str(e)}"
|
| 376 |
|
|
|
|
| 377 |
def img_to_image(image, prompt, neg_prompt, strength, steps, guidance_scale,
|
| 378 |
face_preserve, bbox_x1, bbox_y1, bbox_x2, bbox_y2,
|
| 379 |
progress=gr.Progress()):
|
|
|
|
| 389 |
print(f"Negativ-Prompt: {neg_prompt}")
|
| 390 |
print(f"Gesicht beibehalten: {face_preserve}")
|
| 391 |
|
| 392 |
+
# Automatischen Negativ-Prompt generieren
|
|
|
|
| 393 |
auto_negatives = auto_negative_prompt(prompt)
|
| 394 |
print(f"🤖 Automatisch generierter Negativ-Prompt: {auto_negatives}")
|
| 395 |
|
| 396 |
+
# Kombiniere manuellen und automatischen Prompt
|
| 397 |
combined_negative_prompt = ""
|
| 398 |
|
| 399 |
if neg_prompt and neg_prompt.strip():
|
|
|
|
| 402 |
print(f"👤 Benutzer Negativ-Prompt: {user_neg}")
|
| 403 |
|
| 404 |
# Entferne Duplikate zwischen automatischen und manuellen Prompts
|
|
|
|
| 405 |
user_words = [word.strip().lower() for word in user_neg.split(",")]
|
| 406 |
auto_words = [word.strip().lower() for word in auto_negatives.split(",")]
|
| 407 |
|
|
|
|
| 413 |
if auto_word and auto_word not in user_words:
|
| 414 |
combined_words.append(auto_word)
|
| 415 |
|
| 416 |
+
# Zusammenfügen und Duplikate entfernen
|
| 417 |
unique_words = []
|
| 418 |
seen_words = set()
|
| 419 |
for word in combined_words:
|
|
|
|
| 428 |
print(f"ℹ️ Kein manueller Negativ-Prompt, verwende nur automatischen: {combined_negative_prompt}")
|
| 429 |
|
| 430 |
print(f"✅ Finaler kombinierter Negativ-Prompt: {combined_negative_prompt}")
|
|
|
|
|
|
|
| 431 |
|
| 432 |
progress(0, desc="Starte Generierung mit ControlNet...")
|
| 433 |
|
|
|
|
| 511 |
print(f"🕒 Dauer: {end_time - start_time:.2f} Sekunden")
|
| 512 |
|
| 513 |
generated_image = result.images[0]
|
| 514 |
+
|
| 515 |
+
# Optional: Face-Fix auch auf das transformierte Bild anwenden
|
| 516 |
+
if FACEFIX_AVAILABLE and is_person_prompt(prompt):
|
| 517 |
+
print("Transformiertes Bild → Wende Face-Fix an...")
|
| 518 |
+
try:
|
| 519 |
+
generated_image = apply_facefix(
|
| 520 |
+
image=generated_image,
|
| 521 |
+
prompt=prompt,
|
| 522 |
+
negative_prompt=combined_negative_prompt,
|
| 523 |
+
seed=seed,
|
| 524 |
+
model_id="runwayml/stable-diffusion-v1-5"
|
| 525 |
+
)
|
| 526 |
+
print("Face-Fix auf transformiertem Bild abgeschlossen!")
|
| 527 |
+
except Exception as e:
|
| 528 |
+
print(f"Face-Fix auf transformiertem Bild fehlgeschlagen: {e}")
|
| 529 |
+
|
| 530 |
return generated_image
|
| 531 |
|
| 532 |
except Exception as e:
|
|
|
|
| 535 |
traceback.print_exc()
|
| 536 |
return None
|
| 537 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 538 |
def main_ui():
|
| 539 |
with gr.Blocks(
|
| 540 |
title="AI Image Generator",
|
|
|
|
| 629 |
color: #721c24;
|
| 630 |
border: 1px solid #f5c6cb;
|
| 631 |
}
|
| 632 |
+
.face-fix-badge {
|
| 633 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 634 |
+
color: white;
|
| 635 |
+
padding: 4px 8px;
|
| 636 |
+
border-radius: 12px;
|
| 637 |
+
font-size: 12px;
|
| 638 |
+
margin-left: 10px;
|
| 639 |
+
display: inline-block;
|
| 640 |
+
}
|
| 641 |
"""
|
| 642 |
) as demo:
|
| 643 |
|
|
|
|
| 645 |
with gr.Tab("Text zu Bild"):
|
| 646 |
gr.Markdown("## 🎨 Text zu Bild Generator")
|
| 647 |
|
| 648 |
+
# Face-Fix Info Badge
|
| 649 |
+
if FACEFIX_AVAILABLE:
|
| 650 |
+
gr.Markdown(
|
| 651 |
+
f"""
|
| 652 |
+
<div style="background: #e3f2fd; padding: 10px; border-radius: 8px; margin-bottom: 20px; border-left: 4px solid #2196f3;">
|
| 653 |
+
🎭 <strong>Face-Fix aktiviert!</strong> Gesichter werden automatisch verbessert.
|
| 654 |
+
</div>
|
| 655 |
+
"""
|
| 656 |
+
)
|
| 657 |
+
|
| 658 |
with gr.Row():
|
| 659 |
with gr.Column(scale=2):
|
| 660 |
# Modellauswahl Dropdown (NUR 2 MODELLE)
|
|
|
|
| 680 |
|
| 681 |
with gr.Column(scale=3):
|
| 682 |
txt_input = gr.Textbox(
|
| 683 |
+
placeholder="z.B. ultra realistic portrait of a beautiful woman with detailed skin, perfect eyes, sharp focus, cinematic lighting",
|
| 684 |
lines=3,
|
| 685 |
label="🎯 Prompt (Englisch)",
|
| 686 |
info="Beschreibe detailliert, was du sehen möchtest. Negative Prompts werden automatisch generiert."
|