Upload app.py
Browse files
app.py
CHANGED
|
@@ -9,8 +9,8 @@ from PIL import Image
|
|
| 9 |
|
| 10 |
from huggingface_hub import hf_hub_download, snapshot_download
|
| 11 |
from insightface.app import FaceAnalysis
|
| 12 |
-
from diffusers import
|
| 13 |
-
from diffusers.
|
| 14 |
|
| 15 |
# ==============================================================================
|
| 16 |
# 1. SETUP E INIZIALIZZAZIONE GLOBALE
|
|
@@ -25,72 +25,69 @@ def download_antelopev2():
|
|
| 25 |
if not os.path.exists(model_dir) or len(os.listdir(model_dir)) < 5:
|
| 26 |
print("Scaricamento dei modelli antelopev2 in corso...")
|
| 27 |
os.makedirs(model_dir, exist_ok=True)
|
| 28 |
-
# Scarica da un repository pubblico affidabile
|
| 29 |
snapshot_download(repo_id="DIAMONIK7777/antelopev2", local_dir=model_dir)
|
| 30 |
print("Scaricamento completato.")
|
| 31 |
-
# Insightface aggiunge in automatico "/models/antelopev2" alla root che gli passiamo.
|
| 32 |
-
# Quindi la root corretta da passargli è "./", così cercherà in "./models/antelopev2".
|
| 33 |
return "./"
|
| 34 |
|
| 35 |
model_root = download_antelopev2()
|
| 36 |
|
| 37 |
-
|
| 38 |
-
|
| 39 |
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
canny_path = "diffusers/controlnet-canny-sdxl-1.0"
|
| 43 |
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
subfolder="ControlNetModel",
|
| 49 |
-
torch_dtype=dtype,
|
| 50 |
use_safetensors=True
|
| 51 |
)
|
| 52 |
-
controlnet_logo = ControlNetModel.from_pretrained(canny_path, torch_dtype=dtype, use_safetensors=True)
|
| 53 |
-
controlnet = MultiControlNetModel([controlnet_identity, controlnet_logo])
|
| 54 |
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
|
|
|
| 58 |
controlnet=controlnet,
|
| 59 |
torch_dtype=dtype,
|
| 60 |
use_safetensors=True
|
| 61 |
)
|
| 62 |
-
pipe.to(device)
|
| 63 |
|
|
|
|
| 64 |
print("Caricamento Face Adapter InstantID...")
|
| 65 |
-
pipe.
|
|
|
|
|
|
|
|
|
|
| 66 |
|
| 67 |
|
| 68 |
# ==============================================================================
|
| 69 |
-
# 2. CONFIGURAZIONE STILI
|
| 70 |
# ==============================================================================
|
| 71 |
|
| 72 |
-
# Invece di file locali, usiamo i repo ID di modelli pubblici gratuiti su Hugging Face
|
| 73 |
STYLES = {
|
| 74 |
"Stile 1: Bit-Builder (8-Bit/Pixel Art)": {
|
| 75 |
-
"repo_id": "nerijs/pixel-art-xl",
|
| 76 |
"weight_name": "pixel-art-xl.safetensors",
|
| 77 |
"prompt": "pixelart style, Pixel art portrait of a tech builder, indie hacker typing on a retro mechanical keyboard. He is wearing a jacket with a small green, white, and red square patch on the chest. Cyberpunk desk setup, glowing monitors displaying code. High quality, 8-bit game style.",
|
| 78 |
"negative_prompt": "ugly, deformed, lowres, bad anatomy, bad hands, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, watermark, text"
|
| 79 |
},
|
| 80 |
"Stile 2: Il Costruttore Rinascimentale": {
|
| 81 |
-
"repo_id": "davidmoref/sdxl-lora-adapter-renaissance",
|
| 82 |
"weight_name": "pytorch_lora_weights.safetensors",
|
| 83 |
"prompt": "renaissance oil painting, A masterpiece renaissance oil painting of an Italian genius inventor in the style of Leonardo da Vinci. The man is holding a wooden prototype. He is wearing elegant renaissance dark clothes with a subtle green, white and red enameled pin on the lapel. Architectural blueprints on a rustic wooden table. Chiaroscuro lighting.",
|
| 84 |
"negative_prompt": "ugly, deformed, modern, photography, bad anatomy, worst quality, low quality, watermark, text"
|
| 85 |
},
|
| 86 |
"Stile 3: L'Architetto del Futuro (Design Geometrico)": {
|
| 87 |
-
"repo_id": "goofyai/flat_illustration_sdxl",
|
| 88 |
"weight_name": "flat_illustration_sdxl.safetensors",
|
| 89 |
"prompt": "flat vector art, Modern flat vector illustration of a tech architect holding a smartphone. He wears a minimalist grey suit jacket featuring a small geometric green, white, and red enamel badge. Clean geometric lines, industrial design background, minimalistic.",
|
| 90 |
"negative_prompt": "ugly, deformed, photorealistic, 3d render, bad anatomy, worst quality, low quality, watermark, text"
|
| 91 |
},
|
| 92 |
"Stile 4: Genio Creativo (Pop-Art)": {
|
| 93 |
-
"repo_id": None,
|
| 94 |
"weight_name": None,
|
| 95 |
"prompt": "vintage pop art, Comic book style pop-art portrait of a creative maker. Vibrant colors. He wears a trendy jacket adorned with an 'Italian Builders' custom patch in green, white and red. Halftone dots, vintage poster aesthetic.",
|
| 96 |
"negative_prompt": "ugly, deformed, photorealistic, bad anatomy, worst quality, low quality, watermark, extra text"
|
|
@@ -102,7 +99,7 @@ STYLES = {
|
|
| 102 |
# ==============================================================================
|
| 103 |
|
| 104 |
def draw_kps(image_pil, kps, color_list=[(255,0,0), (0,255,0), (0,0,255), (255,255,0), (255,0,255)]):
|
| 105 |
-
"""Genera keypoints per InstantID."""
|
| 106 |
stickwidth = 4
|
| 107 |
limbSeq = np.array([[0, 2], [1, 2], [3, 2], [4, 2]])
|
| 108 |
kps = np.array(kps)
|
|
@@ -127,59 +124,71 @@ def draw_kps(image_pil, kps, color_list=[(255,0,0), (0,255,0), (0,0,255), (255,2
|
|
| 127 |
|
| 128 |
return Image.fromarray(out_img.astype(np.uint8))
|
| 129 |
|
| 130 |
-
def
|
| 131 |
-
"""
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
|
| 149 |
@spaces.GPU(duration=120)
|
| 150 |
def generate_avatar(user_image, logo_image, selected_style):
|
| 151 |
if user_image is None:
|
| 152 |
raise gr.Error("Per favore, carica un'immagine del volto.")
|
| 153 |
-
if logo_image is None:
|
| 154 |
-
raise gr.Error("Per favore, carica l'immagine del logo.")
|
| 155 |
|
| 156 |
# 1. Analisi del volto
|
| 157 |
cv_image = cv2.cvtColor(np.array(user_image), cv2.COLOR_RGB2BGR)
|
| 158 |
-
faces =
|
| 159 |
if len(faces) == 0:
|
| 160 |
raise gr.Error("Nessun volto rilevato. Riprova con un'altra foto in cui il volto sia ben visibile.")
|
| 161 |
-
|
| 162 |
face = sorted(faces, key=lambda x: (x.bbox[2]-x.bbox[0])*(x.bbox[3]-x.bbox[1]))[-1]
|
| 163 |
face_emb = face.normed_embedding
|
| 164 |
-
|
| 165 |
-
|
|
|
|
| 166 |
user_image_resized = user_image.resize((1024, 1024))
|
| 167 |
-
faces_resized =
|
| 168 |
if len(faces_resized) > 0:
|
| 169 |
face_kps = sorted(faces_resized, key=lambda x: (x.bbox[2]-x.bbox[0])*(x.bbox[3]-x.bbox[1]))[-1].kps
|
| 170 |
-
|
| 171 |
-
face_kps = face.kps * (1024 / max(user_image.size)) # Approssimazione se fallisce il resize
|
| 172 |
-
|
| 173 |
kps_image = draw_kps(user_image_resized, face_kps)
|
| 174 |
-
|
| 175 |
-
# 2.
|
| 176 |
-
# Il colore originale viene ignorato, prendiamo solo la sagoma (Canny edges)
|
| 177 |
-
logo_canny_image = process_logo_canny(logo_image)
|
| 178 |
-
|
| 179 |
-
# 3. Gestione LoRA dinamico da Hugging Face
|
| 180 |
style_config = STYLES[selected_style]
|
| 181 |
repo_id = style_config["repo_id"]
|
| 182 |
-
|
| 183 |
if repo_id:
|
| 184 |
try:
|
| 185 |
print(f"Scaricamento/Caricamento LoRA {repo_id}...")
|
|
@@ -187,30 +196,32 @@ def generate_avatar(user_image, logo_image, selected_style):
|
|
| 187 |
except Exception as e:
|
| 188 |
print(f"Errore caricamento LoRA da HF: {e}")
|
| 189 |
|
| 190 |
-
#
|
| 191 |
prompt = style_config["prompt"]
|
| 192 |
negative_prompt = style_config["negative_prompt"]
|
| 193 |
-
|
| 194 |
-
print("Inizio generazione
|
|
|
|
| 195 |
image = pipe(
|
| 196 |
prompt=prompt,
|
| 197 |
negative_prompt=negative_prompt,
|
| 198 |
image_embeds=face_emb,
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
# Bilanciamo l'influenza: 0.8 per il volto, 0.4 per il logo (abbastanza per imprimere la forma ma fuso)
|
| 202 |
-
controlnet_conditioning_scale=[0.8, 0.4],
|
| 203 |
-
ip_adapter_scale=0.8,
|
| 204 |
num_inference_steps=30,
|
| 205 |
guidance_scale=5.0,
|
| 206 |
).images[0]
|
| 207 |
-
|
| 208 |
if repo_id:
|
| 209 |
try:
|
| 210 |
pipe.unload_lora_weights()
|
| 211 |
except:
|
| 212 |
pass
|
| 213 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 214 |
return image
|
| 215 |
|
| 216 |
# ==============================================================================
|
|
@@ -234,12 +245,12 @@ with gr.Blocks(css=custom_css, theme=theme) as demo:
|
|
| 234 |
with gr.Column(elem_classes="container"):
|
| 235 |
gr.HTML("<h1 class='header-title'>🇮🇹 Italian Builders Avatar Generator 🇮🇹</h1>")
|
| 236 |
gr.HTML("<p class='header-subtitle'>Trasforma il tuo selfie. Il tuo brand, la tua identità.</p>")
|
| 237 |
-
|
| 238 |
with gr.Row():
|
| 239 |
with gr.Column(scale=1):
|
| 240 |
input_image = gr.Image(type="pil", label="1. Carica il tuo Selfie")
|
| 241 |
-
logo_image = gr.Image(type="pil", label="2. Carica il Logo (
|
| 242 |
-
|
| 243 |
with gr.Column(scale=1):
|
| 244 |
style_selector = gr.Radio(
|
| 245 |
choices=list(STYLES.keys()),
|
|
@@ -248,10 +259,10 @@ with gr.Blocks(css=custom_css, theme=theme) as demo:
|
|
| 248 |
)
|
| 249 |
gr.Markdown("<br>")
|
| 250 |
generate_button = gr.Button("Genera il tuo Avatar 🚀", elem_classes="generate-btn", variant="primary")
|
| 251 |
-
|
| 252 |
with gr.Column(scale=1):
|
| 253 |
output_image = gr.Image(label="Il tuo Avatar Builder", interactive=False)
|
| 254 |
-
|
| 255 |
generate_button.click(
|
| 256 |
fn=generate_avatar,
|
| 257 |
inputs=[input_image, logo_image, style_selector],
|
|
|
|
| 9 |
|
| 10 |
from huggingface_hub import hf_hub_download, snapshot_download
|
| 11 |
from insightface.app import FaceAnalysis
|
| 12 |
+
from diffusers import ControlNetModel
|
| 13 |
+
from diffusers.pipelines import StableDiffusionXLInstantIDPipeline
|
| 14 |
|
| 15 |
# ==============================================================================
|
| 16 |
# 1. SETUP E INIZIALIZZAZIONE GLOBALE
|
|
|
|
| 25 |
if not os.path.exists(model_dir) or len(os.listdir(model_dir)) < 5:
|
| 26 |
print("Scaricamento dei modelli antelopev2 in corso...")
|
| 27 |
os.makedirs(model_dir, exist_ok=True)
|
|
|
|
| 28 |
snapshot_download(repo_id="DIAMONIK7777/antelopev2", local_dir=model_dir)
|
| 29 |
print("Scaricamento completato.")
|
|
|
|
|
|
|
| 30 |
return "./"
|
| 31 |
|
| 32 |
model_root = download_antelopev2()
|
| 33 |
|
| 34 |
+
face_app = FaceAnalysis(name='antelopev2', root=model_root, providers=['CPUExecutionProvider'])
|
| 35 |
+
face_app.prepare(ctx_id=0, det_size=(640, 640))
|
| 36 |
|
| 37 |
+
# Scarica il file ip-adapter.bin di InstantID
|
| 38 |
+
face_adapter = hf_hub_download(repo_id="InstantX/InstantID", filename="ip-adapter.bin")
|
|
|
|
| 39 |
|
| 40 |
+
# Carica il ControlNet (IdentityNet) di InstantID
|
| 41 |
+
print("Caricamento ControlNet InstantID...")
|
| 42 |
+
controlnet = ControlNetModel.from_pretrained(
|
| 43 |
+
"InstantX/InstantID",
|
| 44 |
+
subfolder="ControlNetModel",
|
| 45 |
+
torch_dtype=dtype,
|
| 46 |
use_safetensors=True
|
| 47 |
)
|
|
|
|
|
|
|
| 48 |
|
| 49 |
+
# Carica la pipeline dedicata InstantID (NON quella generica ControlNet)
|
| 50 |
+
print("Caricamento Pipeline SDXL InstantID...")
|
| 51 |
+
pipe = StableDiffusionXLInstantIDPipeline.from_pretrained(
|
| 52 |
+
"stabilityai/stable-diffusion-xl-base-1.0",
|
| 53 |
controlnet=controlnet,
|
| 54 |
torch_dtype=dtype,
|
| 55 |
use_safetensors=True
|
| 56 |
)
|
|
|
|
| 57 |
|
| 58 |
+
# Carica il Face Adapter con il metodo specifico di InstantID
|
| 59 |
print("Caricamento Face Adapter InstantID...")
|
| 60 |
+
pipe.load_ip_adapter_instantid(face_adapter)
|
| 61 |
+
pipe.to(device)
|
| 62 |
+
|
| 63 |
+
print("Pipeline pronta!")
|
| 64 |
|
| 65 |
|
| 66 |
# ==============================================================================
|
| 67 |
+
# 2. CONFIGURAZIONE STILI
|
| 68 |
# ==============================================================================
|
| 69 |
|
|
|
|
| 70 |
STYLES = {
|
| 71 |
"Stile 1: Bit-Builder (8-Bit/Pixel Art)": {
|
| 72 |
+
"repo_id": "nerijs/pixel-art-xl",
|
| 73 |
"weight_name": "pixel-art-xl.safetensors",
|
| 74 |
"prompt": "pixelart style, Pixel art portrait of a tech builder, indie hacker typing on a retro mechanical keyboard. He is wearing a jacket with a small green, white, and red square patch on the chest. Cyberpunk desk setup, glowing monitors displaying code. High quality, 8-bit game style.",
|
| 75 |
"negative_prompt": "ugly, deformed, lowres, bad anatomy, bad hands, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, watermark, text"
|
| 76 |
},
|
| 77 |
"Stile 2: Il Costruttore Rinascimentale": {
|
| 78 |
+
"repo_id": "davidmoref/sdxl-lora-adapter-renaissance",
|
| 79 |
"weight_name": "pytorch_lora_weights.safetensors",
|
| 80 |
"prompt": "renaissance oil painting, A masterpiece renaissance oil painting of an Italian genius inventor in the style of Leonardo da Vinci. The man is holding a wooden prototype. He is wearing elegant renaissance dark clothes with a subtle green, white and red enameled pin on the lapel. Architectural blueprints on a rustic wooden table. Chiaroscuro lighting.",
|
| 81 |
"negative_prompt": "ugly, deformed, modern, photography, bad anatomy, worst quality, low quality, watermark, text"
|
| 82 |
},
|
| 83 |
"Stile 3: L'Architetto del Futuro (Design Geometrico)": {
|
| 84 |
+
"repo_id": "goofyai/flat_illustration_sdxl",
|
| 85 |
"weight_name": "flat_illustration_sdxl.safetensors",
|
| 86 |
"prompt": "flat vector art, Modern flat vector illustration of a tech architect holding a smartphone. He wears a minimalist grey suit jacket featuring a small geometric green, white, and red enamel badge. Clean geometric lines, industrial design background, minimalistic.",
|
| 87 |
"negative_prompt": "ugly, deformed, photorealistic, 3d render, bad anatomy, worst quality, low quality, watermark, text"
|
| 88 |
},
|
| 89 |
"Stile 4: Genio Creativo (Pop-Art)": {
|
| 90 |
+
"repo_id": None,
|
| 91 |
"weight_name": None,
|
| 92 |
"prompt": "vintage pop art, Comic book style pop-art portrait of a creative maker. Vibrant colors. He wears a trendy jacket adorned with an 'Italian Builders' custom patch in green, white and red. Halftone dots, vintage poster aesthetic.",
|
| 93 |
"negative_prompt": "ugly, deformed, photorealistic, bad anatomy, worst quality, low quality, watermark, extra text"
|
|
|
|
| 99 |
# ==============================================================================
|
| 100 |
|
| 101 |
def draw_kps(image_pil, kps, color_list=[(255,0,0), (0,255,0), (0,0,255), (255,255,0), (255,0,255)]):
|
| 102 |
+
"""Genera l'immagine dei keypoints per la ControlNet di InstantID."""
|
| 103 |
stickwidth = 4
|
| 104 |
limbSeq = np.array([[0, 2], [1, 2], [3, 2], [4, 2]])
|
| 105 |
kps = np.array(kps)
|
|
|
|
| 124 |
|
| 125 |
return Image.fromarray(out_img.astype(np.uint8))
|
| 126 |
|
| 127 |
+
def apply_logo_overlay(generated_image, logo_image, style_name):
|
| 128 |
+
"""
|
| 129 |
+
Applica il logo sull'immagine generata come una "spilla" o "toppa",
|
| 130 |
+
adattandone l'aspetto in base allo stile scelto.
|
| 131 |
+
"""
|
| 132 |
+
logo = logo_image.copy().convert("RGBA")
|
| 133 |
+
|
| 134 |
+
# Ridimensiona il logo per farlo sembrare una spilla (circa 15% dell'immagine)
|
| 135 |
+
logo_size = int(generated_image.width * 0.15)
|
| 136 |
+
logo = logo.resize((logo_size, logo_size), Image.LANCZOS)
|
| 137 |
+
|
| 138 |
+
# Applica filtri stilistici al logo in base allo stile
|
| 139 |
+
logo_np = np.array(logo)
|
| 140 |
+
|
| 141 |
+
if "Bit-Builder" in style_name:
|
| 142 |
+
# Effetto pixelato: rimpicciolisci e riallarga
|
| 143 |
+
small = cv2.resize(logo_np, (16, 16), interpolation=cv2.INTER_NEAREST)
|
| 144 |
+
logo_np = cv2.resize(small, (logo_size, logo_size), interpolation=cv2.INTER_NEAREST)
|
| 145 |
+
elif "Rinascimentale" in style_name:
|
| 146 |
+
# Effetto dipinto a olio: sfocatura leggera
|
| 147 |
+
logo_np[:, :, :3] = cv2.bilateralFilter(logo_np[:, :, :3], 9, 75, 75)
|
| 148 |
+
elif "Architetto" in style_name:
|
| 149 |
+
# Effetto geometrico: aumento contrasto
|
| 150 |
+
logo_np[:, :, :3] = np.clip(logo_np[:, :, :3].astype(np.float32) * 1.3, 0, 255).astype(np.uint8)
|
| 151 |
+
# Pop-Art: logo invariato (già vivace di suo)
|
| 152 |
+
|
| 153 |
+
logo_styled = Image.fromarray(logo_np)
|
| 154 |
+
|
| 155 |
+
# Posiziona il logo in basso a sinistra (zona petto)
|
| 156 |
+
x_pos = int(generated_image.width * 0.15)
|
| 157 |
+
y_pos = int(generated_image.height * 0.70)
|
| 158 |
+
|
| 159 |
+
# Componi sul risultato finale
|
| 160 |
+
result = generated_image.convert("RGBA")
|
| 161 |
+
result.paste(logo_styled, (x_pos, y_pos), logo_styled)
|
| 162 |
+
|
| 163 |
+
return result.convert("RGB")
|
| 164 |
|
| 165 |
@spaces.GPU(duration=120)
|
| 166 |
def generate_avatar(user_image, logo_image, selected_style):
|
| 167 |
if user_image is None:
|
| 168 |
raise gr.Error("Per favore, carica un'immagine del volto.")
|
|
|
|
|
|
|
| 169 |
|
| 170 |
# 1. Analisi del volto
|
| 171 |
cv_image = cv2.cvtColor(np.array(user_image), cv2.COLOR_RGB2BGR)
|
| 172 |
+
faces = face_app.get(cv_image)
|
| 173 |
if len(faces) == 0:
|
| 174 |
raise gr.Error("Nessun volto rilevato. Riprova con un'altra foto in cui il volto sia ben visibile.")
|
| 175 |
+
|
| 176 |
face = sorted(faces, key=lambda x: (x.bbox[2]-x.bbox[0])*(x.bbox[3]-x.bbox[1]))[-1]
|
| 177 |
face_emb = face.normed_embedding
|
| 178 |
+
face_kps = face.kps
|
| 179 |
+
|
| 180 |
+
# Ridimensioniamo l'immagine utente a 1024x1024 (standard SDXL)
|
| 181 |
user_image_resized = user_image.resize((1024, 1024))
|
| 182 |
+
faces_resized = face_app.get(cv2.cvtColor(np.array(user_image_resized), cv2.COLOR_RGB2BGR))
|
| 183 |
if len(faces_resized) > 0:
|
| 184 |
face_kps = sorted(faces_resized, key=lambda x: (x.bbox[2]-x.bbox[0])*(x.bbox[3]-x.bbox[1]))[-1].kps
|
| 185 |
+
|
|
|
|
|
|
|
| 186 |
kps_image = draw_kps(user_image_resized, face_kps)
|
| 187 |
+
|
| 188 |
+
# 2. Gestione LoRA
|
|
|
|
|
|
|
|
|
|
|
|
|
| 189 |
style_config = STYLES[selected_style]
|
| 190 |
repo_id = style_config["repo_id"]
|
| 191 |
+
|
| 192 |
if repo_id:
|
| 193 |
try:
|
| 194 |
print(f"Scaricamento/Caricamento LoRA {repo_id}...")
|
|
|
|
| 196 |
except Exception as e:
|
| 197 |
print(f"Errore caricamento LoRA da HF: {e}")
|
| 198 |
|
| 199 |
+
# 3. Generazione immagine con InstantID
|
| 200 |
prompt = style_config["prompt"]
|
| 201 |
negative_prompt = style_config["negative_prompt"]
|
| 202 |
+
|
| 203 |
+
print("Inizio generazione InstantID...")
|
| 204 |
+
pipe.set_ip_adapter_scale(0.8)
|
| 205 |
image = pipe(
|
| 206 |
prompt=prompt,
|
| 207 |
negative_prompt=negative_prompt,
|
| 208 |
image_embeds=face_emb,
|
| 209 |
+
image=kps_image,
|
| 210 |
+
controlnet_conditioning_scale=0.8,
|
|
|
|
|
|
|
|
|
|
| 211 |
num_inference_steps=30,
|
| 212 |
guidance_scale=5.0,
|
| 213 |
).images[0]
|
| 214 |
+
|
| 215 |
if repo_id:
|
| 216 |
try:
|
| 217 |
pipe.unload_lora_weights()
|
| 218 |
except:
|
| 219 |
pass
|
| 220 |
+
|
| 221 |
+
# 4. Sovrapposizione del logo stilizzato (se fornito)
|
| 222 |
+
if logo_image is not None:
|
| 223 |
+
image = apply_logo_overlay(image, logo_image, selected_style)
|
| 224 |
+
|
| 225 |
return image
|
| 226 |
|
| 227 |
# ==============================================================================
|
|
|
|
| 245 |
with gr.Column(elem_classes="container"):
|
| 246 |
gr.HTML("<h1 class='header-title'>🇮🇹 Italian Builders Avatar Generator 🇮🇹</h1>")
|
| 247 |
gr.HTML("<p class='header-subtitle'>Trasforma il tuo selfie. Il tuo brand, la tua identità.</p>")
|
| 248 |
+
|
| 249 |
with gr.Row():
|
| 250 |
with gr.Column(scale=1):
|
| 251 |
input_image = gr.Image(type="pil", label="1. Carica il tuo Selfie")
|
| 252 |
+
logo_image = gr.Image(type="pil", label="2. Carica il Logo (opzionale, PNG a colori)")
|
| 253 |
+
|
| 254 |
with gr.Column(scale=1):
|
| 255 |
style_selector = gr.Radio(
|
| 256 |
choices=list(STYLES.keys()),
|
|
|
|
| 259 |
)
|
| 260 |
gr.Markdown("<br>")
|
| 261 |
generate_button = gr.Button("Genera il tuo Avatar 🚀", elem_classes="generate-btn", variant="primary")
|
| 262 |
+
|
| 263 |
with gr.Column(scale=1):
|
| 264 |
output_image = gr.Image(label="Il tuo Avatar Builder", interactive=False)
|
| 265 |
+
|
| 266 |
generate_button.click(
|
| 267 |
fn=generate_avatar,
|
| 268 |
inputs=[input_image, logo_image, style_selector],
|