CheckSim commited on
Commit
f2df896
·
verified ·
1 Parent(s): 97dddb6

Upload 6 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ esempi/8bit.png filter=lfs diff=lfs merge=lfs -text
37
+ esempi/architetto.png filter=lfs diff=lfs merge=lfs -text
38
+ esempi/popart.png filter=lfs diff=lfs merge=lfs -text
39
+ esempi/rinascimentale.png filter=lfs diff=lfs merge=lfs -text
app.py CHANGED
@@ -1,189 +1,177 @@
1
- import os
2
- import cv2
3
- import math
4
  import torch
 
5
  import numpy as np
6
- import gradio as gr
 
 
7
  import spaces
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 ControlNetModel
13
-
14
- from pipeline_stable_diffusion_xl_instantid import StableDiffusionXLInstantIDPipeline
15
 
16
  # ==============================================================================
17
- # 1. SETUP E INIZIALIZZAZIONE GLOBALE
18
  # ==============================================================================
19
 
20
- device = "cuda" if torch.cuda.is_available() else "cpu"
21
- dtype = torch.float16 if torch.cuda.is_available() else torch.float32
22
-
23
- def download_antelopev2():
24
- """Scarica automaticamente i modelli di InsightFace se non sono presenti."""
25
- model_dir = "./models/antelopev2"
26
- if not os.path.exists(model_dir) or len(os.listdir(model_dir)) < 5:
27
- print("Scaricamento dei modelli antelopev2 in corso...")
28
- os.makedirs(model_dir, exist_ok=True)
29
- snapshot_download(repo_id="DIAMONIK7777/antelopev2", local_dir=model_dir)
30
- print("Scaricamento completato.")
31
- return "./"
32
-
33
- model_root = download_antelopev2()
34
-
35
- face_app = FaceAnalysis(name='antelopev2', root=model_root, providers=['CPUExecutionProvider'])
36
  face_app.prepare(ctx_id=0, det_size=(640, 640))
37
 
38
- # Scarica il file ip-adapter.bin di InstantID
39
- face_adapter = hf_hub_download(repo_id="InstantX/InstantID", filename="ip-adapter.bin")
40
-
41
- # Carica il ControlNet (IdentityNet) di InstantID
42
- print("Caricamento ControlNet InstantID...")
43
- controlnet = ControlNetModel.from_pretrained(
44
- "InstantX/InstantID",
45
- subfolder="ControlNetModel",
46
- torch_dtype=dtype,
47
- use_safetensors=True
48
- )
 
 
49
 
50
- # Carica la pipeline dedicata InstantID (NON quella generica ControlNet)
51
- print("Caricamento Pipeline SDXL InstantID...")
52
- pipe = StableDiffusionXLInstantIDPipeline.from_pretrained(
53
- "stabilityai/stable-diffusion-xl-base-1.0",
54
- controlnet=controlnet,
55
  torch_dtype=dtype,
 
56
  use_safetensors=True
57
  )
58
 
59
- # Carica il Face Adapter con il metodo specifico di InstantID
60
- print("Caricamento Face Adapter InstantID...")
61
- pipe.load_ip_adapter_instantid(face_adapter)
62
- pipe.to(device)
63
-
64
- print("Pipeline pronta!")
65
-
66
 
67
  # ==============================================================================
68
- # 2. CONFIGURAZIONE STILI
69
  # ==============================================================================
70
-
71
  STYLES = {
72
  "Stile 1: Bit-Builder (8-Bit/Pixel Art)": {
 
73
  "repo_id": "nerijs/pixel-art-xl",
74
  "weight_name": "pixel-art-xl.safetensors",
75
- "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.",
76
- "negative_prompt": "ugly, deformed, lowres, bad anatomy, bad hands, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, watermark, text"
77
  },
78
  "Stile 2: Il Costruttore Rinascimentale": {
 
79
  "repo_id": "davidmoref/sdxl-lora-adapter-renaissance",
80
  "weight_name": "pytorch_lora_weights.safetensors",
81
- "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.",
82
- "negative_prompt": "ugly, deformed, modern, photography, bad anatomy, worst quality, low quality, watermark, text"
83
  },
84
- "Stile 3: L'Architetto del Futuro (Design Geometrico)": {
85
- "repo_id": "goofyai/flat_illustration_sdxl",
86
- "weight_name": "flat_illustration_sdxl.safetensors",
87
- "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.",
88
- "negative_prompt": "ugly, deformed, photorealistic, 3d render, bad anatomy, worst quality, low quality, watermark, text"
89
- },
90
- "Stile 4: Genio Creativo (Pop-Art)": {
91
  "repo_id": None,
92
  "weight_name": None,
93
- "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.",
94
- "negative_prompt": "ugly, deformed, photorealistic, bad anatomy, worst quality, low quality, watermark, extra text"
95
  }
96
  }
