VcRlAgent commited on
Commit
f4a3087
·
1 Parent(s): 396f468

InstantID For retaining facial identity

Browse files
Files changed (3) hide show
  1. app copy.py +277 -0
  2. app.py +80 -10
  3. requirements.txt +3 -0
app copy.py ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ==========================================
2
+ # FaceForge AI – ZeroGPU Gradio Version
3
+ # Author: Vijay S. Chaudhari | 2025
4
+ # ==========================================
5
+
6
+ import gradio as gr
7
+ import spaces
8
+ import torch
9
+ import cv2
10
+ import numpy as np
11
+ from PIL import Image, ImageEnhance, ImageOps
12
+ from rembg import remove
13
+ from diffusers import StableDiffusionImg2ImgPipeline
14
+ from diffusers import StableDiffusionXLPipeline
15
+ from instantid import InstantID
16
+ import io
17
+
18
+
19
+ import torchvision
20
+ print("Printing Torch and TorchVision versions:")
21
+ print(torch.__version__)
22
+ print(torchvision.__version__)
23
+
24
+ # GPU libraries
25
+ from gfpgan import GFPGANer
26
+ from basicsr.archs.rrdbnet_arch import RRDBNet
27
+ from realesrgan import RealESRGANer
28
+
29
+ # ------------------------------------------
30
+ # Model Loading (Outside GPU decorator)
31
+ # ------------------------------------------
32
+
33
+ def load_models():
34
+ """Load models once at startup"""
35
+ device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
36
+
37
+ # RealESRGAN upsampler
38
+ model = RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=2)
39
+ upsampler = RealESRGANer(
40
+ scale=2,
41
+ model_path='https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.1/RealESRGAN_x2plus.pth',
42
+ model=model,
43
+ tile=400,
44
+ tile_pad=10,
45
+ pre_pad=0,
46
+ half=True,
47
+ device=device
48
+ )
49
+
50
+ # GFPGAN enhancer
51
+ face_enhancer = GFPGANer(
52
+ model_path='https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.3.pth',
53
+ upscale=2,
54
+ arch='clean',
55
+ channel_multiplier=2,
56
+ bg_upsampler=upsampler,
57
+ device=device
58
+ )
59
+
60
+ # Stable Diffusion Img2Img pipeline (public model)
61
+ sd_pipe = StableDiffusionImg2ImgPipeline.from_pretrained(
62
+ "runwayml/stable-diffusion-v1-5",
63
+ torch_dtype=torch.float16
64
+ ).to(device)
65
+
66
+ # Optimize for ZeroGPU memory
67
+ sd_pipe.enable_attention_slicing()
68
+ sd_pipe.enable_model_cpu_offload()
69
+
70
+ return face_enhancer, sd_pipe
71
+
72
+ # Load models globally
73
+ face_enhancer, sd_pipe = load_models()
74
+
75
+ # ------------------------------------------
76
+ # GPU-Accelerated Functions
77
+ # ------------------------------------------
78
+
79
+ @spaces.GPU
80
+ def enhance_face(img: Image.Image) -> Image.Image:
81
+ """Enhance face using GFPGAN (GPU)"""
82
+ img_cv = cv2.cvtColor(np.array(img.convert('RGB')), cv2.COLOR_RGB2BGR)
83
+
84
+ with torch.no_grad():
85
+ _, _, restored_img = face_enhancer.enhance(
86
+ img_cv,
87
+ has_aligned=False,
88
+ only_center_face=False,
89
+ paste_back=True,
90
+ weight=0.5
91
+ )
92
+
93
+ restored_img = cv2.cvtColor(restored_img, cv2.COLOR_BGR2RGB)
94
+ return Image.fromarray(restored_img)
95
+
96
+ # ------------------------------------------
97
+ # Image Processing Functions
98
+ # ------------------------------------------
99
+
100
+ def enhance_image(img: Image.Image) -> Image.Image:
101
+ """Basic enhancement"""
102
+ img = ImageEnhance.Contrast(img).enhance(1.15)
103
+ img = ImageEnhance.Sharpness(img).enhance(1.1)
104
+ return img
105
+
106
+ @spaces.GPU
107
+ def create_headshot(img: Image.Image) -> Image.Image:
108
+ """Professional headshot with gradient background"""
109
+ # Enhance face
110
+ img_enhanced = enhance_face(img)
111
+
112
+ # Remove background
113
+ img_no_bg = remove(img_enhanced)
114
+
115
+ # Gradient background
116
+ bg = Image.new("RGB", img_no_bg.size, (200, 210, 230))
117
+ if img_no_bg.mode == 'RGBA':
118
+ bg.paste(img_no_bg, mask=img_no_bg.split()[3])
119
+
120
+ return enhance_image(bg)
121
+
122
+ @spaces.GPU
123
+ def create_passport(img: Image.Image) -> Image.Image:
124
+ """Passport photo with white background"""
125
+ # Enhance face
126
+ img_enhanced = enhance_face(img)
127
+
128
+ # Remove background
129
+ img_no_bg = remove(img_enhanced)
130
+
131
+ # White background (600x600)
132
+ bg = Image.new("RGB", (600, 600), (255, 255, 255))
133
+ img_no_bg.thumbnail((550, 550), Image.Resampling.LANCZOS)
134
+ offset = ((600 - img_no_bg.width) // 2, (600 - img_no_bg.height) // 2)
135
+
136
+ if img_no_bg.mode == 'RGBA':
137
+ bg.paste(img_no_bg, offset, mask=img_no_bg.split()[3])
138
+
139
+ return bg
140
+
141
+ @spaces.GPU
142
+ def create_avatar(img: Image.Image, prompt: str, strength: float, guidance_scale: float) -> Image.Image:
143
+ """Stylized AI avatar using Stable Diffusion Img2Img with user inputs"""
144
+ # Enhance face
145
+ img_enhanced = enhance_face(img)
146
+
147
+ # Resize for SD (512x512)
148
+ img_resized = img_enhanced.convert("RGB").resize((512, 512))
149
+
150
+ # Stylize with SD prompt. We are selecting these from UI now.
151
+ #prompt = "highly detailed, digital portrait, professional lighting, cinematic style, artistic AI avatar"
152
+ #prompt = "stylized yet realistic portrait, balanced lighting, subtle gradient background, sharp focus on face"
153
+ #prompt = "studio portrait, even lighting, neutral background, realistic skin, confident pose"
154
+ #prompt = "realistic professional headshot, soft studio lighting, neutral background, crisp details, natural skin tone"
155
+
156
+ with torch.autocast("cuda"):
157
+ result = sd_pipe(prompt=prompt, image=img_resized, strength=strength, guidance_scale=guidance_scale)
158
+
159
+ avatar = enhance_face(result.images[0])
160
+
161
+ return avatar
162
+
163
+ @spaces.GPU
164
+ def process_all(img: Image.Image):
165
+ """Process all three types at once"""
166
+ headshot = create_headshot(img)
167
+ passport = create_passport(img)
168
+ avatar = create_avatar(img)
169
+ return headshot, passport, avatar
170
+
171
+ # ------------------------------------------
172
+ # Gradio Interface
173
+ # ------------------------------------------
174
+
175
+ with gr.Blocks(theme=gr.themes.Soft(), title="FaceForge AI") as demo:
176
+ gr.Markdown(
177
+ """
178
+ # 🎨 FaceForge AI
179
+ ### GPU-Accelerated Professional Headshot & Avatar Generator
180
+ Upload your photo and choose or customize how your AI avatar is generated.
181
+ """
182
+ )
183
+
184
+ # --- Define a mapping: Short Label -> Full Prompt Text ---
185
+ PROMPT_MAP = {
186
+ "🎬 Cinematic Portrait": "highly detailed, digital portrait, professional lighting, cinematic style, artistic AI avatar",
187
+ "🎨 Stylized Realism": "stylized yet realistic portrait, balanced lighting, subtle gradient background, sharp focus on face",
188
+ "🏢 Studio Professional": "studio portrait, even lighting, neutral background, realistic skin, confident pose",
189
+ "🤵 Natural Headshot": "realistic professional headshot, soft studio lighting, neutral background, crisp details, natural skin tone"
190
+ }
191
+
192
+ with gr.Row():
193
+ with gr.Column(scale=1):
194
+ input_image = gr.Image(type="pil", label="📷 Upload Your Photo")
195
+
196
+ gr.Markdown("### ⚙️ Avatar Generation Settings")
197
+
198
+ # Dropdown shows short labels only
199
+ preset_prompt = gr.Dropdown(
200
+ label="🎨 Choose Avatar Style Preset",
201
+ choices=list(PROMPT_MAP.keys()),
202
+ value="🤵 Natural Headshot"
203
+ )
204
+
205
+ # Optional custom prompt box for flexibility
206
+ custom_prompt = gr.Textbox(
207
+ label="✏️ Custom Prompt (optional)",
208
+ placeholder="Enter your own prompt or leave blank to use preset...",
209
+ lines=2
210
+ )
211
+
212
+ strength_slider = gr.Slider(
213
+ label="🎛️ Style Strength (0.0 = keep original, 1.0 = full restyle)",
214
+ minimum=0.1,
215
+ maximum=1.0,
216
+ value=0.45,
217
+ step=0.05
218
+ )
219
+
220
+ guidance_slider = gr.Slider(
221
+ label="🎯 Prompt Guidance Scale (higher = more prompt influence)",
222
+ minimum=1.0,
223
+ maximum=10.0,
224
+ value=5.5,
225
+ step=0.5
226
+ )
227
+
228
+ with gr.Column(scale=1):
229
+ gr.Markdown("### Results")
230
+
231
+ # --- Independent Outputs & Buttons ---
232
+ output_headshot = gr.Image(label="💼 Professional Headshot", type="pil")
233
+ btn_headshot = gr.Button("📸 Generate Headshot", variant="secondary")
234
+
235
+ output_passport = gr.Image(label="🛂 Passport Photo", type="pil")
236
+ btn_passport = gr.Button("🪪 Generate Passport", variant="secondary")
237
+
238
+ output_avatar = gr.Image(label="🎭 AI Avatar", type="pil")
239
+ btn_avatar = gr.Button("✨ Generate Avatar", variant="primary")
240
+
241
+ # --- Functions for individual generations ---
242
+ def run_headshot(img):
243
+ return create_headshot(img)
244
+
245
+ def run_passport(img):
246
+ return create_passport(img)
247
+
248
+ def run_avatar(img, preset_label, custom, strength, guidance):
249
+ final_prompt = custom.strip() if custom and custom.strip() != "" else PROMPT_MAP[preset_label]
250
+ return create_avatar(img, final_prompt, strength, guidance)
251
+
252
+ # --- Button actions ---
253
+ btn_headshot.click(fn=run_headshot, inputs=[input_image], outputs=[output_headshot])
254
+ btn_passport.click(fn=run_passport, inputs=[input_image], outputs=[output_passport])
255
+ btn_avatar.click(
256
+ fn=run_avatar,
257
+ inputs=[input_image, preset_prompt, custom_prompt, strength_slider, guidance_slider],
258
+ outputs=[output_avatar]
259
+ )
260
+
261
+ gr.Markdown(
262
+ """
263
+ ---
264
+ ### Features
265
+ - 💼 **Professional Headshots**: Perfect for LinkedIn and business profiles
266
+ - 🛂 **Passport Photos**: Standard 600x600px with white background
267
+ - 🎭 **AI Avatars**: Stylized versions for social media
268
+ - ⚡ **GPU-Accelerated**: Fast processing with GFPGAN enhancement
269
+
270
+ © 2025 Vijay S. Chaudhari | Powered by ZeroGPU 🚀
271
+ """
272
+ )
273
+
274
+ # Launch
275
+ if __name__ == "__main__":
276
+ demo.queue(max_size=20)
277
+ demo.launch()
app.py CHANGED
@@ -11,6 +11,8 @@ import numpy as np
11
  from PIL import Image, ImageEnhance, ImageOps
12
  from rembg import remove
13
  from diffusers import StableDiffusionImg2ImgPipeline
 
 
14
  import io
15
 
16
 
@@ -138,12 +140,10 @@ def create_passport(img: Image.Image) -> Image.Image:
138
 
139
  @spaces.GPU
140
  def create_avatar(img: Image.Image, prompt: str, strength: float, guidance_scale: float) -> Image.Image:
141
- """Stylized AI avatar using Stable Diffusion Img2Img with user inputs"""
142
- # Enhance face
143
- img_enhanced = enhance_face(img)
144
-
145
- # Resize for SD (512x512)
146
- img_resized = img_enhanced.convert("RGB").resize((512, 512))
147
 
148
  # Stylize with SD prompt. We are selecting these from UI now.
149
  #prompt = "highly detailed, digital portrait, professional lighting, cinematic style, artistic AI avatar"
@@ -151,13 +151,83 @@ def create_avatar(img: Image.Image, prompt: str, strength: float, guidance_scale
151
  #prompt = "studio portrait, even lighting, neutral background, realistic skin, confident pose"
152
  #prompt = "realistic professional headshot, soft studio lighting, neutral background, crisp details, natural skin tone"
153
 
154
- with torch.autocast("cuda"):
155
- result = sd_pipe(prompt=prompt, image=img_resized, strength=strength, guidance_scale=guidance_scale)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
- avatar = enhance_face(result.images[0])
158
-
159
  return avatar
160
 
 
161
  @spaces.GPU
162
  def process_all(img: Image.Image):
163
  """Process all three types at once"""
 
11
  from PIL import Image, ImageEnhance, ImageOps
12
  from rembg import remove
13
  from diffusers import StableDiffusionImg2ImgPipeline
14
+ from diffusers import StableDiffusionXLPipeline
15
+ from instantid import InstantID
16
  import io
17
 
18
 
 
140
 
141
  @spaces.GPU
142
  def create_avatar(img: Image.Image, prompt: str, strength: float, guidance_scale: float) -> Image.Image:
143
+ """
144
+ Create a stylized AI avatar while preserving facial identity using InstantID.
145
+ Retains core facial features, skin tone, and expressions of the input photo.
146
+ """
 
 
147
 
148
  # Stylize with SD prompt. We are selecting these from UI now.
149
  #prompt = "highly detailed, digital portrait, professional lighting, cinematic style, artistic AI avatar"
 
151
  #prompt = "studio portrait, even lighting, neutral background, realistic skin, confident pose"
152
  #prompt = "realistic professional headshot, soft studio lighting, neutral background, crisp details, natural skin tone"
153
 
154
+ # --- Convert input ---
155
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
156
+ img = img.convert("RGB").resize((512, 512), Image.Resampling.LANCZOS)
157
+
158
+ # --- Step 1: Load InstantID + SDXL pipeline ---
159
+ pipe = StableDiffusionXLPipeline.from_pretrained(
160
+ "stabilityai/stable-diffusion-xl-base-1.0",
161
+ torch_dtype=torch.float16
162
+ ).to(device)
163
+
164
+ instantid = InstantID.from_pretrained("InstantID/InstantID")
165
+ pipe.load_ip_adapter(instantid)
166
+
167
+ # --- Step 2: Optimize for ZeroGPU memory ---
168
+ pipe.enable_attention_slicing()
169
+ pipe.enable_model_cpu_offload()
170
+
171
+ # --- Step 3: Prepare conditioning (face embedding) ---
172
+ np_img = np.array(img)
173
+ bgr_img = cv2.cvtColor(np_img, cv2.COLOR_RGB2BGR)
174
+ face_emb = instantid.extract_face_embedding(bgr_img) # key step: ID embedding guidance
175
+
176
+ # --- Step 4: Stylized generation ---
177
+ gen = pipe.generate_with_identity(
178
+ image=img,
179
+ face_embedding=face_emb,
180
+ prompt=(
181
+ prompt
182
+ + ", portrait of the same person, consistent identity, detailed lighting, "
183
+ "highly realistic skin texture, cinematic color tones"
184
+ ),
185
+ strength=float(strength),
186
+ guidance_scale=float(guidance_scale),
187
+ num_inference_steps=30
188
+ )
189
+
190
+ avatar = gen.images[0]
191
+
192
+ # --- Step 5 (Optional): Post-process with GFPGAN for crispness ---
193
+ try:
194
+ from gfpgan import GFPGANer
195
+ from realesrgan import RealESRGANer
196
+ from basicsr.archs.rrdbnet_arch import RRDBNet
197
+
198
+ model = RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64,
199
+ num_block=23, num_grow_ch=32, scale=2)
200
+ upsampler = RealESRGANer(
201
+ scale=2,
202
+ model_path='https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.1/RealESRGAN_x2plus.pth',
203
+ model=model,
204
+ tile=400,
205
+ tile_pad=10,
206
+ pre_pad=0,
207
+ half=True,
208
+ device=device
209
+ )
210
+ face_enhancer = GFPGANer(
211
+ model_path='https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.3.pth',
212
+ upscale=1,
213
+ arch='clean',
214
+ channel_multiplier=2,
215
+ bg_upsampler=upsampler,
216
+ device=device
217
+ )
218
+
219
+ img_cv = cv2.cvtColor(np.array(avatar), cv2.COLOR_RGB2BGR)
220
+ _, _, restored_img = face_enhancer.enhance(
221
+ img_cv, has_aligned=False, only_center_face=False,
222
+ paste_back=True, weight=0.4
223
+ )
224
+ avatar = Image.fromarray(cv2.cvtColor(restored_img, cv2.COLOR_BGR2RGB))
225
+ except Exception as e:
226
+ print(f"[WARN] GFPGAN post-process skipped: {e}")
227
 
 
 
228
  return avatar
229
 
230
+
231
  @spaces.GPU
232
  def process_all(img: Image.Image):
233
  """Process all three types at once"""
requirements.txt CHANGED
@@ -30,6 +30,9 @@ diffusers>=0.20.0
30
  transformers==4.39.0
31
  accelerate==0.28.0
32
 
 
 
 
33
  # Core Dependencies
34
  scipy==1.12.0
35
  tqdm==4.66.1
 
30
  transformers==4.39.0
31
  accelerate==0.28.0
32
 
33
+ # InstantID
34
+ instantid
35
+
36
  # Core Dependencies
37
  scipy==1.12.0
38
  tqdm==4.66.1