97
 
98
  # ==============================================================================
99
- # 3. PIPELINE DI GENERAZIONE
100
  # ==============================================================================
101
 
102
- def draw_kps(image_pil, kps, color_list=[(255,0,0), (0,255,0), (0,0,255), (255,255,0), (255,0,255)]):
103
- """Genera l'immagine dei keypoints per la ControlNet di InstantID."""
104
- stickwidth = 4
105
- limbSeq = np.array([[0, 2], [1, 2], [3, 2], [4, 2]])
106
- kps = np.array(kps)
107
- w, h = image_pil.size
108
- out_img = np.zeros([h, w, 3])
109
-
110
- for i in range(len(limbSeq)):
111
- index = limbSeq[i]
112
- color = color_list[index[0]]
113
- x = kps[index][:, 0]
114
- y = kps[index][:, 1]
115
- length = ((x[0] - x[1]) ** 2 + (y[0] - y[1]) ** 2) ** 0.5
116
- angle = math.degrees(math.atan2(y[0] - y[1], x[0] - x[1]))
117
- polygon = cv2.ellipse2Poly((int(np.mean(x)), int(np.mean(y))), (int(length / 2), stickwidth), int(angle), 0, 360, 1)
118
- cv2.fillConvexPoly(out_img, polygon, color)
119
- out_img = (out_img * 0.6).astype(np.uint8)
120
-
121
- for idx, kp in enumerate(kps):
122
- color = color_list[idx]
123
- x, y = kp
124
- cv2.circle(out_img, (int(x), int(y)), 10, color, -1)
125
-
126
- return Image.fromarray(out_img.astype(np.uint8))
127
-
128
- @spaces.GPU(duration=120)
129
  def generate_avatar(user_image, selected_style):
130
  if user_image is None:
131
  raise gr.Error("Per favore, carica un'immagine del volto.")
132
-
133
- # 1. Analisi del volto
134
- cv_image = cv2.cvtColor(np.array(user_image), cv2.COLOR_RGB2BGR)
135
- faces = face_app.get(cv_image)
136
- if len(faces) == 0:
137
- raise gr.Error("Nessun volto rilevato. Riprova con un'altra foto in cui il volto sia ben visibile.")
138
-
139
- face = sorted(faces, key=lambda x: (x.bbox[2]-x.bbox[0])*(x.bbox[3]-x.bbox[1]))[-1]
140
- face_emb = face.normed_embedding
141
- face_kps = face.kps
142
-
143
- # Ridimensioniamo l'immagine utente a 1024x1024 (standard SDXL)
144
- user_image_resized = user_image.resize((1024, 1024))
145
- faces_resized = face_app.get(cv2.cvtColor(np.array(user_image_resized), cv2.COLOR_RGB2BGR))
146
- if len(faces_resized) > 0:
147
- face_kps = sorted(faces_resized, key=lambda x: (x.bbox[2]-x.bbox[0])*(x.bbox[3]-x.bbox[1]))[-1].kps
148
-
149
- kps_image = draw_kps(user_image_resized, face_kps)
150
-
151
- # 2. Gestione LoRA
152
  style_config = STYLES[selected_style]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  repo_id = style_config["repo_id"]
154
-
 
155
  if repo_id:
156
  try:
157
  print(f"Scaricamento/Caricamento LoRA {repo_id}...")
158
  pipe.load_lora_weights(repo_id, weight_name=style_config["weight_name"])
159
  except Exception as e:
160
  print(f"Errore caricamento LoRA da HF: {e}")
161
-
162
- # 3. Generazione immagine con InstantID
163
- prompt = style_config["prompt"]
164
- negative_prompt = style_config["negative_prompt"]
165
-
166
- print("Inizio generazione InstantID...")
167
- pipe.set_ip_adapter_scale(0.8)
168
- image = pipe(
169
- prompt=prompt,
170
- negative_prompt=negative_prompt,
171
- image_embeds=face_emb,
172
- image=kps_image,
173
- controlnet_conditioning_scale=0.8,
174
  num_inference_steps=30,
175
- guidance_scale=5.0,
176
  ).images[0]
177
 
 
178
  if repo_id:
179
  try:
180
  pipe.unload_lora_weights()
181
  except:
182
  pass
183
 
184
-
185
-
186
- return image
187
 
188
  # ==============================================================================
189
  # 4. INTERFACCIA GRADIO (FRONTEND)
@@ -205,11 +193,12 @@ theme = gr.themes.Soft(primary_hue="blue", secondary_hue="emerald").set(
205
  with gr.Blocks(css=custom_css, theme=theme) as demo:
206
  with gr.Column(elem_classes="container"):
207
  gr.HTML("<h1 class='header-title'>🇮🇹 Italian Builders Avatar Generator 🇮🇹</h1>")
208
- gr.HTML("<p class='header-subtitle'>Trasforma il tuo selfie. Il tuo brand, la tua identità.</p>")
209
 
210
  with gr.Row():
211
  with gr.Column(scale=1):
212
  input_image = gr.Image(type="pil", label="1. Carica il tuo Selfie")
 
213
  with gr.Column(scale=1):
214
  style_selector = gr.Radio(
215
  choices=list(STYLES.keys()),
@@ -220,7 +209,7 @@ with gr.Blocks(css=custom_css, theme=theme) as demo:
220
  generate_button = gr.Button("Genera il tuo Avatar 🚀", elem_classes="generate-btn", variant="primary")
221
 
222
  with gr.Column(scale=1):
223
- output_image = gr.Image(label="Il tuo Avatar Builder", interactive=False)
224
 
225
  generate_button.click(
226
  fn=generate_avatar,
 
1
+ import gradio as gr
 
 
2
  import torch
3
+ import cv2
4
  import numpy as np
5
+ import os
6
+ from PIL import Image, ImageDraw, ImageFilter
7
+ from diffusers import StableDiffusionXLInpaintPipeline
8
  import spaces
 
 
 
9
  from insightface.app import FaceAnalysis
10
+ import insightface
11
+ from huggingface_hub import hf_hub_download
 
12
 
13
  # ==============================================================================
14
+ # 1. SETUP DELL'AMBIENTE E DOWNLOAD MODELLI
15
  # ==============================================================================
16
 
17
+ # Inizializza l'analizzatore di volti (InsightFace)
18
+ print("Inizializzazione FaceAnalysis...")
19
+ face_app = FaceAnalysis(name='antelopev2', root='./', providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  face_app.prepare(ctx_id=0, det_size=(640, 640))
21
 
22
+ # Download e caricamento del modello Inswapper
23
+ print("Download e caricamento Inswapper...")
24
+ try:
25
+ inswapper_path = hf_hub_download(repo_id="ezioruan/inswapper_128.onnx", filename="inswapper_128.onnx")
26
+ face_swapper = insightface.model_zoo.get_model(inswapper_path, providers=['CUDAExecutionProvider', 'CPUExecutionProvider'])
27
+ except Exception as e:
28
+ print(f"Errore caricamento Inswapper: {e}")
29
+ face_swapper = None
30
+
31
+ # Caricamento Pipeline Inpainting SDXL
32
+ print("Caricamento pipeline SDXL Inpaint...")
33
+ device = "cuda" if torch.cuda.is_available() else "cpu"
34
+ dtype = torch.float16 if torch.cuda.is_available() else "float32"
35
 
36
+ pipe = StableDiffusionXLInpaintPipeline.from_pretrained(
37
+ "diffusers/stable-diffusion-xl-1.0-inpainting-0.1",
 
 
 
38
  torch_dtype=dtype,
39
+ variant="fp16" if torch.cuda.is_available() else None,
40
  use_safetensors=True
41
  )
42
 
43
+ # Rimuovi il limitatore di sicurezza se crea problemi (opzionale)
44
+ pipe.watermark = None
 
 
 
 
 
45
 
46
  # ==============================================================================
47
+ # 2. CONFIGURAZIONE STILI E TEMPLATE
48
  # ==============================================================================
49
+ # I template sono le immagini pre-generate fornite dall'utente.
50
  STYLES = {
51
  "Stile 1: Bit-Builder (8-Bit/Pixel Art)": {
52
+ "template": "esempi/8bit.png",
53
  "repo_id": "nerijs/pixel-art-xl",
54
  "weight_name": "pixel-art-xl.safetensors",
55
+ "prompt": "pixelart style, 8-bit game style face, highly detailed, perfect pixel art shading, 8-bit retro aesthetic, matching lighting",
56
+ "negative_prompt": "ugly, deformed, lowres, realistic, photographic, 3d render, photo, photorealistic"
57
  },
58
  "Stile 2: Il Costruttore Rinascimentale": {
59
+ "template": "esempi/rinascimentale.png",
60
  "repo_id": "davidmoref/sdxl-lora-adapter-renaissance",
61
  "weight_name": "pytorch_lora_weights.safetensors",
62
+ "prompt": "renaissance oil painting, oil painting face, elegant brush strokes, chiaroscuro lighting, classic masterpiece, perfectly integrated face",
63
+ "negative_prompt": "ugly, deformed, modern, photography, bad anatomy, worst quality, low quality, pixel art, cartoon"
64
  },
65
+ "Stile 3: Genio Creativo (Pop-Art)": {
66
+ "template": "esempi/popart.png",
 
 
 
 
 
67
  "repo_id": None,
68
  "weight_name": None,
69
+ "prompt": "vintage pop art, Comic book style pop-art face, vibrant colors, halftone dots, graphic illustration, bold lines",
70
+ "negative_prompt": "ugly, deformed, photorealistic, realistic photography, oil painting, 3d render"
71
  }
72
  }
73
 
74
  # ==============================================================================
75
+ # 3. FUNZIONI CORE DELL'APPLICAZIONE
76
  # ==============================================================================
77
 
78
+ def create_face_mask(image, bbox, expansion=0.2, blur_radius=15):
79
+ """
80
+ Crea una maschera sfumata attorno al volto per l'inpainting.
81
+ """
82
+ mask = Image.new("L", image.size, 0)
83
+ draw = ImageDraw.Draw(mask)
84
+
85
+ x1, y1, x2, y2 = bbox
86
+ w = x2 - x1
87
+ h = y2 - y1
88
+
89
+ # Espandi leggermente la bounding box
90
+ nx1 = max(0, x1 - w * expansion)
91
+ ny1 = max(0, y1 - h * expansion)
92
+ nx2 = min(image.width, x2 + w * expansion)
93
+ ny2 = min(image.height, y2 + h * (expansion * 1.5)) # Espandi un po' di più verso il basso per il mento
94
+
95
+ # Disegna un ovale
96
+ draw.ellipse([nx1, ny1, nx2, ny2], fill=255)
97
+
98
+ # Sfuma i bordi per fondere meglio
99
+ mask = mask.filter(ImageFilter.GaussianBlur(blur_radius))
100
+ return mask
101
+
102
+ @spaces.GPU(duration=60)
 
 
103
  def generate_avatar(user_image, selected_style):
104
  if user_image is None:
105
  raise gr.Error("Per favore, carica un'immagine del volto.")
106
+
107
+ if face_swapper is None:
108
+ raise gr.Error("Modello Inswapper non caricato correttamente.")
109
+
110
+ # 1. Analisi del volto utente
111
+ cv_user_image = cv2.cvtColor(np.array(user_image), cv2.COLOR_RGB2BGR)
112
+ user_faces = face_app.get(cv_user_image)
113
+ if len(user_faces) == 0:
114
+ raise gr.Error("Nessun volto rilevato nella tua foto. Riprova con un selfie più chiaro.")
115
+
116
+ # Prendi il volto più grande
117
+ user_face = sorted(user_faces, key=lambda x: (x.bbox[2]-x.bbox[0])*(x.bbox[3]-x.bbox[1]))[-1]
118
+
119
+ # 2. Caricamento e analisi del Template
 
 
 
 
 
 
120
  style_config = STYLES[selected_style]
121
+ template_path = style_config["template"]
122
+
123
+ if not os.path.exists(template_path):
124
+ raise gr.Error(f"Errore: il file di template {template_path} non esiste.")
125
+
126
+ template_pil = Image.open(template_path).convert("RGB")
127
+ cv_template_image = cv2.cvtColor(np.array(template_pil), cv2.COLOR_RGB2BGR)
128
+
129
+ template_faces = face_app.get(cv_template_image)
130
+ if len(template_faces) == 0:
131
+ raise gr.Error("Impossibile trovare un volto nel template di base.")
132
+ template_face = sorted(template_faces, key=lambda x: (x.bbox[2]-x.bbox[0])*(x.bbox[3]-x.bbox[1]))[-1]
133
+
134
+ # 3. Swap del Volto (Fotorealistico)
135
+ print("Eseguendo Face Swap...")
136
+ swapped_cv = face_swapper.get(cv_template_image, template_face, user_face, paste_back=True)
137
+ swapped_pil = Image.fromarray(cv2.cvtColor(swapped_cv, cv2.COLOR_BGR2RGB))
138
+
139
+ # 4. Creazione Maschera per l'Inpainting
140
+ mask_pil = create_face_mask(template_pil, template_face.bbox)
141
+
142
+ # 5. Stylization tramite Inpainting
143
  repo_id = style_config["repo_id"]
144
+
145
+ # Carica il LoRA dello stile se necessario
146
  if repo_id:
147
  try:
148
  print(f"Scaricamento/Caricamento LoRA {repo_id}...")
149
  pipe.load_lora_weights(repo_id, weight_name=style_config["weight_name"])
150
  except Exception as e:
151
  print(f"Errore caricamento LoRA da HF: {e}")
152
+
153
+ # Sposta la pipeline su GPU per l'inferenza
154
+ pipe.to(device)
155
+
156
+ print("Inizio Inpainting per fondere lo stile...")
157
+ stylized_image = pipe(
158
+ prompt=style_config["prompt"],
159
+ negative_prompt=style_config["negative_prompt"],
160
+ image=swapped_pil,
161
+ mask_image=mask_pil,
162
+ strength=0.35, # Bassa strength: mantiene forte l'identità, applica solo lo stile superficiale
163
+ guidance_scale=7.5,
 
164
  num_inference_steps=30,
 
165
  ).images[0]
166
 
167
+ # Scarica il LoRA per evitare conflitti al prossimo giro
168
  if repo_id:
169
  try:
170
  pipe.unload_lora_weights()
171
  except:
172
  pass
173
 
174
+ return stylized_image
 
 
175
 
176
  # ==============================================================================
177
  # 4. INTERFACCIA GRADIO (FRONTEND)
 
193
  with gr.Blocks(css=custom_css, theme=theme) as demo:
194
  with gr.Column(elem_classes="container"):
195
  gr.HTML("<h1 class='header-title'>🇮🇹 Italian Builders Avatar Generator 🇮🇹</h1>")
196
+ gr.HTML("<p class='header-subtitle'>Trasforma il tuo selfie e unisciti alla community.</p>")
197
 
198
  with gr.Row():
199
  with gr.Column(scale=1):
200
  input_image = gr.Image(type="pil", label="1. Carica il tuo Selfie")
201
+
202
  with gr.Column(scale=1):
203
  style_selector = gr.Radio(
204
  choices=list(STYLES.keys()),
 
209
  generate_button = gr.Button("Genera il tuo Avatar 🚀", elem_classes="generate-btn", variant="primary")
210
 
211
  with gr.Column(scale=1):
212
+ output_image = gr.Image(label="Il tuo Avatar", interactive=False)
213
 
214
  generate_button.click(
215
  fn=generate_avatar,
esempi/8bit.png ADDED

Git LFS Details

  • SHA256: 8e7d04a8f1f57b0bdcc892c327b8058d74dfbdaff0b0542791bdf627f4dc9e98
  • Pointer size: 132 Bytes
  • Size of remote file: 1.29 MB
esempi/architetto.png ADDED

Git LFS Details

  • SHA256: 37cf0ccd6bc6e006b546fa8a5c8fb5b6854e20295110c8c79110cd950be95515
  • Pointer size: 131 Bytes
  • Size of remote file: 918 kB
esempi/popart.png ADDED

Git LFS Details

  • SHA256: 2c37003db94784669c41d25964d840b3615e87e395e69689010d16d4c582bde2
  • Pointer size: 132 Bytes
  • Size of remote file: 1.62 MB
esempi/rinascimentale.png ADDED

Git LFS Details

  • SHA256: da1e9f9dc9c1ee4d5b876147d98bad580adb5355ae30543bb94c7ec824a90666
  • Pointer size: 132 Bytes
  • Size of remote file: 1.3 MB